Case study
The Branch That Lived for Four Months
A client's "big rewrite" feature branch had 847 commits, was 14,000 lines behind main, and three engineers were trapped on it. The merge took a week. It didn't have to be this way.
The first thing I noticed when I cloned the repo was the branch list. Alongside main and the usual short-lived feature branches, there was one called feature/new-checkout-flow. The last commit was two hours old. The first commit was from June.
It was October.
I asked the tech lead about it. He sighed the kind of sigh that tells you the answer is going to be long.
How a branch becomes a project
The checkout rewrite had started as a two-sprint effort. The product team wanted to replace the existing three-page checkout with a single-page flow. Two engineers scoped it at three weeks. Reasonable enough.
But scope grew. The new flow needed a different state management approach, so they refactored the cart context. The cart context touched the inventory service, so they updated that API contract. The updated contract needed new validation logic, which meant new database constraints. Each change made sense in isolation. Together, they meant the branch was touching 40% of the codebase.
By week six, a third engineer joined the branch. By week ten, the branch had its own standup. Nobody talked about merging anymore. They talked about "the checkout project" as if it were a separate product.
When I ran git log --oneline feature/new-checkout-flow | wc -l, the count was 847. When I ran git merge-base to check how far behind main they'd drifted: 14,000 lines of diff. Not because they'd written 14,000 lines — because main hadn't stood still. Bug fixes, other features, dependency updates, all landing on main while the checkout branch slowly diverged.
The merge
They'd tried merging main into the feature branch twice before I arrived. Both times, the merge produced so many conflicts that they gave up after a day. The conflicts weren't just textual — they were semantic. A utility function that the branch relied on had been renamed on main. An API endpoint the branch called had been versioned. A shared component had been split into two.
I've seen this before. The longer you wait to integrate, the harder integration gets. And the harder it gets, the more you want to postpone it. It's a debt spiral.
We spent a full week on the merge. Not just resolving conflicts — re-testing every interaction between the new checkout flow and code that had changed on main. We found four bugs that only existed because of the divergence. A discount calculation that used the old cart structure. A webhook handler that expected fields the new flow didn't send. Two race conditions where the old and new code had different assumptions about session state.
Warning
Why they didn't merge sooner
This is the part that matters more than the war story. The team wasn't lazy or incompetent. They had reasons:
"It's not ready." The new checkout wasn't feature-complete. Merging it to main would mean having two checkout flows in the codebase, and they didn't have feature flags. In their mind, the only options were "everything on the branch" or "everything on main." The middle ground — merging incrementally behind a flag — hadn't been part of their workflow.
"We'll break things." The branch had grown so large that nobody could confidently say what a partial merge would affect. The integration risk was real, but it was a risk they'd created by waiting. Each week of delay made the next week's merge scarier.
"We're almost done." This one is the most dangerous. I've heard it on every long-lived branch engagement. They were two weeks from done in August. They were two weeks from done in September. They were two weeks from done when I arrived in October.
What we changed
After the merge, we spent two days setting up what should have existed from the start.
First, feature flags. We used a simple config-based system — nothing fancy. A JSON file with boolean toggles, loaded at app startup. The new checkout flow got a flag. Internal users saw it. Everyone else got the old flow. Total setup time: about three hours.
Second, a rule: no branch lives longer than a week. Not a suggestion. A hard rule. If your work isn't mergeable in a week, you need to break it into smaller pieces. If you can't break it into smaller pieces, you need to rethink the approach.
Third, we started doing trunk-based development for real. Short-lived branches, daily merges to main, feature flags for anything not ready for users. The team resisted at first. They were worried about half-finished code on main.
It took about a month before the worry faded. What replaced it was a quiet confidence that came from knowing main was always deployable, always integrated, always telling the truth about the state of the codebase.
The math nobody does
Here's a rough calculation I did for the client. Three engineers spent four months on a branch. That's twelve engineer-months. The merge itself consumed one engineer-week for three people, plus two days of my time. Call it four engineer-weeks just on integration. The four bugs we found during the merge took another week to fix.
So roughly five engineer-weeks — more than a full engineer-month — were spent purely on the cost of having a long-lived branch. Not on building features. Not on fixing bugs. On reconciling two versions of reality that should never have diverged that far.
That's before counting the opportunity cost. While those three engineers were on the branch, they couldn't easily contribute to main. Other teams had to work around them. The codebase had a 40% "do not touch" zone that everyone tiptoed around.
The uncomfortable pattern
I've seen this pattern at four different clients now. A team starts a "big rewrite" on a branch. The branch grows. Integration gets deferred. The branch becomes a parallel universe. Eventually someone calls a consultant (or an incident forces the merge), and the team pays the accumulated tax all at once.
The fix is always the same: smaller batches, feature flags, and a culture that treats integration as a continuous activity rather than a milestone event. It sounds obvious when you write it down. But in the middle of a complex rewrite, when the branch already has three months of work on it, "just merge more often" feels like unhelpful advice.
The best time to set up feature flags is before you start the rewrite. The second best time is now. If you have a branch that's older than two weeks and growing, take an honest look at it. Calculate the integration cost you're accumulating. Then decide whether you're building a feature or maintaining a fork.