Choosing a Branching Model
A branching model is the team's agreement on how branches map to releases and deploys. There is no universally correct answer, and most of the choice collapses to two questions: how often do you ship, and do you have to support more than one released version at the same time?
The three common models sit on a spectrum from heavy to light. Git Flow keeps long-lived branches for parallel-version maintenance; GitHub Flow keeps a single main you deploy from; trunk-based development pushes everyone onto main daily behind feature flags. Pick by deploy cadence, not by what looks rigorous on a diagram.
GitHub Flow
GitHub Flow is the minimal model: one long-lived main, short feature branches cut from it, and deploys straight from main once a PR merges. It assumes you ship to a single production continuously and do not maintain old versions in the field.
The whole model fits on a postcard, which is the point — there is no develop, no release/*, and nothing to keep in sync. For a web service behind one URL, the ceremony of anything heavier buys you nothing.
Git Flow
Git Flow adds a permanent develop branch alongside main, plus release/*, hotfix/*, and feature/* branches with defined merge paths between them. It is built for software that ships discrete versions and supports several of them at once: desktop apps, SDKs, on-prem installs.
That structure earns its keep only when you genuinely run multiple released versions in parallel. On a continuously deployed web app it is overhead — every release becomes a multi-branch merge dance for a version concept you do not have.
Trunk-Based Development
In trunk-based development everyone commits to main at least daily, and branches live hours rather than days. Incomplete work ships to production hidden behind feature flags, so a half-built feature is merged but dark until the flag flips on.
It minimizes merge pain because nothing diverges long enough to conflict, but it demands strong CI and disciplined flag hygiene. Without flags, trunk-based just means shipping unfinished features.
The Deciding Factors
Four variables decide the model: deploy frequency, number of supported versions, team size, and CI maturity. Many-deploys-a-day with solid CI points at trunk-based; continuous web delivery to one production points at GitHub Flow; multiple maintained versions in the wild is the one case that justifies Git Flow.
Release Branches and Hotfixes
A release/* branch is worth its cost only when you must patch an old version that customers are still running — mobile builds awaiting store review, on-prem deployments, SDKs with supported legacy lines. For everything else, a release branch is a place for bugs to diverge and hotfixes to get forgotten on one side.
Git Flow — two permanent branches plus release/* and hotfix/*, fitting software with multiple released versions to maintain (desktop, SDKs, on-prem). It adds merge overhead and slows delivery, so it is waste anywhere you ship one production continuously.
GitHub Flow — one main, short feature branches, deploy from main; the right default for web apps shipping continuously to a single production. Trunk-Based — commit to main daily behind feature flags; fits high-frequency teams with strong CI, minimizing merge pain but demanding flag discipline. Pick by deploy cadence: many-versions points at Git Flow, continuous web at GitHub Flow, many-deploys-a-day at trunk-based.
- Adopting Git Flow for a web app that deploys twenty times a day — the
developandreleaseceremony adds merge overhead for releases you do not actually have. - Doing trunk-based development without feature flags, so half-finished features reach production because there is no mechanism to hide them.
- Letting feature branches live for two weeks in any model — they accumulate conflicts that take longer to resolve than the feature took to write.
- Cherry-picking hotfixes between
mainanddevelopby hand in Git Flow and forgetting one branch, so the bug reappears in the next release. - Mixing models across teams in one monorepo, where CI and protection rules cannot satisfy two contradictory branch conventions at once.
- Default to GitHub Flow for web services and reach for Git Flow only when you support multiple released versions in the wild.
- Keep feature branches under a few days and merge to
mainfrequently to limit the conflict surface. - Pair trunk-based development with feature flags so incomplete work stays dark in production until it is ready.
- Enforce the chosen model with branch protection and required status checks so following it is not optional.
- Use
release/*branches only for products with genuine parallel-version support needs, not as a default release step.
Knowledge Check
Your team ships a web app to one production roughly twenty times a day with strong CI. Which model fits?
- GitHub Flow or trunk-based — a single
maindeployed continuously, with no parallel versions to maintain - Git Flow, because frequent deploys need the safety of a
developandreleasebranch - A long-lived
release/*branch cut for every deploy, kept open in case the build needs patching later - A separate model per developer, each person choosing whichever workflow they personally prefer
Why does trunk-based development require feature flags?
- Because work merges to
mainbefore it is finished, flags hide incomplete features from production until they are ready - Because flags replace the need for any CI, letting the team skip the test pipeline entirely on each merge to
main - Because trunk-based forbids branches entirely, so flags become the only way to isolate and test new work
- Because feature flags are what compress the commit graph down into a single linear history on
mainwith no merge commits
When is Git Flow's overhead actually justified rather than waste?
- When you support multiple released versions in the field at once, such as on-prem installs or SDKs with legacy lines
- Always — its extra
developandreleasebranches make every team safer regardless of cadence or deploy frequency - Only for a solo developer who never collaborates and wants the structure to organize their own work
- Whenever you deploy a single-production web app more than once a day and want a documented paper trail recorded per release
What is the cost of a feature branch that lives for two weeks?
- It accumulates conflicts against a moving
mainthat can take longer to resolve than writing the feature did - Git refuses the merge once a branch is older than a week and forces you to recreate it from
main - The branch is automatically deleted by GitHub after a fixed inactivity window, losing the unmerged work
- Nothing — branch age has no effect on merge difficulty, since Git replays the same diff either way
You got correct