Branch Strategies
How a team decides what branches exist, who creates them, and how they merge. The strategy shapes deploy cadence and release process more than tooling does.
The four common strategies
1. Trunk-based development
One long-lived branch (main/trunk). Short-lived feature branches (≤ 1 day) that merge to main. Continuous deployment from main.
main: A───B───C───D───E───F (deployed continuously)
\ /
feature (merged in <1 day)
- Pros: minimum branching cost, real CI/CD, no merge hell.
- Cons: requires feature flags for half-built work, requires good test coverage.
- Best for: SaaS web apps, modern teams with strong CI/CD.
This is what most modern, high-performing teams do. (See “DORA / Accelerate” research — trunk-based correlates with deploy frequency.)
2. GitHub Flow
Like trunk-based but with PR-style branches that may live a few days. Main is always deployable.
main: A───B───C───────D───E
\ /
feature──(PR + review)
- Pros: simple, PR-based review.
- Cons: branches that live too long start to look like the next strategy.
GitHub themselves use this. Default for most open-source projects.
3. Git Flow (Vincent Driessen, 2010)
Multiple long-lived branches: main (production), develop (integration), feature/*, release/*, hotfix/*.
main: A─────────────────M───────H
\ / |
develop──D1──D2─/ |
\ |
feature hotfix
- Pros: clear release process; supports multi-version maintenance.
- Cons: heavy ceremony, slow to deploy, encourages long-lived branches.
- Best for: shipped software with explicit versions (desktop apps, mobile apps with App Store gates, embedded). Overkill for SaaS.
The author has since said it’s overused — most web teams should not adopt git-flow.
4. Release branches (the lightweight middle ground)
Trunk + occasional release branches for stabilization.
main: ───A───B───C───D───E───F───G───
\
release/1.4 ── stabilize ── tag v1.4.0
\── tag v1.4.1 (cherry-picked fixes)
- Pros: simple core (trunk), supports versioned releases when needed.
- Cons: cherry-picking fixes back/forward gets gnarly. Limit to 1-2 active release branches.
Picking one
Default for SaaS / web: Trunk-based or GitHub Flow. Short-lived branches, frequent merges, feature flags for incomplete work.
For shipped software with versioned releases: Trunk + release branches. Skip git-flow.
For “we have no convention”: GitHub Flow. It’s the easiest to onboard onto.
Branch naming conventions
| Pattern | Use |
|---|---|
feature/<jira-id>-short-desc |
features tied to a ticket |
fix/<short-desc> |
bug fixes |
hotfix/<short-desc> |
urgent production fixes |
chore/<short-desc> |
dependency bumps, refactors |
experiment/<short-desc> |
spike, may never merge |
Pick a convention and put it in the README. Tooling (Jira integrations, branch protection) often relies on the prefix.
Branch protection rules
For any branch that gets deployed (main, release/*):
- No direct pushes — must go through a PR.
- Required reviews — usually 1–2.
- Required status checks — CI must pass.
- Linear history — block merge commits if you want squash-only history.
- Conversation resolution required before merge.
- Signed commits for high-security repos.
- No force pushes —
--force-with-leasewould still bypass; explicitly disable for protected branches.
Configurable in GitHub Settings → Branches, GitLab Push Rules, Bitbucket Branch Permissions.
Long-lived feature branches — the trap
Anti-pattern: a branch that lives 3+ weeks. Symptoms:
- Constant merge conflicts when you
git pull main. - Reviewer can’t reasonably read all the changes.
- Behavior diverges from main; surprise breakage on merge.
Avoidance:
- Break the work into smaller PRs that ship behind a feature flag.
- Rebase onto main daily.
- If unavoidable: dedicate one engineer to keeping the branch in sync.
Feature flags as the alternative to long branches
Instead of “build the new checkout on a branch for a month,” merge incremental code to main behind:
if features.is_enabled("new_checkout", user):
return new_checkout(user)
return old_checkout(user)
- New code ships disabled (no user impact).
- Toggle on for internal users → beta → percentage rollout → 100%.
- Removes the branch-merge conflict problem entirely.
Flag tools: LaunchDarkly, Unleash, Statsig, Flipt, your own DB table.
Forks vs branches
Two ways to contribute to a repo you don’t have write access to:
| Branch | Fork | |
|---|---|---|
| Where the branch lives | original repo | your own copy of the repo |
| Need write access? | yes | no — anyone can fork |
| Common in | private/team repos | open source |
| PR direction | branch → main (same repo) | fork → upstream |
Open-source standard: fork → branch in fork → PR to upstream. Internal teams: branch directly in the shared repo, PR to main.
Tags vs branches
Both are pointers to commits, but:
| Branch | Tag | |
|---|---|---|
| Moves with new commits? | yes | no — frozen pointer |
| Use | active development | mark a specific commit (release version) |
Tag a release: git tag -a v1.4.0 -m "v1.4.0" then git push --tags. Annotated tags (-a) include author, date, message; lightweight tags are just refs.
Common interview confusions
- “Trunk-based means no branches at all.” — short-lived branches (≤ 1 day) are fine. Just no long-lived integration branches.
- “Git Flow is the standard.” — was popular ~2012; now considered overkill for most web teams. The author publicly walked it back.
- “You can’t deploy from a branch.” — you can, but in trunk-based you usually deploy from main. Branch deploys are for staging/preview environments.
Interview angle
- “What branch strategy does your team use and why?” — name the strategy, explain the deploy cadence and team size. Most modern web teams: GitHub Flow / trunk-based. Mention feature flags for in-progress work.
- “What’s wrong with long-lived feature branches?” — merge hell, hard to review, behavior diverges from main, reviewer fatigue. Mitigate with smaller PRs + feature flags.
- “When does Git Flow make sense?” — versioned shipped software (desktop, mobile, embedded) with multiple supported releases. Overkill for SaaS.
- “What’s branch protection and what should you require on
main?” — required reviews, required CI status checks, no direct pushes, no force pushes, linear history if you want squash-only. - “How do feature flags relate to branch strategy?” — they let you merge incomplete work to main safely, replacing long-lived branches with toggles. Enables true continuous deployment.
- “Fork vs branch — when each?” — fork for open-source contributions where you don’t have write access; branch for internal team work where you do.