Git & Collaboration Workflows

Branching Strategies Compared

18 min Lesson 5 of 28

Branching Strategies Compared

Choosing a branching strategy is one of the highest-leverage decisions a team makes. Get it right and releases become routine. Get it wrong and you spend your afternoons resolving merge conflicts, your evenings chasing regressions, and your weekends firefighting broken releases. This lesson maps the three dominant strategies — Git Flow, GitHub Flow, and Trunk-Based Development (TBD) — explains the engineering trade-offs of each, and shows you what top-tier companies actually deploy in production.

Git Flow: Structured Parallel Development

Introduced by Vincent Driessen in 2010, Git Flow defines a strict set of long-lived and short-lived branches. It was designed for software with explicit release cycles — think versioned libraries, mobile apps shipping to app stores, or enterprise software with quarterly releases.

Permanent branches: main (or master) always reflects production. develop accumulates completed features before a release.

Supporting branches (short-lived):

  • feature/* — branch from develop, merge back to develop.
  • release/* — branch from develop when the release is feature-complete; only bug fixes go in; merges to both main (tagged) and develop.
  • hotfix/* — branch from main directly; merges to both main and develop.
# Initialise a repo with Git Flow (requires git-flow CLI) git flow init -d # accept all defaults # Start a feature git flow feature start user-auth # Finish it — merges to develop, deletes branch git flow feature finish user-auth # Cut a release git flow release start 2.4.0 # ... fix release bugs, bump version file ... git flow release finish 2.4.0 # merges to main + develop, tags main # Emergency hotfix on production git flow hotfix start fix-payment-crash git flow hotfix finish fix-payment-crash
Git Flow's hidden cost: Long-lived develop and release branches accumulate divergence. By the time release/2.4.0 merges, develop has moved on — and the reverse merge into develop is notoriously painful. Teams that adopted Git Flow in 2012 and never re-evaluated often spend 20–30% of engineering time on merge management. Even Driessen himself added a note to his original post recommending TBD for teams doing continuous delivery.

GitHub Flow: Simplicity for Continuous Delivery

GitHub Flow reduces the branching model to a single rule: main is always deployable. Every change — feature, fix, refactor — lives in a short-lived feature branch that is opened as a pull request, reviewed, and merged directly into main. Deployment happens immediately after merge, or on merge via CI/CD.

# GitHub Flow in practice git switch -c feature/add-oauth main # ... commit work ... git push origin feature/add-oauth # Open PR via CLI gh pr create --base main --title "Add OAuth login" --body "Closes #412" # After approval and passing CI, merge and deploy gh pr merge --squash --delete-branch # main is deployed immediately by CI/CD pipeline

GitHub Flow works extremely well for SaaS products that ship multiple times per day. The PR is the unit of work: it carries the diff, the discussion, the CI results, and the deployment gate in one place.

Where GitHub Flow struggles: It has no built-in mechanism for managing multiple concurrent release versions. If you need to maintain v2 and v3 simultaneously (common for APIs or mobile SDKs), you are on your own. Teams often improvise release/v2 long-lived branches, effectively inventing a subset of Git Flow on top of GitHub Flow.

Trunk-Based Development: What High-Velocity Teams Actually Use

Trunk-Based Development is the strategy behind Google's monorepo (1 billion lines of code, one branch), Meta's internal tooling, and the majority of elite continuous-delivery organisations. The rule is even simpler than GitHub Flow: everyone commits directly to main (the trunk) at least once per day. Feature branches, when they exist at all, live for less than 24 hours.

TBD sounds chaotic to engineers who are used to long-lived branches, but it works because of three complementary practices:

  1. Feature flags — incomplete work ships behind a flag that is off in production. The next lesson covers this in depth.
  2. Comprehensive automated testing — the CI pipeline catches regressions before they reach main; a broken build is an all-hands emergency.
  3. Pair/mob programming or very tight PR review cycles — code is reviewed in real time or within 1–2 hours, not days.
# Trunk-Based Development daily workflow # Short-lived branch (optional, max 1-2 days) git switch -c feat/rate-limiter # ... make changes, write tests ... git push origin feat/rate-limiter # PR reviewed and merged same day gh pr create --base main --fill gh pr merge --squash --delete-branch # If committing directly to trunk (small safe change) git switch main git pull --rebase # ... edit ... git commit -m "fix: correct off-by-one in retry backoff" git push origin main
Three Branching Strategies Side by Side Git Flow main develop feature/* release hotfix When to use • Versioned / packaged software • App store or quarterly releases • Parallel version maintenance GitHub Flow main PR #1 PR #2 Deploy When to use • SaaS / continuous deployment • Single active version in prod • Small-to-medium teams Trunk-Based Dev trunk (main) <1 day Feature Flag When to use • High-velocity engineering orgs • Strong CI test coverage • Feature-flag infrastructure ready More process More speed / discipline Release: weekly–quarterly Release: on every merge Release: multiple times / day Team: any size Team: small–medium Team: any, tight discipline
Git Flow, GitHub Flow, and Trunk-Based Development compared — branch topology, deployment cadence, and ideal context for each.

What Big Tech Actually Uses

The empirical answer is: the industry has moved overwhelmingly toward Trunk-Based Development, with GitHub Flow as a pragmatic middle ground for smaller teams. Google, Meta, Microsoft (internal tooling), LinkedIn, and Etsy have all published engineering blog posts confirming TBD at scale. The DORA (DevOps Research and Assessment) report consistently finds that trunk-based development is a statistically significant predictor of elite software delivery performance — shorter lead times, higher deployment frequency, lower change failure rate.

The key enabler is not courage — it is engineering infrastructure: a fast, comprehensive CI pipeline (Google's is < 10 minutes for most targets), a mature feature-flag platform (LaunchDarkly, Unleash, Flipt, or homegrown), and a culture where a broken build is an immediate priority, not a post-standup topic.

Choosing the Right Strategy for Your Team

Apply these heuristics in order:

  1. Do you maintain multiple released versions simultaneously? If yes, Git Flow (or a simplified variant with long-lived release/* branches on top of GitHub Flow) is probably necessary.
  2. Can you deploy to production after every merge? If yes, start with GitHub Flow. Graduate to TBD once your test suite is comprehensive and review cycles are tight.
  3. Is your CI pipeline under 15 minutes end-to-end? TBD only works if the feedback loop is fast enough that developers do not batch up work to avoid waiting.
  4. Do you have feature flags? TBD without feature flags means incomplete features ship to users. If you cannot deploy flags, stay on GitHub Flow until you can.
Practical migration path: Most teams succeed by moving Git Flow → GitHub Flow first (flatten develop into main, enforce PR-before-merge). That alone reduces merge pain by 70–80%. Then, once CI is solid and flags are in place, shorten branch lifetimes toward zero — that is TBD. Skipping straight from Git Flow to TBD usually fails because the prerequisite infrastructure is not yet there.

Release Branches: The One Git Flow Piece Worth Keeping

Even teams on GitHub Flow or TBD sometimes need release branches — when you are releasing a mobile app, an npm package, or a compiled binary where you cannot push a fix without a new store submission or CDN invalidation. In that case, a release/vX.Y branch cut from main at the release commit, used exclusively for cherry-picked critical fixes, is the right tool. Merge every fix back to main immediately using git cherry-pick (covered in Lesson 3).

# Maintaining a release branch alongside trunk-based main git switch -c release/v3.2 v3.2.0-tag # cut from the tag git push origin release/v3.2 # When a critical fix is needed on the release line: git switch main git cherry-pick <fix-sha> # fix main first (trunk rule) git switch release/v3.2 git cherry-pick <fix-sha> # then port to release git tag -a v3.2.1 -m "Patch: fix payment crash" git push origin release/v3.2 --tags

The discipline rule: nothing lands on a release branch that has not already landed on main. Release branches are a delivery mechanism, not a development environment.

Common Failure Modes

  • "GitHub Flow but branches live for two weeks" — this is not GitHub Flow, it is slow Git Flow without the structure. Long-lived branches defeat the entire model. Enforce a branch age limit in CI (e.g., fail PRs open longer than 3 days on stale base).
  • Skipping the reverse merge in Git Flow — forgetting to merge release/* back into develop means the next release is missing the very fixes you just shipped. Git Flow tooling automates this, but manual Git Flow workflows routinely skip it.
  • TBD without tests — committing to trunk daily without a comprehensive, fast test suite is just "we all edit the same branch and hope for the best." TBD requires tests as a hard prerequisite.
  • Feature flags left on forever — flags accumulate as technical debt. At Netflix and Google, flags are given a scheduled removal date at creation time. Stale flags cause incidents (a flag left on that should have been removed has taken down production at multiple companies).
The DORA finding in one sentence: Teams that practise trunk-based development with short-lived branches deploy 208× more frequently and recover from incidents 2,604× faster than low-performing teams. The branching strategy you choose is not a style preference — it is an engineering performance lever.