← The archive Dispatch

Article · Field notes

Trunk and Trains

22 Sept 2026 8 min read

Trunk and Trains

Friday afternoon, someone does the stale-PR sweep. There’s one sitting there from three weeks ago, the same diff as a fix that’s already in production, rotted into a conflict with a feature branch nobody remembers opening. They close it. Nothing goes red. Nobody gets paged.

The bug is now back on the next train.

That’s git-flow working exactly as designed. Two weeks before the train leaves, something breaks in release/2026.09. You open a PR into the release branch, because that’s what ships. Then you open a second PR with the same diff into develop, because that’s where everyone actually works. The first one is reviewed, merged and on staging before lunch. The second one is the one that died on Friday.

Nobody broke a rule. The rule was the bug.

It was right in 2010. It shipped on a disc.

Subversion turned up in 2000 and made a branch a cheap copy. Merging stayed expensive and stayed stupid. Until version 1.5 in 2008, SVN kept no record of what had already been merged, so you tracked it by hand in commit messages like an accountant with a hangover and hoped you never applied the same change twice. Teams branched rarely, merged with dread, and did the work on trunk.

Git landed in 2005 and made branches free. GitHub landed in 2008 and made the pull request the unit of work. A branch per feature now cost nothing, and every branch needed a ceremony to get home.

Vincent Driessen’s A successful Git branching model named the ceremony in January 2010. master for production, develop for what’s coming, feature branches, release branches, hotfix branches, and every release and every hotfix merging twice. The diagram has five lanes and the colour scheme of a Tokyo metro map, and for about eight years printing it out was how you announced that you had a process.

It was correct for the software Driessen was writing, which shipped in numbered versions to customers who might sit on 2.3 until 2015. Ten years later he stuck a note on the top of his own post saying web apps aren’t that class of software, they roll forward and not back, and those teams should go and use GitHub flow instead.

Panaceas don’t exist. Consider your own context.

Vincent Driessen, 2020 note on the git-flow post

The reference implementation was archived in October 2025. Atlassian files its own git-flow tutorial under legacy. A large slice of every company founded before 2018 is still running it.

The second merge is the whole bug

A twin PR isn’t one change in two places. It’s two originals. The same fix, reviewed twice, by different people, against different bases, sometimes weeks apart, and there’s nothing in git that says they’re related. They can produce different trees, and often do. CI tested each of them, and neither run tells you anything at all about the combination.

Only one of the two merges wakes anybody up at 3am. Guess which one gets forgotten. Not malice, just people doing the thing the process rewards.

Then it compounds. The release branch that was cut for a two-week stabilisation window stays open for the whole cycle, because QA signs off there and only there, so that’s where fixes go first, and someone forward-ports them later. Or doesn’t. Two months in, you have two products wearing the same name.

GitLab has had a request open since 2020 to automate the double merge, complete with a workaround that involves a third branch and switching your protections off. When a workflow needs a bot to remember its own steps, the workflow is shit.

Nobody who ships versioned software at volume does this. The Linux stable trees won’t take a fix unless it already exists in mainline. Chromium is main-first, with a release manager reviewing every request to merge into a release branch. Kubernetes fixes on master and opens one cherry-pick PR per supported branch. Same rule three times. One canonical change on the trunk, and the backport is a mechanical copy with provenance rather than a second piece of work with its own opinions.

What to run instead

main      ●──●──●──●──●──●──●──●──●──●──▶  always green
             │                    │
             └ release/2026.09 ●──●──▶ v2026.09.0 (prod)
               cherry-picks only  └ release/2026.11 ●──▶
feature/x ●──●──┐  two days, rebased on main, merge queue
agent/y   ●──┘

One long-lived branch, held to release standard rather than to “mostly works.” Every merge redeploys it somewhere product and sales can open without asking. The real test isn’t what CI says. It’s whether you’d let a salesperson click around it during a customer call without hovering behind them.

Feature branches come off it and go back within days, and the merge queue does the merging, not you. Anything longer than a train goes behind a flag or gets split into a stack of small PRs.

Release branches get cut on a fixed date, two weeks out, and from that moment accept cherry-picks of commits already on main and nothing else. A bot opens them with -x so the commit records where it came from, and the release manager approves against one question. Does this need to ride this train, or can it wait eight weeks? Hotfixes use the same path, because there isn’t another one. Production is on fire, you fix it on main, and that’s the only PR anyone reviews.

The interval is a business decision. Chrome ran six weeks for a decade and moved to two this month. The stabilisation window isn’t negotiable the same way. If it takes you six weeks to get a release branch shippable, that’s main telling you it was never release-ready when you cut it.

What’s coming

By April 2026 Sundar Pichai was saying three quarters of Google’s new code is AI-generated. Anthropic’s own retrospective puts Claude’s share of merged code at 80%. Across GitHub, merged PRs went from about 25 million a month in early 2023 to about 90 million a month by mid-2026.

The pull request was invented so one human could explain a change to another human. LinearB’s benchmark of 8 million PRs found AI-authored ones wait over sixteen hours for a first review and merge at 33% instead of 85%. A study of 33,000 agent PRs found 61% never got a human review at all. Run several agents on the same repo at once and 42% of PR pairs conflict.

None of that is an argument for more branches. Everybody’s first instinct under load is to add one, because a branch feels like control. It isn’t. It’s a place to hide work until it becomes somebody else’s problem. Add machinery instead. A merge queue that batches and bisects, affected-target testing so CI doesn’t turn into a number somebody in finance forwards to your CEO, CODEOWNERS with real names on a rotation, and a size cap that agents will find annoying.

The harder problem is that nobody knows why the code exists any more. For twenty years the answer was the commit message, or a person you could walk over to. When 80% of it comes out of an agent in a session that’s closed and gone, that person doesn’t know either. The diff was never the knowledge, and now nobody has it.

That’s the hole the new forges are digging into. Entire, from Thomas Dohmke who ran GitHub until 2025, stores every agent session, the prompt, the transcript, the tool calls, as refs inside the repository, next to the commit. Cursor’s Origin is a full forge with stacked PRs and an agent-aware merge queue. GitLab’s Orbit builds a context graph so an agent can ask the platform instead of re-reading your repo from scratch every morning. GitButler just raised a Series A on Scott Chacon’s line that generating change was never the hard problem, and that organising, reviewing and integrating it is.

Don’t switch forges this year. Do start writing agent sessions into the repo, because the format is a git ref and a git ref travels. I made the same argument in May and I’d add that capturing this now is cheaper than reconstructing it during an incident in 2028. And read every one of these launches as a report on what your own process can’t answer. If the market is selling “why did this change happen” as a feature, it’s because your repo can’t tell you.

The last holdout is always the same guy, and he’ll tell you the double merge has never actually bitten him. That’s bollocks, and it takes ten minutes to prove. Run git log --oneline release/current..develop and then the reverse, count the commits that exist on one side only, and put the list up on the screen. It bit him in March. He just wasn’t the one who had to fix it.


Want to chat? X / LinkedIn