An old tree on a hillside under a grey sky, one branch grown so long and heavy that it cracks and tears away from the trunk as it reaches out over the valley.

Why Long-Running Branches Are a Tax on Everyone Elsed3c312f

By

On this page

I started writing software before source control was something every team took for granted. If you wanted to know who changed a file, you often had to ask around, and when two developers modified the same piece of code, putting their changes back together could turn into an adventure. I bring that up because it still surprises me how quickly we forget the problems older solutions were designed to solve, and GitFlow is a good example.

Spend any time reading engineering blogs and you'll run into someone explaining that trunk-based development is superior and GitFlow is outdated. The conclusion may be right, but the explanation usually leaves out the context. GitFlow got popular because it solved problems that many organizations had at the time, and the engineers who adopted it knew what they were doing. If you want to understand why so many teams are moving away from it now, start with why it made sense in the first place.

GitFlow Solved Real Problems

When centralized source control systems became common, they felt revolutionary. Problems that teams used to handle through discipline, communication, and a fair amount of luck finally had tooling behind them.

Then Git arrived and made branching cheap. Creating a branch got so easy that most developers stopped thinking about it. If you needed a branch you made one, and if you needed ten you made ten. Branching went from a decision you might discuss with your team to something that happened almost automatically, and that was a good change.

GitFlow showed up around the same time Git was taking over, as a structured way to organize all that work, and looking back it made a lot of sense. Most organizations were not deploying continuously. Plenty of teams released monthly and some released quarterly. Automated testing existed, but it was nowhere near as thorough or trustworthy as what many teams have today, infrastructure automation was still maturing, and deployment weekends and release freezes were normal. Getting software into production was risky.

GitFlow gave that world some structure. Feature branches let developers work independently, release branches held things stable while final testing happened, and hotfix branches gave teams a way to push urgent fixes into production. For teams whose deployments were expensive and stressful, it was a sensible way to coordinate delivery.

Cheap Branches, Expensive Integration

The catch is that Git made creating a branch cheap without making integration one bit cheaper.

Software engineering has a long history of confusing local optimizations with system optimizations. Creating a branch is a local optimization that makes life easier for the developer doing the work. Merging that branch back is a system problem, and it stayed exactly as hard after the first part got easy.

While many organizations were refining more and more elaborate branching strategies, the industry was also getting much better at automation. Build systems matured, automated tests became more dependable, and cloud infrastructure took a lot of the friction out of deployments. Continuous integration and continuous delivery went from niche practices to mainstream ones. At first glance none of that has much to do with branching strategy, but I think the two are tightly connected.

CI/CD Changed the Economics

CI/CD changed the economics of software delivery. When deployments are difficult, expensive, and risky, optimizing for release management is the sensible call, but once they become routine and mostly automated, the bottleneck moves away from getting software into production and toward integrating changes safely and continuously across every team touching the same code. That sounds like a subtle shift. It changes almost everything.

Most discussions about trunk-based development are about branches, and I think that misses the more important point, which is integration delay. Every day that code stays isolated on a branch is another day the system hasn't learned something it needs to know. The developer may have learned it, the branch may look healthy, and the feature may even seem finished, but the rest of the system still has no idea whether that work fits with everything else going on.

Uncertainty Accumulates Quietly

That uncertainty builds up where nobody is looking. Another team changes a dependency, or a shared library starts behaving differently, or a feature that worked perfectly in isolation does something odd once it's combined with other changes. None of that is unusual. Software systems change constantly, and the longer integration waits, the more room there is for work to drift apart.

I've come to see long-running branches as a tax, because the cost of a branch grows with time and the people paying it are usually not the ones who got the benefit. Branches aren't bad in themselves, and plenty of them never cause any trouble at all.

Who Pays for a Long-Running Branch

The developer who creates the branch gets isolation, and that has value, since it lets them focus without absorbing everyone else's changes as they land.

The system inherits the bill.

Testing gets harder once there are several versions of reality to cover, and somebody has to work out when and how those versions come back together before a release. Integration also happens later, after more assumptions have piled up and more people are involved, so the operational risk climbs with it.

