Branching Strategies
Course 1 covered branches mechanically — create, switch, merge. What it didn't cover is the bigger question: which branches should exist, what are they for, and when do they get merged? That's a branching strategy — a set of conventions a team (or even a solo developer) agrees to follow, so collaboration stays predictable. This chapter covers the three most common approaches and how to choose between them.
Git Flow — The Original, Structured Approach
Git Flow was one of the earliest formalised branching models, built around scheduled releases. It defines several long-lived branch types, each with a specific job.
- main — always reflects what's currently in production, nothing else.
- develop — the integration branch where finished features accumulate between releases.
- feature/* — one branch per feature, branched from develop, merged back into develop when done.
- release/* — cut from develop when preparing a release; only bug fixes happen here while it stabilises, then merges to both main and develop.
- hotfix/* — branched directly from main for urgent production fixes, merged back to both main and develop.
GitHub Flow — Simpler, Continuous Deployment
GitHub Flow strips this down to almost nothing: one long-lived branch (main), and short-lived feature branches that get reviewed via PR (Course 1, Chapter 7) and merged straight back in.
No develop, no release/*, no hotfix/* — every change follows the exact same path: branch → commit → open PR → review → merge → deploy. This matches how most modern web projects actually ship, including continuous deployment setups where every merge to main can trigger an automatic deploy (Chapter 8 covers the GitHub Actions side of this).
Trunk-Based Development — The Leanest Option
Trunk-based development pushes simplicity even further: developers commit directly to main (the "trunk") as much as possible, using very short-lived branches — often merged within hours, not days — or sometimes feature flags instead of branches entirely, to hide unfinished work from users without needing a separate branch at all.
- Branches, when used, live for hours, not days. The goal is minimising how far any branch diverges from main before merging back.
- Feature flags hide incomplete work — code can be merged to main and deployed while disabled behind a flag, separating "merged" from "visible to users" entirely.
- Requires strong automated testing to be safe — without a solid CI pipeline catching regressions before they reach main, frequent direct commits to main become risky fast.
Comparing the Three
Choosing for Your Own Project
| Question | If yes → |
|---|---|
| Do you ship in discrete numbered versions? | Git Flow |
| Do you deploy continuously, every merge can go live? | GitHub Flow |
| Solo project or very small team? | GitHub Flow — simplest to maintain alone |
| Large team, strong CI, frequent merge conflicts a real pain point? | Trunk-based, with feature flags |
| Not sure / starting fresh? | GitHub Flow — easiest to adopt, easiest to explain to new contributors |
Chapter 1 Quick Reference
- Git Flow — main/develop/feature/release/hotfix branches; built for versioned, infrequent releases
- GitHub Flow — main + short feature branches + PRs; built for continuous deployment; the sensible default for most projects
- Trunk-based — very short-lived branches or direct commits to main, often paired with feature flags; needs strong CI first
- Choosing: versioned releases → Git Flow; continuous deploy → GitHub Flow; high-velocity team with strong tests → trunk-based
- When unsure, default to GitHub Flow — simplest to adopt and explain
- Next chapter: merge vs rebase — what's actually happening under the hood, and when to use each