awesome-everything RU
↑ Back to the climb

Engineering Practice

Short-lived branches vs gitflow

Crux Trunk-based means short-lived branches — three or fewer, gone within a day, no integration phase — not no branches. Gitflow suits scheduled shrink-wrapped releases but institutionalizes drift for continuous deploy. Big refactors stay on trunk via branch by abstraction.
Your altitude — climbing toward senior
ZeroJuniorMiddleSenior
You are at middle altitude — in the sky
◷ 16 min

A team reads “trunk-based development” and hears “commit straight to main, no branches, no PRs.” They turn off branch protection. Within a week the trunk is red twice a day, an unreviewed change leaks a credential, and they conclude trunk-based “doesn’t scale.” They threw out the wrong thing. Trunk-based was never about deleting branches — it’s about keeping them so short-lived that they never accumulate drift. The branch lives for an hour, behind a green gate and a review, then it’s gone.

What “short-lived” actually specifies

DORA’s definition of trunk-based is concrete, not vibes. Keep three or fewer active branches in the repository at any time. Branches should last no more than a day before merging into trunk — often only a few hours. There are no code freezes and no integration phases. Each branch is one small, scoped change that opens a PR, passes a fast CI gate, gets a quick review, and merges the same day. The branch is a staging area for review and gating, not a place where work lives.

This is sometimes run as pure “commit to trunk” (no branches at all, common in small co-located teams with pairing) and more often as “short-lived feature branches” (the version most teams use, because it keeps code review and the PR gate). Both are trunk-based. The dividing line isn’t “branch or no branch” — it’s lifetime. A branch that lives hours is trunk-based; a feature/* branch that lives two weeks is the long-lived branch the practice exists to eliminate, no matter what you call your workflow.

Why gitflow optimizes for the opposite world

Gitflow gives you permanent main and develop branches, plus feature/*, release/*, and hotfix/* branches with formal phases: features integrate into develop, a release branch stabilizes, then merges to main and tags. It’s a coherent model — for the world it was designed for: shrink-wrapped, versioned software with scheduled releases, multiple versions supported in the field, and no continuous deployment. In that world, an explicit stabilization phase and parallel release lines earn their keep.

For a continuously deployed web service, gitflow institutionalizes exactly the drift the previous lesson warned about. Long-lived feature/* branches accumulate divergence; the develop-to-release-to-main promotion is a built-in integration phase; “done” features sit in develop rotting while they wait for the next release train. You inherit big-bang merges and code freezes by design. DORA’s research explicitly flags integration phases and code freezes as anti-patterns for delivery performance — gitflow makes them load-bearing.

DimensionGitflowGitHub flowTrunk-based
Long-lived branchesmain + develop (+ release)main onlytrunk only
Feature branch lifeDays to weeksUntil PR merges (variable)Hours, < 1 day
Integration phaseExplicit (release branch)NoneNone
Built forVersioned, scheduled releasesWeb apps, continuous deployContinuous deploy at scale
ReleasePromote develop → release → mainDeploy merged mainTag/branch from trunk at release point

Releasing without long-lived branches

The objection: “if there’s only trunk, where do releases come from, and how do I patch an old version?” Trunk-based has a specific answer — branch for release, not for feature. When you cut a release, you create a short-lived release/x.y branch from trunk at that commit. Day-to-day development never happens on it. If a critical fix is needed, you fix it on trunk first (so the next release has it too) and cherry-pick the single commit onto the release branch. The release branch is a read-mostly snapshot, not a parallel development line, so it never accumulates drift. Teams doing continuous deployment often skip even this and just tag trunk.

For big changes that genuinely can’t ship in a day — replacing an ORM, swapping a payment provider — the trunk-based tool is branch by abstraction, not a long-lived branch. You introduce an abstraction layer over the thing you’re replacing, build the new implementation behind it incrementally on trunk (each step merged daily, dark until ready), switch the abstraction to the new implementation, then remove the old one and the abstraction. The large refactor lives on trunk the entire time as a sequence of small green commits — never as a three-week branch.

Why this works

The naming trap is real: a feature/* branch in a gitflow repo and a short-lived branch in a trunk-based repo can look identical in git. The difference is invisible in the branch and visible only in its lifetime and the discipline around it. “We use feature branches” tells you nothing about whether you’re trunk-based — “our branches are merged or deleted within a day” tells you everything.

Pick the best fit

You're replacing the ORM across a continuously deployed service. The migration will take six weeks. How do you do it trunk-based?

Quiz

What is the actual dividing line between trunk-based and a long-lived-branch workflow?

Quiz

In trunk-based development, how is a critical fix to an already-released version handled?

Order the steps

Order a branch-by-abstraction migration on trunk:

  1. 1 Introduce an abstraction layer over the component you're replacing
  2. 2 Build the new implementation behind the abstraction in small daily-merged commits, dark
  3. 3 Switch the abstraction to point at the new implementation
  4. 4 Verify in production, then delete the old implementation
  5. 5 Remove the now-redundant abstraction layer
Recall before you leave
  1. 01
    A team turned off all branch protection because they read that trunk-based means 'no branches.' What did they misunderstand, and what does trunk-based actually specify?
  2. 02
    If there's only trunk, how do you cut releases and do a six-week refactor without a long-lived branch?
Recap

Trunk-based development forbids long-lived branches, not branches themselves — DORA specifies three or fewer active branches, a lifetime under a day, and no integration phases or code freezes. Whether you commit directly to trunk or use short-lived feature branches with PRs, the distinguishing variable is lifetime, not the presence of a branch. Gitflow’s main/develop/release/hotfix structure is coherent for shrink-wrapped, scheduled, multi-version software, but for a continuously deployed service it institutionalizes the very drift, big-bang merges, and integration phases that trunk-based exists to remove. Releases come from short-lived branches cut from trunk, with fixes made on trunk first and cherry-picked down so the release branch never becomes a parallel development line. And changes too big to ship in a day use branch by abstraction — an abstraction layer, the new implementation built behind it in small daily commits, a flip, then cleanup — so even a six-week refactor lives on trunk as small green steps instead of a long-lived branch.

Connected lessons
Continue the climb ↑Feature flags decouple deploy from release
shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources4
expand
  1. 01
  2. 02
  3. 03
  4. 04

Trademarks belong to their respective owners. Editorial reference only.