jongkwan.dev
Development · Essay №018

Git Branching Strategies: From Git Flow to Trunk-Based

A comparison of Git Flow, GitHub Flow, Trunk-Based Development and how to choose between them

Jongkwan Lee2025년 9월 13일11 min read
Contents

A branching strategy follows from team size, release cadence, and continuous integration and delivery (CI/CD) maturity, and there is no single right answer.

Overview

A branching strategy is the convention that defines how a team separates, integrates and releases code. What fits depends on team size, release cadence and the nature of the product. Organizations built around continuous integration and delivery (CI/CD) have widely adopted Trunk-Based Development.

Git Flow

The traditional branching model Vincent Driessen proposed in 2010. It suits projects with periodic releases.

Branch structure

text
master (production)

  ├── hotfix/payment-bug ────→ merged into master + develop

  ├── release/1.2.0 ─────────→ merged into master + develop

develop (integration branch)

  ├── feature/user-auth ──→ merged into develop
  ├── feature/payment ────→ merged into develop
  └── feature/notification → merged into develop

What each branch is for

BranchLifetimePurposeMerge target
master (main)PermanentCode deployed to production-
developPermanentIntegration for the next release-
feature/*TemporaryNew feature developmentdevelop
release/*TemporaryRelease preparation (quality assurance (QA), bug fixes)master + develop
hotfix/*TemporaryUrgent production fixesmaster + develop

The Git Flow workflow

bash
# 1. start work on a feature
git checkout develop
git checkout -b feature/user-auth
 
# 2. feature is done, merge into develop
git checkout develop
git merge --no-ff feature/user-auth
git branch -d feature/user-auth
 
# 3. prepare the release
git checkout develop
git checkout -b release/1.2.0
 
# 4. release is done, merge into master and develop
git checkout master
git merge --no-ff release/1.2.0
git tag -a v1.2.0 -m "Release 1.2.0"
git checkout develop
git merge --no-ff release/1.2.0
git branch -d release/1.2.0
 
# 5. hotfix
git checkout master
git checkout -b hotfix/payment-bug
# after the fix
git checkout master
git merge --no-ff hotfix/payment-bug
git tag -a v1.2.1 -m "Hotfix: payment bug"
git checkout develop
git merge --no-ff hotfix/payment-bug

--no-ff blocks a fast-forward merge so a merge commit is always recorded. The work for one feature stays grouped under a single commit, which makes it easy to trace later which branch was integrated and when.

Strengths and weaknesses

StrengthsWeaknesses
Release management is systematicMany branches, high complexity
Version history is explicitFrequent merge conflicts
Production stays stableDoes not sit well with CI/CD
Suits large teamsRelease cycles can stretch out

When it fits

  • Mobile apps (app store review cycles)
  • On-premise software
  • Periodic releases (every two or four weeks)
  • Products that maintain several versions at once

Trunk-Based Development (TBD)

A strategy centered on a single main branch (trunk/main), with short-lived branches merged frequently. Google, Facebook and Netflix use it, and it is common in CI/CD-driven organizations.

Core principles

  1. Short-lived branches: two days at most, ideally a few hours
  2. Frequent integration: merge into main several times a day
  3. Always deployable: main can go to production at any moment
  4. Feature flags: unfinished features live in the code but stay switched off

Feature flags

java
// Feature flag gating an unfinished feature
if (featureFlags.isEnabled("new-checkout-flow")) {
    return newCheckoutService.process(order);
} else {
    return legacyCheckoutService.process(order);
}
Feature flag toolCharacteristics
LaunchDarklySaaS, real-time toggles, A/B testing
UnleashOpen source, self-hosted
FlagsmithAvailable both open source and as SaaS
AWS AppConfigNative AWS integration
OpenFeatureVendor-neutral standard (under the Cloud Native Computing Foundation (CNCF))

TBD and CI/CD

Strengths and weaknesses

StrengthsWeaknesses
Merge conflicts stay smallFeature flags are their own management burden
Fast feedback loopRequires strong CI/CD
Highest possible deployment frequencyRequires automated tests
Lighter review load (small PRs)An inexperienced team destabilizes main

GitHub Flow

The simplified branching model GitHub proposed. It uses only main and feature branches.

Workflow

text
main ─── A ─── B ─── C ─── D ─── E ─── F
              \           /
               └── feat ──┘
                 (PR + Review)
  1. Branch off main
  2. Add commits
  3. Open a pull request
  4. Review the code
  5. Merge into main
  6. Deploy immediately

Compared to Git Flow

AspectGit FlowGitHub Flow
Number of branch types52 (main + feature)
ComplexityHighLow
Deployment frequencyPeriodicImmediately on merge
Team size it fitsLarge teamsSmall to mid-size teams

GitLab Flow

GitHub Flow with environment branches added.

text
main ──→ staging ──→ production

Environment branches

text
feature → main (development)


         staging (test/QA)


         production
  • Provides a deployment gate per environment
  • Hotfixes can be cherry-picked straight onto production
  • Environment branches take the place of release branches

Release Flow (Microsoft)

The strategy Microsoft uses on large teams such as Azure DevOps.

Core structure

  • main: like Trunk-Based, always deployable
  • release branches: cut from main at release time, hotfixes only, applied by cherry-pick
  • Suits products that must support several releases simultaneously

Monorepo strategy

What a monorepo is

Managing several projects or services in a single Git repository. Google, Meta and Microsoft all do this.

The monorepo tooling ecosystem

ToolCharacteristicsUsers
NxSmart build cache, dependency graph analysisNrwl and others
TurborepoRemote caching, pipeline-based task executionVercel
BazelMulti-language, hermetic builds, remote executionGoogle, Uber, Airbnb
Lernanpm package management, versioningOpen source community
RushLarge-scale monorepo management from MicrosoftMicrosoft
MoonRust-based, fast task executionCommunity

Monorepo plus Trunk-Based Development

The hard parts:

  • Affected analysis: automatically determining which services a change touches
  • Selective build and test: building and testing only the services that changed
  • CODEOWNERS: assigning reviewers automatically by directory ownership
bash
# Nx: test only the affected projects
npx nx affected --target=test
 
# Turborepo: reuse cached results
turbo run build --filter=auth-service...

nx affected picks out only the projects that depend on the changed files and tests those. In Turborepo, the trailing ... in --filter=auth-service... means the service and its dependent packages are built together.

Merge queue

The problem: the rebase race

When several PRs are approved at the same time, this happens:

text
PR #1: approved → merge attempt → success
PR #2: approved → main has changed → rebase needed → re-test → merge attempt
PR #3: approved → main has changed again → rebase needed → re-test...

What a merge queue does

text
PR #1 ─┐
PR #2 ─┤──→ [Merge Queue] ──→ auto rebase → test → merge
PR #3 ─┘         │
                 main always stays green
  1. An approved PR enters the merge queue
  2. The queue rebases it automatically
  3. Tests run on a temporary merge branch
  4. On success it merges automatically; on failure it is dropped from the queue

Main implementations

ToolCharacteristics
GitHub Merge Queue (native)Integrated with GitHub Actions
MergifySaaS, advanced rule engine
Trunk MergeIntegrated with the trunk.io ecosystem
Aviator MergeQueueOptimized for monorepos
BorsOpen source, started in the Rust community

Stacked PRs

A workflow that splits a large feature into a chain of small PRs so review and merging can run in parallel. It started as a Meta internal tool and Graphite brought it to a wider audience.

The old way versus stacked PRs

text
[Traditional: one big PR]
feature/big-change ──────────────────────→ main
(1000 lines, hard to review, takes forever)
 
[Stacked PRs: a chain of small PRs]
stack/part-1 (DB schema) ──→ main
    └── stack/part-2 (API) ──→ stacked on top of stack/part-1
         └── stack/part-3 (UI) ──→ stacked on top of stack/part-2

Benefits

  • Review efficiency: a small PR gets a focused, fast review
  • Parallel development: part-2 and part-3 can be built while part-1 is in review
  • Fewer merge conflicts: small changes integrate often
  • Easy rollback: when something breaks, only one step has to be reverted

Main tools

ToolCharacteristics
GraphitePurpose-built for stacked PRs, CLI plus web dashboard
ghstack (Meta)Stacked PRs on GitHub
git-branchlessMercurial-like UX, stack management
AviatorStacked PRs plus merge queue in one
bash
# Graphite usage example
gt branch create part-1
# after making changes
gt commit create -m "feat: add db schema"
 
gt branch create part-2  # automatically stacked on top of part-1
# after making changes
gt commit create -m "feat: add API endpoints"
 
gt stack submit  # submit the whole stack as PRs

gt branch create part-2 automatically stacks on the previous branch (part-1), and gt stack submit pushes the whole stack up as individual PRs in one go.

Branch protection rules

GitHub branch protection settings

RuleDescription
Require pull request reviewsPR review required (at least N reviewers)
Require status checks to passCI checks must pass
Require branches to be up to dateMust be current before merging
Require signed commitsGnuPG (GPG) or SSH signature required
Require linear historyNo merge commits (rebase or squash only)
Include administratorsRules apply to admins too
Restrict pushesDirect pushes limited
Allow auto-mergeMerges automatically once conditions are met
yaml
# Example status check in GitHub Actions
name: CI
on:
  pull_request:
    branches: [main]
 
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm test
      - run: npm run lint

Choosing a branching strategy

CriterionGit FlowTrunk-BasedGitHub FlowRelease Flow
Team sizeLargeAny sizeSmall to mid-sizeLarge
Release cadencePeriodic (2-4 weeks)ContinuousContinuousPeriodic
Deployment automationOptionalRequiredRecommendedRecommended
Maintaining parallel versionsPossibleDifficultDifficultPossible
ComplexityHighLowVery lowMedium
Dependence on CI/CDLowHighMediumMedium
Feature flagsOptionalRequiredOptionalOptional
Fit for monoreposLowHighMediumHigh

Decision flowchart

Summary

There is no absolute right answer in branching, and the choice is driven by team size, release cadence and CI/CD maturity. Periodic releases or several versions maintained in parallel point to Git Flow or Release Flow. Solid automation and frequent deployment make Trunk-Based Development cheaper in merge conflicts and integration effort. A small or mid-size team that wants simplicity is well served by GitHub Flow.

Whichever strategy you pick, merge queues, stacked PRs and branch protection rules are what hold integration quality up. The next post continues with code review and collaboration conventions.