Git Branching Strategies

Git makes branches cheap, which means a team has to decide how to use them. How long do branches live? Where does release-ready code sit? How do hotfixes reach production? A branching strategy answers these questions, and it shapes how often you integrate, how painful merges are, and how fast changes reach users.

The industry has largely converged on short-lived branches merged frequently into one main branch, backed by strong CI and feature flags. Heavier models like GitFlow still fit some products, such as versioned software and mobile apps with store review. The key is choosing deliberately rather than inheriting a process.

TL;DR

Quick Example

GitHub Flow in practice:

With branch protection on main (required reviews, required status checks, linear history), every change reaching production has been reviewed and tested.

Core Concepts

Trunk-Based Development

Developers integrate small changes into the trunk (main) continuously, at least once a day. Branches, if used, live hours rather than weeks. Unfinished features ship dark behind feature flags, and releases are cut from trunk, often automatically on every merge.

GitHub Flow

One long-lived branch (main, always deployable) plus short-lived topic branches merged through pull requests. It's effectively trunk-based development with mandatory PR review. It's simple enough to explain in a sentence and fits most web services.

GitFlow

GitFlow gives structure for planned, versioned releases, but its long-lived branches cause merge debt, and double-merging hotfixes is error-prone. Even its author has noted it's a poor fit for continuously delivered web apps.

Release Branches

For products that support several shipped versions (libraries, on-prem software, mobile apps), cut release/x.y from main when stabilizing a release. Fixes land on main first and are cherry-picked to supported release branches. This keeps main moving while shipped versions get patches.

Merge Policies

Squash-merge is the most common default for application repos: one PR equals one commit, which pairs well with Conventional Commits for changelogs and makes git revert of a whole feature trivial. See Git rebase for keeping branches current.

Choosing a Strategy

Best Practices

Keep Branches Short-Lived

The longer a branch lives, the more it diverges and the more painful the merge. Break large features into small, independently mergeable steps. Use flags to hide incomplete work, and stacked PRs when changes depend on each other.

Protect the Main Branch

Require pull requests, passing status checks, and at least one approval, and block force-pushes. Enable merge queues on busy repos so each merge is tested against the latest main.

Automate Releases From Tags or Merges

Releases shouldn't depend on someone remembering the steps. Tag releases (v2.4.0) and let CI build and publish, or deploy every merge to main behind progressive rollout. See CI/CD.

Name Branches Consistently

Prefixes like feat/, fix/, chore/, plus a ticket ID (feat/PAY-142-refund-api), make branches searchable and let automation link them to issues.

Common Mistakes

Long-Lived Feature Branches

A branch open for three weeks against a busy main ends in a merge that's risky to resolve and hard to review. Integrate at least daily, or rebase regularly and split the work.

GitFlow for a Continuously Deployed Web App

Maintaining develop, release branches, and back-merges adds ceremony without benefit when every merge could ship. Simplify to GitHub Flow.

Environment Branches

Branches named staging and production that code is "promoted" through by merging drift apart quickly and create confusing conflicts. Promote the same build artifact through environments instead, and keep environment configuration outside the branch model. See deployment strategies.

FAQ

What's the difference between trunk-based development and GitHub Flow?

They're closely related. Both have one main branch and short-lived branches. Strict trunk-based development emphasizes integrating at least daily, sometimes committing directly to trunk, with feature flags for incomplete work. GitHub Flow always goes through a pull request but doesn't prescribe how long branches live. In practice, GitHub Flow with small, fast PRs is trunk-based development.

Is GitFlow outdated?

For continuously delivered web applications, largely yes: its long-lived branches slow integration. It still fits products with scheduled, versioned releases and several supported versions, though many such teams now prefer trunk plus release branches.

How do feature flags relate to branching?

Flags decouple deploying code from releasing features. You can merge unfinished work to main while it's hidden, which removes the need for long-lived feature branches. See feature flags.

Should I squash-merge or rebase-merge?

Squash if individual PR commits are messy or you want one revertible commit per change. Rebase-merge if your team writes clean, meaningful commits and values the granular history. Whichever you pick, enforce it in repository settings so history stays consistent.

Related Topics

References