open atlas
↑ Back to track
Git, from zero to senior GIT · 06 · 04

Trunk-based development

Trunk-based development: everyone integrates to one trunk (main) via very short-lived branches, with incomplete features hidden behind feature flags, not long branches. Needs heavy CI and flags; release branches cut from trunk give versioning. How big orgs avoid merge hell.

GIT Middle ◷ 17 min
Level
FoundationsJuniorMiddleSenior

At Google-scale — thousands of engineers in one repository — long-lived branches are not just inconvenient, they are catastrophic: a branch open for two weeks against a codebase that thousands of people are changing becomes an un-mergeable mess. The workflow that solves this is trunk-based development (TBD), and its rule sounds almost reckless: everyone commits to one branch, all the time, and nobody keeps a branch open for long. It is what most FAANG-scale orgs actually run, and it is the logical endpoint of “keep branches short.”

By the end of this lesson you will be able to explain how TBD integrates work continuously without merge hell, why feature flags are not optional but load-bearing, and how versioned releases still happen from a single trunk.

Goal

After this lesson you can describe trunk-based development’s single-trunk model, explain why it relies on feature flags and strong CI, and show how release branches cut from trunk provide versioning without reintroducing long-lived development branches.

1

Everyone integrates into one trunk — main — continuously, many times a day. Continuous integration in its original, literal sense means exactly this: each developer merges their work into the shared trunk at least daily, often several times. The trunk is the one source of truth, and the gap between “code written” and “code on trunk” is measured in hours. The reason is mathematical: divergence between a branch and trunk grows with time, so if every change integrates within hours, divergence — and therefore merge conflicts — stays tiny. TBD attacks merge hell at its root by never letting two lines of work drift apart in the first place.

2

Work happens in very short-lived branches or small direct commits. Branches in TBD live for hours, rarely more than a day. Many teams use a branch only long enough to run CI and get a quick PR review:

git checkout main
git pull
git checkout -b fix-rate-limit-header
# one small, focused change ...
git push -u origin fix-rate-limit-header
# fast review + green CI, merge same day, delete branch

Smaller orgs practising TBD sometimes commit directly to trunk for trivial changes (with CI gating the push). Either way the unit of work is small: a change you can finish, review, and integrate the same day. Big features are not done on big branches — they are broken into small increments that each land on trunk.

3

Incomplete features are merged but hidden behind feature flags. This is the load-bearing idea. If a feature takes three weeks, you do not keep a three-week branch — you merge its half-finished code to trunk continuously, wrapped in a feature flag that is off in production:

if (flags.enabled("new-checkout")) {
  return renderNewCheckout();
}
return renderLegacyCheckout();

The unfinished code is on trunk (integrated, compiled, tested against everyone else’s changes) but invisible to users until the flag is turned on — which can be a gradual rollout to 1%, then 10%, then everyone. Flags are what make “merge constantly, even unfinished work” safe. Without them, TBD is impossible; with them, branch length drops to near zero and incompleteness lives in configuration, not in git topology.

4

Versioned releases come from release branches cut off trunk — and only off trunk. TBD does not forbid versioning; it forbids long-lived development branches. When you need to ship a versioned release, you cut a short release branch from trunk at a chosen commit, stabilise it, tag it, and ship — but development never moves to that branch:

git checkout main
git checkout -b release/2.4
git tag -a v2.4.0 -m "Release 2.4.0"
# critical fixes cherry-picked FROM trunk INTO the release branch

The crucial discipline: fixes are made on trunk first, then cherry-picked into the release branch, never the reverse. The release branch is a read-mostly snapshot for shipping and patching a specific version; all real work stays on the one trunk. This is how TBD supports versioned products without the divergence that sank git-flow’s develop.

Why this works

Why do large orgs choose this demanding model over the friendlier GitHub flow? Scale. With thousands of engineers, the cost of merge conflicts grows super-linearly with branch lifetime and the number of concurrent branches. GitHub flow’s “a day or two” branches are fine at ten engineers and ruinous at ten thousand. TBD pushes branch lifetime toward zero so that integration cost stays flat no matter how many people commit. The price is steep — you must have excellent CI, a real feature-flag system, and the discipline to break every feature into trunk-safe increments — which is exactly why TBD is associated with mature, high-velocity engineering orgs rather than small teams.

Worked example

Building a three-week checkout rewrite without a three-week branch.

You are rewriting checkout, a 15-day job, on a TBD team. Instead of a feature/new-checkout branch open for 15 days, you wrap the new flow in a new-checkout feature flag (off in prod) on day one. Then every day you land small PRs onto trunk: the new cart model Monday, the new payment step Tuesday, the new confirmation page Wednesday — each merged within hours, each behind the flag, each tested against the rest of the codebase as it evolves. By day 15 the whole new flow is on trunk, fully integrated, having never caused a single large merge. You flip the flag on for 1% of users, watch metrics, ramp to 100%, then delete the flag and the old code.

Contrast the git-flow or naïve GitHub flow version: a 15-day branch that diverges from everyone else’s 15 days of work and detonates into conflicts at merge time. TBD never let that branch exist — the same feature shipped as fifteen safe daily integrations.

Common mistake

The seductive mistake is adopting TBD’s branch rule (“commit to trunk constantly”) while skipping its enablers (heavy CI, feature flags, small increments). Commit unfinished, unflagged code straight to trunk without a strong test gate and you do not get Google-grade velocity — you get a permanently broken trunk that blocks everyone. TBD is not “less process,” it is different process: the discipline moves from branch management into CI rigor and flag hygiene. A team without that infrastructure is usually better served by GitHub flow until the CI and flag systems are real.

Check yourself
Quiz

In trunk-based development, how is a large, multi-week feature integrated without keeping a long-lived branch?

Recap

Trunk-based development has everyone integrate into one trunk continuously — many times a day — via very short-lived branches or small direct commits, keeping divergence and therefore merge conflicts tiny. Incomplete features are merged but hidden behind feature flags, so branch length drops toward zero and incompleteness lives in configuration, not git topology. It demands heavy continuous integration and real flag infrastructure, which is why high-velocity, large-scale orgs run it. Versioned releases still happen — via short release branches cut from trunk, with fixes made on trunk and cherry-picked in. Next you will pull all three workflows together into a decision guide for choosing the right one.

Practice

Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.

recallapplystretch0 of 4 done

Something unclear?

Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.

shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.