Your branch doesn't grow while you're away. The trunk does.
Everyone estimates a merge by looking at their own diff, which is the one part of the problem that stopped changing the moment they stopped typing. The cost is set by the other side — by how far the trunk moved, multiplied by how long you were gone.
Commit before you look.
You write 40 lines on a branch and then leave it open for four weeks — a holiday, a reprioritisation, a review that never came. Meanwhile eight teammates keep merging to trunk. Your branch is still 40 lines. How hard is the merge?
The divergence clock.
Two lanes: yours and the trunk. Every day apart, both keep moving, and the gap between them grows as the product of the two — which is why it feels fine for a week and impossible by week four. Drive the days, the size of the team you are diverging from, and how much of the hot code your change touches.
How the model works. Divergence is
trunk commits × your commits × overlap — a product, so it grows with the
square of the days apart, not in a straight line. Textual conflicts scale with divergence;
semantic conflicts are modelled at half that number, because they are the subset that shares no
literal lines. The constants are illustrative. The shape of the curve is the point.
Which of these will Git actually tell you about?
You have been away three weeks. Here is what happened on trunk while you were gone. For each one, decide whether your merge will stop and demand a decision — or whether it will succeed silently, compile, go green, and fail later.
Called: 0 of 5 · Correct: 0
Branch lifetime is not the same lever as batch size.
They are cousins, and teams often conflate them. Small Batches is about how much goes out in one release — blast radius, and how many changes you have to bisect. This is about how long your work stays unintegrated — divergence, and how much reality moved underneath it. You can have a small change that is a catastrophic merge, which is exactly the case the batch-size lens misses.
| Long-lived branch | Short-lived branch | |
|---|---|---|
| Merge cost | Grows with the square of the time away. | Near-constant, and small. |
| Conflicts arrive | All at once, at the worst moment. | One at a time, in the flow of the work. |
| Semantic conflicts | Many, and invisible until runtime. | Caught within a day, while you remember why. |
| Review quality | "LGTM" on 4,000 lines nobody can hold in their head. | Real comments, because the diff is readable. |
| Rework risk | High — the design you built on may have moved. | Low — you are building on today's code. |
| Who absorbs it | Whoever merges last. Usually not the person who caused it. | Everyone, continuously, in tiny amounts. |
"But my feature isn't finished"
This is the real objection, and it deserves a real answer rather than an exhortation to be braver. The answer is to separate merging from releasing: put the unfinished code on trunk behind a switch that is off, and integrate every day anyway. That is what feature flags are for, and it is why trunk-based development and flags are always taught together — neither works properly without the other.
Semantic conflicts are the expensive half
A textual conflict is a good outcome. Git stops, shows you both versions and makes you decide, while you still remember the context. The costly ones share no lines at all: someone renamed the function you call, made the column you read nullable, or changed an endpoint to return a list. Those merge clean and build green. You find them at runtime, on a Thursday, and the person who made the change has long since moved on.
Merging four branches is not four merges
Integration week reads like four tasks. It is n(n−1)/2 reconciliations — six pairwise
interactions for four branches, ten for five — and each one can undo a decision made in another.
That is why "we'll integrate at the end" scales so much worse than anyone plans for, and why the
week is always the one that slips.
Four branches that were only going to be open for a couple of days.
Nobody sets out to keep a branch for three months. Every one of these started as a reasonable plan with a short horizon.
The release branch that became a fork
A "stabilisation branch" is cut for a big release so that trunk can carry on. Six weeks of fixes then land on both sides, in slightly different forms. Half of them get lost in the reconciliation, and one bug gets quietly un-fixed by a merge that resolved in favour of the older version.
The refactor nobody could merge
Three months of "clean architecture" work touching almost every file. By the time it is ready, trunk has moved so far that the merge is a rewrite of the rewrite. The branch is quietly abandoned, and the lesson the team takes away is "refactoring doesn't work here" — which is the wrong lesson entirely.
The review nobody could do
A 4,000-line pull request gets "LGTM" in eight minutes. A 60-line pull request from the same reviewer, the same afternoon, gets three substantive comments and a question about naming. The reviewer did not get lazier between the two.
Trunk-based, with the lights off
Everyone commits to trunk at least daily. Half-finished work goes in behind a flag that is off in production, so "not finished" and "not integrated" stop being the same thing. The frightening integration happens thirty times, in pieces small enough that nobody notices it happening.
A branch is a bet that the world will hold still.
Every branch is a wager that nothing you depend on will move before you come back. Over a day that is a safe bet. Over a month, on a team of eight, it is a bad one — and the losses land on whoever merges last, which is rarely the person who placed it. Changing that is mostly a matter of policy and nerve rather than tooling, which is why it is work we do with teams rather than for them.
Talk to us about coachingDivergence is a product, not a sum
Your change times their change times time apart. That is why a branch feels fine for a week and impossible by week four — the cost curve bends upward while your diff stands still.
The conflicts you see are the cheap ones
Git stops for overlapping lines. It says nothing about a renamed function, a newly nullable column or a changed return shape — and those merge clean, build green, and break later.
Unfinished and unintegrated are different
The only reason to hold work back is that it is not ready for users — and a flag solves that without holding it back from the codebase. Integrate daily; release when ready.