Cloud Parts

Branching strategies

A team’s rules for how changes flow into the shared code and out to releases: which branches exist, how long they live, and how they get merged.

How it works

Every team that shares a repository needs an agreement about branches, and the main variable is how long work stays apart before it is integrated. In trunk-based development everyone merges small changes into one branch (trunk or main) at least once a day; branches, if used at all, last hours, and unfinished features hide behind feature flags rather than living on a branch. GitHub flow is a light version for teams that deploy continuously: main is always deployable, each change gets a short branch and a pull request, and a merged pull request is deployed straight away. Git flow, published by Vincent Driessen in 2010, keeps two permanent branches, main (originally master) for released code and develop for the next release, plus feature, release and hotfix branches; it suits software shipped in numbered versions. Any of these can add release branches: a branch cut from the mainline to stabilize one version while new work continues, with fixes made on the mainline and cherry-picked across. The longer a branch lives, the further it drifts and the bigger and riskier its merge. The last choice is how a branch lands: a merge commit keeps its commits and records the join, a rebase replays its commits on top of main for a straight-line history, and a squash merge folds the whole branch into one commit.

Think of it as

Writers on a shared document. One team types into the live draft all day in small edits; another writes chapters in private copies for weeks and then faces a painful afternoon pasting them back in.

Reach for it when

  • Trunk-based or GitHub flow: a web service deployed many times a day, with fast automated tests
  • Git flow or release branches: installed software, libraries or firmware where several versions are supported at once
  • Feature flags: you want to merge unfinished work daily without exposing it to users

Think twice when

  • Long-lived feature branches that take weeks to merge; split the work or put it behind a flag
  • Git flow for a continuously deployed web app; its own author now recommends something simpler
  • Rebasing commits other people have already pulled; it rewrites history they have built on

A real example

A team of eight runs a SaaS product on Git flow, with a release branch every two weeks and a two-day merge-and-stabilize ritual before each one. They switch to trunk-based development: each change is a branch of a few hours with a pull request, CI runs on every push, and a half-built billing page ships to production switched off behind a feature flag until it is ready. They delete the develop branch, and production now gets several small deployments a day instead of one big one every fortnight.

The strategies side by side#

StrategyLong-lived branchesTypical branch lifeHow a release happensFits
Trunk-based developmentOnly trunk (release branches optional)A few hoursDeploy from trunk, or cut a release branch when it’s timeTeams with fast tests and feature flags, at any size
GitHub flowOnly mainOne pull request’s worth of workEach merged pull request is deployed at onceWeb apps and services deployed continuously
Git flowmain and developFeature branches last as long as the featureA release branch from develop is stabilized, merged to main and taggedExplicitly versioned software with several versions in use
Release branchesOne per supported versionUntil that version is retired, sometimes yearsOnly stabilizing fixes go in; new work stays on the mainlineProducts with versions in the field that need patches
  • DORA’s guidance for trunk-based development: three or fewer active branches, merge to trunk at least once a day, and no code freezes or integration phases.

Merge, rebase or squash#

Hosts such as GitHub let a repository allow or enforce each method.

Merge commit

--no-ff

Every commit from the branch is kept, plus a merge commit that records when the branch joined. Nothing is rewritten, but the history branches and rejoins, which some find hard to read.

Rebase and merge

linear

The branch’s commits are replayed on top of main, so history reads as one straight line. The replayed commits get new hashes, so never rebase commits that others have already based work on.

Squash and merge

one commit

The whole branch becomes one commit on main, so work-in-progress commits disappear and main has one commit per change. The branch’s individual steps are lost from main’s history.

In code · bash

# Three ways to land the branch fix-cart on main

# 1. Merge commit: keeps every branch commit plus a commit that joins them
git switch main && git merge --no-ff fix-cart

# 2. Rebase: replay the branch on top of main, then fast-forward (straight history)
git switch fix-cart && git rebase main
git switch main && git merge --ff-only fix-cart

# 3. Squash: the whole branch becomes one new commit on main
git switch main && git merge --squash fix-cart
git commit -m "Fix rounding in cart totals"

Often confused with

Where it came from1999–2020

Branching is as old as version control, and so is the pain of merging. Large merges of long-separated work often brought bugs and regressions, so teams held code freezes and integration phases before each release. In the 1990s Microsoft’s daily builds and then Extreme Programming’s continuous integration pushed the other way: merge everything into one mainline at least daily so merges stay small. When Google adopted Perforce it worked trunk-based from the outset, and Paul Hammant later collected the techniques at trunkbaseddevelopment.com.

Git made branches cheap and fast, and teams started using many of them. In January 2010 Vincent Driessen published “A successful Git branching model”, with permanent master and develop branches plus feature, release and hotfix branches, and git-flow became one of the most common policies around. In August 2011 Scott Chacon explained why GitHub didn’t use it: GitHub deployed to production every day, often several times, so it had no “releases” to manage. GitHub flow had one rule at its core: anything in master is deployable.

Research and hindsight then favored integrating often. Analysis of DORA’s 2016 and 2017 survey data found that teams with three or fewer active branches, daily merges to trunk and no code freezes achieved higher delivery speed, stability and availability. In 2017 Pete Hodgson described release toggles as the feature flags that make trunk-based development possible. In March 2020 Driessen added a note to his post: for continuously delivered software, adopt a simpler workflow like GitHub flow; git-flow still fits software that is explicitly versioned.

Why it’s built this way

Integrate at least daily
Large merges of long-lived branches often introduce bugs and lead to code freezes. Merging small batches into trunk every day keeps each merge small and the mainline always working.
Hide unfinished work behind flags, not on branches
A release toggle lets incomplete code ship to production switched off, so it can be merged daily. The cost is extra code paths to test and old flags to remove, which Hodgson calls the carrying cost of toggles.
A separate develop branch in Git flow
Driessen wanted every commit on master to be a production release by definition, so integration happened on develop. That makes sense when releases are planned events, and adds overhead when every merge is a release.
Fix on the mainline, then cherry-pick to the release
Fixes made only on a release branch are easy to forget to copy back, and the regression reappears in the next version. Fowler notes that many teams make the fix on the mainline first and cherry-pick it into the release branch.

Timeline

  1. 1999

    Extreme Programming popularizes continuous integration into one mainline

  2. 2010

    Vincent Driessen publishes the git-flow branching model in January

  3. 2011

    Scott Chacon describes GitHub flow in August

  4. 2017

    Pete Hodgson’s “Feature Toggles” describes release toggles for trunk-based teams

  5. 2020

    Driessen’s note of reflection (March); Martin Fowler’s “Patterns for Managing Source Code Branches” (May)

Sources9

Improve this page on GitHub