Git Good
Git Good
Git Good
The Pipeline: How Git Learned to Ship Itself
S2 E3119m · Apr 05, 2026
In the late 1990s, integration day meant merging weeks of separate work and watching builds fail—until one practice changed everything about how software ships.

The Pipeline: How Git Learned to Ship Itself

The Invisible Machine

This is episode thirty-one of Git Good. Every time you push code to a repository, something happens that you probably do not think about. A machine somewhere wakes up. It clones your repository. It installs your dependencies. It runs your tests. It scans your code for vulnerabilities. It checks whether your formatting matches the rules somebody wrote three years ago and nobody has questioned since. And at the end, a tiny icon appears next to your commit. A green check or a red X. The whole thing takes minutes, sometimes seconds. Most developers glance at the icon, react accordingly, and move on with their day.

But behind that icon is an entire world. A world of build servers and pipeline configurations and secret credentials and fragile scripts that nobody dares to touch. A world where Git is not just keeping track of your code. It is the trigger for everything that happens after you write it. The version control system that was built to remember became the starting pistol for a machine that ships.

This is the story of how that machine got built, who built it, and what happens when someone compromises it.

The Pain That Started It

The idea that became continuous integration started with pain. In the late nineteen nineties, software teams were hitting a wall that had nothing to do with writing code. Writing code was the easy part. The hard part was putting everyone's code together.

Teams would work for weeks, sometimes months, on separate features. Each developer had their own copy of the project, their own local changes, their own assumptions about how things fit together. Then came integration day. The team would attempt to merge everything into one codebase, build it, and see if it worked. It rarely did. Conflicts everywhere. Features that worked in isolation broke when combined. Functions that one developer renamed were still being called by the old name in another developer's code. Integration day was universally dreaded. Some teams called it merge hell.

Kent Beck, one of the creators of extreme programming, had a simple insight. If integration is painful, do not do it less. Do it more. Integrate constantly. Every day. Multiple times a day. Make the pain so small and so frequent that it stops being pain at all. He called the practice continuous integration. Martin Fowler wrote the article that codified it, first in two thousand one and then in a definitive update in two thousand six. Fowler laid out the discipline. Everyone commits to a shared mainline at least once a day. Every commit triggers an automated build. Every build runs the tests. If the build breaks, fixing it becomes the team's top priority, right now, before anyone commits anything else.

The theory was elegant. The practice required a machine. Someone had to actually run those builds. Someone had to watch the repository for new commits and trigger the tests. In the early days, teams wrote their own scripts. Cron jobs polling version control every few minutes, shell scripts that checked out the latest code and ran make, email alerts when something failed. It worked, barely, and it was fragile, completely. Every team reinvented the same wheel, badly.

The Ugly Workhorse

In the summer of two thousand four, a software engineer named Kohsuke Kawaguchi was working at Sun Microsystems. Kawaguchi was building and testing Java code, and he was tired of breaking the build and finding out from an annoyed colleague instead of from a machine. Years later, he described what drove him.

I was a guy who breaks the build and I wanted to have software that catches my mistake before others do. Little did I know that the itch I was scratching was shared by so many other people.

So he built a tool. It watched a repository, triggered builds automatically when code changed, and displayed the results in a web dashboard. He called it Hudson.

Hudson was not beautiful. The interface was functional in the way that a hospital waiting room is functional. But it worked. It was written in Java, so it ran everywhere. It was open source, so anyone could use it. And crucially, it was extensible. Kawaguchi designed Hudson with a plugin architecture, and the community responded. Plugins for different version control systems. Plugins for different build tools. Plugins for notifications, for code coverage, for deployment, for things Kawaguchi never anticipated. By two thousand eight, Hudson was the most popular continuous integration server in the world. Not because it was elegant. Because it was there, it was free, and it solved the problem. And then Oracle bought Sun Microsystems.

The acquisition closed in two thousand ten, and the trouble started almost immediately. Oracle claimed ownership of the Hudson name. In December of that year, they filed a trademark registration. The community, which had built Hudson into what it was through years of volunteer work and plugin development, objected. The argument was not about code. It was about control. Who owns a project that was created at a company but built by a community?

On January eleventh, two thousand eleven, the community called a vote. The proposal was simple. Rename the project. Walk away from the name Oracle claimed and start fresh. On January twenty-ninth, the vote passed overwhelmingly. Hudson became Jenkins.

Oracle, on February first, announced they would continue developing Hudson and that Jenkins was the fork. The Jenkins community said the opposite. Both sides claimed the other had left. It was the open source version of a divorce where both parties insist the other one moved out.

The community chose Jenkins. The developers went with Jenkins. The plugins went with Jenkins. Oracle eventually donated Hudson to the Eclipse Foundation, where it lingered for a few years before being declared obsolete in two thousand seventeen. Jenkins, meanwhile, became the backbone of continuous integration for a generation. Ugly, sprawling, infinitely configurable, and running in a closet server at roughly half the companies in the world.

The Beautiful Experiment

