Lesson 22 of 30
Trunk-based, git-flow, GitHub flow — three answers to 'how long should a branch live,' and the wrong answer costs you merge conflicts you didn't need.
16 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
A branching strategy is just a team's agreed answer to one question: how long does a branch live before it merges back, and what has to happen before it does? That single decision cascades into how often you integrate, how big your merge conflicts get, and how fast a fix reaches production. Three strategies dominate real teams:
Git-flow is the oldest and heaviest. It formalizes multiple long-lived branches: main holds only released code, develop is the integration branch, feature/* branches fork from develop and merge back into it, and release/* branches stabilize a version before it becomes a tagged commit on main. It also has hotfix/* branches that patch main directly and get merged back into both main and develop. It was designed for software with scheduled, versioned releases — think desktop apps shipping v2.3, v2.4.
GitHub flow strips that down to one long-lived branch, main, which is always deployable. Everything else is a short-lived feature branch that forks from main, gets a pull request, gets reviewed, and merges straight back into main — no develop, no release branch. It assumes you can deploy main continuously, which fits web apps and services far better than versioned desktop software.
Trunk-based development goes further still: branches (if they exist at all) live hours, not days. Developers commit directly to main — or to branches so short they're barely branches — behind feature flags, so incomplete work ships to production dark and gets turned on later. Integration happens constantly instead of in batches.
The tradeoff underneath all three is the same one: the longer a branch lives apart from main, the more main drifts out from under it, and the uglier the eventual merge conflict. Git-flow's develop branch can be weeks stale relative to main's hotfixes. Trunk-based development makes that drift structurally impossible by never letting it accumulate. That's the real reason most modern teams have moved toward GitHub flow or trunk-based: not fashion, but that CI/CD made "deploy constantly" achievable, which removed git-flow's whole reason for existing — coordinating rare, big-batch releases.
A critical bug fix, in each model:
Git-flow:
$ git checkout -b hotfix/login-crash main
# fix, commit
$ git checkout main && git merge hotfix/login-crash
$ git checkout develop && git merge hotfix/login-crash
$ git tag v2.4.1
Two merges, because the fix has to land in both branches that track "current state."
GitHub flow:
$ git checkout -b fix/login-crash main
# fix, commit, push
$ gh pr create --base main
# review, then merge — main is deployed
One branch, one PR, one merge. main was already deployable, so merging it is shipping it.
Trunk-based:
$ git checkout main && git pull
# fix, commit directly (or a branch that lives under an hour)
$ git push origin main
No PR ceremony assumed (though many trunk-based teams still use short-lived PRs) — the fix is live within minutes, often wrapped in a feature flag if it touches anything user-facing that isn't fully tested yet.
Trunk-based development: one main line. Branches are short-lived — created, worked on for hours, and merged straight back into main.
main is always releasable. That's a discipline, not a default — it requires CI gating every merge (tests, lint, build) so a broken main is rare enough that "deployable" stays true in practice, not just in name.Every strategy here is really a policy about branch lifetime, and branch lifetime is what this track's earlier lesson on merge conflicts identified as the actual variable that predicts conflict pain — divergence between two histories, not "how many people touched the file." Trunk-based development is the logical end of minimizing that variable; git-flow is what you get when you accept it in exchange for release control.