Skip to main content

Trunk-Based Development vs GitFlow

What This Concept Is

Two branching models dominate the literature:

  • Trunk-Based Development (TBD). One long-lived branch (main / trunk). Developers either commit directly or use short-lived feature branches (hours to a few days) that merge back to trunk. Releases are cut from trunk, usually by tagging. Work-in-progress is hidden behind feature flags, not behind branches.
  • GitFlow. Multiple long-lived branches: develop, main, release/*, hotfix/*, and per-feature branches. Features accumulate on develop, release branches stabilize, main always reflects production, hotfixes go in through their own lane.

These are not just workflow preferences. They imply entirely different delivery tempos, test strategies, and rollback paths. Pro Git's Distributed Git Workflows chapter lays out the family of models (centralized, integration-manager, dictator-and-lieutenant) -- TBD and GitFlow are two concrete realizations you will encounter almost everywhere.

Why It Matters Here

Branching model is the upper bound on delivery cadence:

  • TBD can deploy to production on every merge because trunk is always releasable.
  • GitFlow structurally batches changes into release branches. You ship on release-branch cadence, not on merge cadence.

If you want DORA "elite" numbers (concept 3) -- multiple deploys per day, < 1 hour lead time, < 1 hour MTTR -- you almost have to be on TBD. GitFlow with its stabilization phase makes those targets arithmetically impossible. Google, Facebook, and most large-scale web operators run trunk-based by default. Long-lived branches are a tax you pay only when an external constraint (store review, SDK LTS, on-prem install base) forces one.

Concrete Example

TBD flow for a bug fix.

  1. git switch -c fix/timezone-null main (short-lived branch).
  2. Commit, push, open PR. CI runs, peer reviews.
  3. Merge to main same day. Pipeline builds artifact, deploys to staging, then production behind a flag off.
  4. Flip flag on at 1%, then 100%. Total time: same-day.

GitFlow flow for the same fix.

  1. Branch feature/fix-timezone off develop.
  2. Merge to develop after review. Change is now mixed with whatever else is on develop.
  3. Wait for the next release/1.7.0 cut. Regression test on the release branch for several days.
  4. Merge release branch to main, tag v1.7.0, deploy.
  5. Urgent? Open hotfix/timezone off main, then cherry-pick back to develop. This is exactly the cherry-pick-across-branches workflow described in Pro Git -- useful when you have parallel long-lived branches, and an extra surface of bugs ("did we cherry-pick it both ways?") when you do not need them.

Same change, 10x the ceremony and a full extra branch type.

Common Confusion / Misconception

"TBD means no feature branches." False. TBD allows short-lived feature branches -- the constraint is short-lived (hours to two days) and always merged back to trunk. The discipline is not "no branches," it is "no long-lived branches." Pro Git's topic-branch chapter describes the shape: a topic branch exists to isolate one piece of work and is then merged or rebased back.

"GitFlow is the 'default' professional workflow." It was popularized for projects with explicit versioned releases (desktop software, SDKs) where you cannot continuously deploy to users. For web services that can, GitFlow imposes cost for no benefit. The original author (Vincent Driessen) later added a note explaining GitFlow was never meant for software continuously delivered to a single production environment.

"We need GitFlow for hotfixes." TBD handles hotfixes trivially: revert the bad commit on trunk, or land a forward-fix, and deploy. Hotfix branches exist in GitFlow only because main is frozen on the last release and cannot receive casual commits.

"Rebasing a trunk-based branch is safer than merging." Both work. Rebase produces linear history; merge preserves context. The real rule is Pro Git's perils of rebasing: never rebase commits that have already been shared on a remote branch that someone else has based work on. Inside a short-lived topic branch, rebase is fine.

"Long-lived branches keep main stable." They keep main stale. Integration pain compounds quadratically with branch age. Pro Git's long-running branches section covers legitimate uses (stable and unstable lanes for LTS), but for a product with one prod env, long-lived branches mostly delay conflicts until they are worst.

How To Use It

Choose based on deployment target, not preference:

SituationRecommended
Web service, one production environment, can deploy anytimeTrunk-based
Library / SDK with multiple supported versions (1.x, 2.x)Trunk + release branches for long-term support
Mobile app shipping through store reviewTrunk + release branches (store review forces batching)
On-prem product with yearly customer releasesGitFlow-ish (release branches justified)
Monorepo with many services deploying independentlyTrunk-based, per-service tags

If you inherit GitFlow on a web service, the migration path is: stop cutting release branches -> deploy from main -> shorten feature branches -> add feature flags -> collapse develop into main. Expect the collapse to be psychologically harder than technically hard; the team has to believe trunk is safe.

Check Yourself

  1. Why can TBD deploy on every merge while GitFlow usually cannot?
  2. Name one product shape where GitFlow-style release branches are genuinely justified, and one where they are not.
  3. What role do feature flags play in making TBD safe without long-lived feature branches?
  4. Under what rule should you not rebase a branch, and why?

Mini Drill or Application

For a codebase you know, list the branches older than 7 days. For each, answer:

  • What is blocking this from merging?
  • Is the blocker technical (tests, review) or political (waiting for a release window)?
  • Could the in-progress work be hidden behind a flag so the branch could merge today?

A common finding: half the long-lived branches exist only because merging "feels" risky, and the fix is a flag, not more review.

Read This Only If Stuck

See also (external)