While Jenkins was spreading through enterprise closets, a group of developers in Berlin had a different idea. In two thousand eleven, Sven Fuchs, Konstantin Haase, Josh Kalderimis, and a few others launched Travis CI. Their premise was radical for the time. Continuous integration should be free for open source projects. Not free as in you can install our software on your own server. Free as in push your code to GitHub and we will run your tests for you, on our infrastructure, at our expense.

The setup was almost absurdly simple. You added a small configuration file to your repository. In that file, you specified your programming language, your test command, and maybe the versions you wanted to test against. You pushed. Travis picked it up, spun up a clean virtual machine, installed your dependencies, ran your tests, and reported the result back to GitHub as a colored badge on your pull request. Green for passing. Red for failing.

That badge changed the culture of open source. Before Travis, most open source projects did not have continuous integration. Running tests was something the maintainer did locally before releasing a new version, if they did it at all. Travis made testing visible. It made testing social. A pull request with a green badge was trustworthy. A pull request with a red badge, or no badge at all, was suspicious. The badge became a signal, not just about whether the code worked, but about whether the project was maintained, whether the contributor cared.

Travis CI grew to over seven hundred thousand users. More than two hundred thousand projects. More than fifty million builds. It became the default CI service for the open source world. If you maintained a popular library between two thousand twelve and two thousand eighteen, you almost certainly had a Travis configuration file in your repository.

Then, in January of two thousand nineteen, Travis CI was acquired by Idera, a company that sold database management tools, owned by the private equity firm TA Associates.

Within weeks, layoffs began. Engineers who had built the platform woke up to termination notices. In March, the infrastructure suffered a multi-day outage. Over the following months, the free tier for open source projects was gradually restricted. Build times slowed. Reliability dropped. The service that had defined open source CI for the better part of a decade was being hollowed out.

Daniel Stenberg, the creator of curl and a maintainer who had used Travis CI for years, wrote a blog post in two thousand twenty-one titled "Bye bye Travis CI."

We mass-migrated everything away from Travis CI. Build times went from forty to fifty minutes back to under five. The Travis service just kept getting worse. More and more builds timed out or hit strange errors. We should have left sooner.

Project after project migrated away. The green badges disappeared from repositories and were replaced by new ones, often from the service that had been Travis CI's death sentence.

The Platform Swallows the Pipeline

On October sixteenth, two thousand eighteen, GitHub announced GitHub Actions at their annual Universe conference. The initial pitch was workflow automation. Not just CI. You could trigger any automated task when something happened in your repository. A push, a pull request, a new issue, a comment, a release. The building blocks were called actions, reusable units of automation that anyone could publish and share.

But the CI use case was the obvious one. In August of two thousand nineteen, GitHub made it explicit. GitHub Actions now supports CI and CD, free for public repositories. In November, it went to general availability. Forty million developers already had GitHub accounts. Now they had a CI system built into the platform, with no external service to configure, no webhook to set up, no separate account to create.

The strategic logic was devastating. Travis CI existed because GitHub did not do CI. CircleCI existed because GitHub did not do CI. A dozen CI startups existed because the platform where code lived and the service that tested code were separate things. GitHub Actions collapsed that separation. It put CI inside the platform. And it gave it away for free to anyone working on open source.

Within eighteen months, GitHub Actions was the dominant CI service for open source projects. The startups that had built businesses in the gap between repository and pipeline found that gap closing. Some survived by focusing on enterprise customers with complex needs. Travis CI, already struggling under new ownership, effectively collapsed. The green badge that had meant Travis now meant GitHub, and the infrastructure that had been spread across independent companies was consolidating into a single platform owned by Microsoft.

The Shadow in the Build

All of this, the automation, the pipelines, the machines that wake up when you push, created something that almost nobody thought about until it was too late. A new attack surface. And in two thousand twenty, someone demonstrated exactly how dangerous it was.

SolarWinds is a company that makes network monitoring software. Their product, Orion, was used by roughly three hundred thousand organizations worldwide, including most of the United States federal government. The Treasury Department. The Department of Homeland Security. The State Department. The Commerce Department. The FBI. The National Nuclear Security Administration. Orion watched their networks, tracked their performance, flagged their outages.

In September of two thousand nineteen, attackers later identified as Russian intelligence gained access to SolarWinds' internal network. They did not touch the source code. That is the detail that matters most. They did not modify a single line in the repository. Instead, they planted a tool in the build environment, the machines that compiled the source code into the software that customers actually installed. The tool waited. When the build process ran, it intercepted the compilation and injected malicious code into the resulting binary. The backdoor was baked into the finished product, but it existed nowhere in the source.

The compromised software was then digitally signed by SolarWinds' own certificates and distributed as a routine update. If you reviewed the Git repository, everything looked clean. If you audited the source code, you would find nothing wrong. The malware existed only in the space between the source code and the compiled output. In the pipeline itself.

Between March and June of two thousand twenty, roughly eighteen thousand organizations installed the poisoned update. The attackers had access to the internal networks of federal agencies, defense contractors, and Fortune five hundred companies. The breach was not discovered until December of two thousand twenty, when the cybersecurity firm FireEye noticed something wrong with their own network and traced it back to the Orion update.

