Trunk-Based, For Real
Last reviewed · content updated
IntermediateWhat you'll learn
~18 min- Run trunk-based delivery with short-lived branches and an always-releasable main
- Configure branch policies that gate for real - and audit docs against repo reality
- Place merge queues and stacked PRs on the maturity ladder without cargo-culting either
The debate that ended
Start with the unusual thing: this argument is over, and the losing side’s author says so. GitFlow’s creator amended his famous post years ago — use it only for explicitly versioned, multi-version software; Atlassian’s own docs file GitFlow under legacy; Microsoft’s current guidance teaches feature-branches-off-main with an always-releasable trunk; and Lesson 1.1’s research names small batches an AI-returns amplifier. The consensus:
main - protected, always releasable; the only long-lived branchfeature/* - hours-to-days lived, one change each, PR back to mainrelease/* - CUT AT RELEASE TIME when you need them (Release Flow): fixes cherry-pick FROM main TO the release branch, never developed on itwhat does NOT exist: a develop branch, environment branches (dev/uat/prod), merge trains of long-lived branches "integrating"That release-branch variant — Release Flow — matters for this audience because it’s the trunk-based shape that fits shops with versioned or scheduled releases (and it’s what Azure DevOps’ own teams practice): trunk stays the single source of truth, releases are snapshots taken from it, and the flow of fixes is one-directional.
The case study: your wiki lies to you
Here’s a true story shape, anonymized from a real 2026 estate — and the best argument for auditing rather than believing. The estate’s documentation, in several places, prescribed GitFlow: “branch from dev, PRs target dev.” The estate’s git history told the truth: one main, ~160 merged PRs of short-lived feature/* branches, squash merges, and no dev/uat/prod branch having ever existed. The team had quietly evolved to trunk-based years earlier; the docs never caught up, and every new hire’s first day included a confusing hour of reconciling paper with reality.
The lesson is a habit, and it’s this training’s version of Zero Trust’s currency-checking: docs describe intent; the repo describes practice; audit the gap. Your artifact does both halves:
1. AUDIT: for each of our active repos, report from git history - long-lived branches that exist (and their last merge in either direction), median feature-branch lifetime, merge style, PRs targeting anything other than main. Then diff that reality against what our contributing docs claim, and list every doc sentence that no longer matches practice.2. GATE: generate the branch-policy set for main: minimum 1 reviewer with reset-votes-on-new-push, linked work items required, build-validation check that plans/builds the changed components, no direct pushes, and the policy applied via the platform's policy API so it's reviewable config, not console clicks.The policy half is the enforcement that makes trunk-based real rather than aspirational: a protected main whose gates run on every PR — reviewer with vote-reset (a re-push after approval re-requires eyes), work-item linkage (traceability for free), and a build check that actually exercises the change. These four are the ground-truth-verified table stakes; everything fancier builds on them.
The maturity ladder above table stakes
RUNG 1 protected main + PR + CI gate <- table stakes, this lessonRUNG 2 policy-as-config (rulesets/policy API), <- also this lesson org-level enforcementRUNG 3 MERGE QUEUE - serializes merges through speculative CI so a green PR can't land on a main that moved beneath it. Worth it above roughly ten merges/day per repo; its failure mode is flaky tests (Lesson 4.2's whole subject)RUNG 4 true commit-to-trunk behind flags; stacked PRs for dependent change seriesThe upper rungs are currently GitHub-native territory, and that asymmetry is itself worth knowing: GitHub has a built-in merge queue (Enterprise/public repos) and shipped native stacked pull requests in public preview in July 2026; branch protection is being superseded by org-level rulesets, and a separate org-wide Actions policy can require SHA-pinned actions (Lesson 3.3). Azure DevOps has no native merge queue as of August 2026 — the closest native approximation is build validation with expiration set to immediately when the target branch updates (it closes the stale-green race, not the throughput problem), and third-party tools fill the rest of the gap. If your ADO repo genuinely needs rung 3, that’s a real input to platform selection, not a footnote. Statuses here are August-2026; re-verify before relying.
One discipline for the whole ladder: resist quoting adoption statistics for any of it — no credible survey measures merge-queue or stacked-PR adoption, and numbers you’ll see floating around are vendor content. The honest claims are the mechanisms and where they pay.
A Meridian team lead reads the trunk-based guidance and objects: 'We ship a versioned product to a regulator quarterly. We NEED release branches — so we're keeping GitFlow.' What's the accurate response?
Key takeaway
One long-lived branch, short-lived features, squash merges, release branches cut-and-cherry-picked when versioning demands them — enforced by four table-stakes policies applied as config, with merge queues and stacked PRs as earned upper rungs rather than defaults. And audit your docs against your git history; one of them is lying, and it’s usually the docs. Next: deciding what to build before building it.