Engineers sometimes talk about merge conflicts as if they were the main cost of a long-running branch. In my experience the conflict is usually a symptom. The bigger problem is finding out that several streams of work evolved on their own and nobody knows how they'll behave once they meet, and that discovery almost always happens close to a release, when uncertainty is at its most expensive.

Trunk-Based Development Is a Consequence, Not a Goal

Trunk-based development starts to make sense here. People will tell you it's fashionable, or that large technology companies use it, or that someone declared it a best practice, and none of those is why it works. It lines up with the same forces that made CI/CD valuable in the first place. Automation is supposed to make delivery repeatable, and a single integration point is far easier to automate than several. It's supposed to reduce uncertainty, which integrating continuously does directly. And if the goal is boring deployments, shortening the distance between writing code and integrating it is one of the surest ways to get there.

A question I ask a lot is this one: if our goal is to automate software delivery, why would we deliberately pick a workflow that creates several competing versions of reality? GitFlow itself hasn't changed much, but the environment around it has. The more mature an organization's automation gets, the more pressure there is to integrate early, and the more often it deploys, the more each integration delay costs. Eventually optimizing for continuous integration matters more than optimizing for long-term isolation, and trunk-based development starts to show up on its own.

A lot of writing presents trunk-based development as the goal. I think it's a consequence. If you care about fast feedback, lower operational risk, and boring production, every one of those pushes you toward integrating more often, and once you follow that far enough, trunk-based development is usually where you end up.

You Choose Where to Pay

Over the years I've become convinced that most engineering decisions should be judged by their effect on the whole system rather than the convenience of any single step. A branch can make one developer's week easier and still leave the system less healthy a month later. The system is what happens after the code ships, and that's where the operational costs, the integration costs, and the uncertainty finally show up.

Long-running branches are dangerous because they let uncertainty pile up where it's hard to see and expensive to resolve. Nothing eliminates risk, but trunk-based development forces the system to face uncertainty earlier, while the problems are still small, the context is still fresh, and mistakes are still cheap to fix. In my experience that tradeoff has kept paying off year after year, which is more than I can say for most of the tradeoffs in this field.

Software delivery is going to be complex no matter what you do, so the choice you actually get is where to pay for it. Continuous integration pays that bill as you go, and most of the time that's cheaper than letting a long-running branch run it up. When I started, integration meant asking around and hoping, and nobody has to work that way anymore.

Frequently asked questions

Why are long-running branches considered a tax?

Because the cost of a branch grows the longer it stays open, and the people paying it usually aren't the ones getting the benefit. The developer gets isolation, while the system inherits harder testing, more complicated release planning, and integration risk that lands later, after more assumptions have piled up.

Was GitFlow a mistake?

No. GitFlow solved problems teams had when they released monthly or quarterly, automated testing was thin, and deployments were risky. Feature, release, and hotfix branches gave teams structure when getting software into production was expensive and stressful. What changed is the environment around it.

Isn't the real cost of a long-running branch the merge conflict?

The merge conflict is usually a symptom. The bigger cost is discovering that several streams of work evolved independently and nobody knows how they behave once combined. That discovery tends to happen close to a release, when uncertainty is most expensive.

How did CI/CD change branching?

CI/CD changed the economics of delivery. When deployments are hard and risky, it makes sense to optimize for release management. When deployments become routine and automated, the bottleneck shifts to integrating changes safely and continuously, which rewards frequent integration over long isolation.

Is trunk-based development the goal?

It's better understood as a consequence. If you care about fast feedback, lower operational risk, and boring production, you end up integrating frequently, and trunk-based development is usually where that leads.

Does trunk-based development eliminate risk?

No, nothing does. It forces the system to confront uncertainty earlier, while the problems are still small, the context is still fresh, and mistakes are still cheap to fix. Delivery stays complex either way, so what you get to choose is where to pay for it.

Conversation

    Log in to join the conversation.

    © 2026 ABWaters. Thinking out loud.