The attackers understood that modern software does not ship from a developer's laptop. It ships from a pipeline. And the pipeline was the one thing nobody was watching.

The lesson of SolarWinds was not that source code can be compromised. People already knew that. The lesson was that the pipeline, the build server, the CI system, the machine that turns your repository into something a customer runs, is just as much a target as the code itself. Maybe more, because everyone reviews code. Almost nobody audits the build.

The Configuration That Ate the Repository

There is one more twist in the story of Git and the pipeline, and it starts with a file format that was never designed for what we now use it for.

YAML was created in two thousand one as a human-readable data serialization language. It was meant for configuration files. Simple ones. A few key-value pairs. Maybe a list. The kind of thing you might put in a settings file, but slightly more structured and slightly more readable.

Then CI and CD adopted it. And YAML became the language in which developers describe the most complex automated processes in their entire codebase. A GitHub Actions workflow file can define multiple jobs running on different operating systems, with conditional steps, matrix builds testing against six versions of a language, secrets injected from encrypted vaults, artifact uploads, deployment gates, notification webhooks, caching strategies, and failure recovery steps. All in YAML. All committed to Git. All indentation-sensitive, so a single misplaced space can break the entire pipeline.

The irony is sharp enough to draw blood. The CI pipeline exists to catch errors in your code. It runs your linter, your type checker, your test suite. But the pipeline configuration itself has none of those protections. There is no linter for your pipeline that catches logical errors. There is no test suite for your workflow files. You cannot run your GitHub Actions workflow locally to see if it works before pushing. You push, you wait, it fails, you read a cryptic error message, you fix the indentation, you push again, you wait again. The feedback loop for pipeline code is worse than the feedback loop for application code was in the nineteen nineties.

Developers have a name for this. They call it YAML hell. And it is everywhere. Not just in CI. The infrastructure-as-code movement took Git further than anyone anticipated. Terraform files defining cloud infrastructure. Kubernetes manifests describing how containers should be orchestrated. Ansible playbooks specifying how servers should be configured. All of it versioned in Git, all of it reviewed in pull requests, all of it deployed through pipelines. The entire shape of a company's infrastructure, from the load balancer in front to the database in back, lives in a Git repository somewhere, described in configuration files that nobody fully understands.

The pattern has a name now. GitOps. The idea is that the Git repository is the single source of truth for everything. Not just code but infrastructure, configuration, policies, deployment state. You want to change a server's configuration, you do not log in and edit a file. You open a pull request. You get it reviewed. You merge it. A tool watching the repository notices the change and applies it automatically. The version control system that Linus Torvalds built to track the Linux kernel is now tracking the state of entire data centers.

The Accelerant No One Asked For

And then there is the thing that is happening right now. AI-generated code is landing in pipelines, and the pipelines do not know the difference.

A developer asks an AI assistant to write a GitHub Actions workflow. The AI produces something that looks correct. The developer commits it. The pipeline runs. It works, or it does not, and the developer asks the AI to fix it, and the AI produces a slightly different version, and the developer commits that instead. At no point does the developer necessarily understand what the pipeline is doing, because the configuration was never something they wrote. It was something they requested.

This is the logical endpoint of the journey that started with Kohsuke Kawaguchi automating his builds in a Sun Microsystems office. The pipeline became so important that it outgrew the people who understood it. First it was a script that a senior engineer maintained. Then it was a Jenkins configuration that a DevOps team managed. Then it was a YAML file that a platform team owned. Now it is a prompt that an AI generated.

The SolarWinds attackers knew that the pipeline was trusted and unexamined. AI-generated pipeline configurations take that same dynamic and scale it. Not maliciously. Just by making it easy to create complex automation without understanding it. Every YAML file generated by an AI and committed without review is a small act of faith in a system that the developer did not build and cannot fully audit.

Git made all of this possible. Not because Linus designed it this way. He built a tool to track source code for the Linux kernel. But Git's model, where every change is recorded, where every state is recoverable, where the entire history is a single chain of cryptographic hashes, turned out to be exactly the foundation that automated pipelines needed. A commit hash is not just an identifier for a snapshot of your code. It is a unique label for a point in time, a trigger for a machine, and increasingly an audit trail for everything your organization does with software.

The tool that was built to remember now decides when to ship. And the machine it triggers is the most powerful, least understood, and most blindly trusted piece of infrastructure in modern software development.

That was episode thirty-one of Git Good. In the next episode, we follow Git into the places it was never designed for. The game studios and data science labs and legal departments that discovered, sometimes painfully, that a tool built for source code does not always fit the rest of the world.

Git push is the command that starts the machine. When you run it, your local commits travel to the remote repository, and if a pipeline is configured, that arrival triggers everything. Builds, tests, scans, deployments. The push itself is simple, just a transfer of objects and references. But what happens next can involve dozens of machines, thousands of test cases, and deployment to servers on three continents. The gap between what git push does and what git push causes is the entire story of modern software delivery. Next time a green check appears on your commit, remember that an invisible factory just ran on your behalf.