Trunk-Based vs Git Flow vs GitHub Flow
· Jerwin Arnado ·
A branching model is the set of rules your team agrees on for how a change travels from a developer’s machine to production. Pick the wrong one and you either drown in long-lived branches and painful merges, or you ship chaos with no release discipline. Three models dominate the conversation — Git Flow, GitHub Flow, and trunk-based development — and they sit on a spectrum from heavyweight to minimal. Here’s each, honestly, with the team it actually fits.
Git Flow: the heavyweight
Proposed by Vincent Driessen in 2010, Git Flow uses multiple long-lived branches with strict roles:
main ──────●────────────────●────── (production, tagged releases only)
develop ──●──●──●──●──●──●──●──●────── (integration branch)
feature/* └──┘ └──┘ (branch off develop, merge back)
release/* └────┘ (stabilize, then merge to main + develop)
hotfix/* └──┘ (branch off main, patch, merge both)
mainholds production; every commit is a tagged release.developis the integration branch where features land.feature/*branches offdevelopand merges back.release/*branches stabilize a version before it hitsmain.hotfix/*patches production directly offmain.
Fits: software with explicit versioned releases — desktop apps, mobile apps, libraries, anything where multiple versions exist in the wild and you ship on a schedule rather than continuously.
The cost: it’s a lot of process. The develop-vs-main split and the
release/hotfix dance are overhead that only pays off when you genuinely have versioned
releases to manage. For a web app that deploys continuously, it’s pure friction — even
Driessen later added a note that Git Flow is not for continuous-delivery web apps.
GitHub Flow: the middleweight
GitHub’s own model strips it to one rule: main is always deployable, everything else
is a short-lived branch.
main ──●──────●──────●──────●── (always deployable)
feature └─PR─┘ └─PR─┘ (branch, PR, review, merge, deploy)
The loop:
- Branch off
mainfor any change (feature/passkey-login). - Commit, push, open a pull request.
- Review, CI runs, discussion happens on the PR.
- Merge to
main— and deploy, immediately or on the next release. - Delete the branch.
No develop, no release branches. Fits: web apps and services that deploy
frequently — which is most of what I build. It keeps the PR-review checkpoint (where
signed commits and CI gates live) without
Git Flow’s branch sprawl. For a small team shipping a SaaS, this is usually the sweet spot.
Trunk-based development: the lightweight
The model behind continuous integration at scale: everyone commits to one branch
(main/trunk), and branches live hours, not days.
main ──●─●─●─●─●─●─●─●─●── (everyone integrates here, constantly)
Changes are tiny and merged the same day — often behind feature flags so incomplete
work can land on main without being active in production. Releases are cut from trunk by
tagging, not by maintaining a separate branch.
Fits: teams with strong CI and test coverage practicing real continuous delivery —
and, ironically, solo developers, for whom “trunk-based” just means “commit to main,
don’t bother branching for every little thing.”
The cost: it demands discipline the model doesn’t provide on its own. Without solid automated tests and feature flags, “everyone commits to main constantly” is a recipe for a broken trunk. The process is light precisely because the engineering underneath it is heavy.
Picking one
| Model | Branches | Fits | The tax |
|---|---|---|---|
| Git Flow | Many, long-lived | Versioned releases (apps, libraries) | Heavy process overhead |
| GitHub Flow | One main + short PRs | Continuously-deployed web apps | Needs deploy automation |
| Trunk-based | One trunk, hours-long | High-CI continuous delivery; solo devs | Needs tests + feature flags |
The honest take for small teams: you’re probably overthinking it. The number of
two-person teams running full Git Flow — with a develop branch, release branches, the
whole ceremony — for a web app that deploys on every merge is genuinely large, and it’s
almost always wasted motion. Short-lived feature branches off main, reviewed via PR,
merged and deployed (GitHub Flow) covers the vast majority of web work. Reach for Git Flow
only when you actually ship versioned releases; reach for strict trunk-based when your CI is
strong enough to trust it.
Caveats and best practices
- Branch lifetime is the real metric. Whatever you call your model, the thing that predicts merge pain is how long branches live. Short branches merge cleanly; week-old branches drift into conflict territory. Optimize for short, no matter the model.
- Match the model to your deploy cadence, not the other way around. Continuous deploy → GitHub Flow or trunk. Scheduled versioned releases → Git Flow. Let how you ship decide how you branch.
- Protect
mainregardless. Required PR reviews, passing CI, and (where it matters) signed commits should gatemainin every model. - Feature flags decouple “merged” from “released.” They’re what makes trunk-based safe and what lets GitHub Flow merge unfinished-but-inert work — worth adopting before you “need” them.
Conclusion
Three models, one underlying question: how does code get to production, and how often? Git Flow manages versions; GitHub Flow manages continuous deploys; trunk-based manages constant integration. Most small teams shipping web apps want GitHub Flow — short branches, PR review, merge, deploy — and most of the complexity beyond that is process borrowed from a problem they don’t have.