Introduction
A branching strategy defines how your team organizes code changes, collaborates, and releases software. The right strategy depends on your team size, release frequency, and project complexity.
This article compares the three most popular strategies and helps you choose the right one.
The Three Main Strategies
flowchart TB
subgraph TrunkBased["Trunk-Based"]
T1["main"] --> T2["Short-lived branches"]
T2 --> T1
end
subgraph GitHubFlow["GitHub Flow"]
G1["main"] --> G2["feature branch"]
G2 --> G3["PR + Review"]
G3 --> G1
end
subgraph GitFlow["Git Flow"]
F1["main"] --> F2["develop"]
F2 --> F3["feature"]
F3 --> F2
F2 --> F4["release"]
F4 --> F1
end
style TrunkBased fill:#22c55e,color:#fff
style GitHubFlow fill:#3b82f6,color:#fff
style GitFlow fill:#8b5cf6,color:#fff
GitHub Flow
Overview
GitHub Flow is a simple, lightweight workflow perfect for continuous deployment:
gitGraph
commit id: "Initial"
branch feature-login
commit id: "Add form"
commit id: "Add validation"
checkout main
merge feature-login id: "Merge PR"
commit id: "Deploy"
The Process
- Create a branch from
main - Make changes with descriptive commits
- Open a Pull Request for discussion
- Review and discuss the code
- Deploy from the branch to test (optional)
- Merge to
mainafter approval - Deploy to production
When to Use GitHub Flow
| Good For | Not Ideal For |
|---|---|
| Continuous deployment | Scheduled releases |
| Small to medium teams | Multiple versions in production |
| Web applications | Mobile apps with store review |
| SaaS products | Regulatory environments |
Implementation
# Branch naming convention
feature/user-authentication
fix/login-validation-bug
docs/api-endpoints
refactor/database-queries
# .github/workflows/github-flow.yml
name: GitHub Flow CI/CD
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm test
deploy:
if: github.ref == 'refs/heads/main'
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: ./deploy.sh
Git Flow
Overview
Git Flow is a more structured workflow for scheduled releases:
gitGraph
commit id: "v1.0.0" tag: "v1.0.0"
branch develop
commit id: "Start dev"
branch feature/login
commit id: "Add login"
checkout develop
merge feature/login
branch release/1.1.0
commit id: "Bump version"
checkout main
merge release/1.1.0 id: "v1.1.0" tag: "v1.1.0"
checkout develop
merge release/1.1.0
Branch Types
| Branch | Purpose | Lifetime |
|---|---|---|
main |
Production-ready code | Permanent |
develop |
Integration branch | Permanent |
feature/* |
New features | Until merged |
release/* |
Release preparation | Until released |
hotfix/* |
Production fixes | Until merged |
The Process
- Develop on
feature/branches fromdevelop - Merge features into
developvia PR - Create release branch from
developwhen ready - Finalize release (version bump, changelog)
- Merge release to both
mainanddevelop - Tag the release on
main - Hotfix from
mainif needed, merge back to both
When to Use Git Flow
| Good For | Not Ideal For |
|---|---|
| Scheduled releases | Continuous deployment |
| Multiple versions | Fast iteration |
| Large teams | Small teams |
| Open source with versioning | SaaS products |
Implementation
# Initialize Git Flow
git flow init
# Start a feature
git flow feature start user-authentication
# Finish a feature
git flow feature finish user-authentication
# Start a release
git flow release start 1.2.0
# Finish a release
git flow release finish 1.2.0
# Hotfix
git flow hotfix start 1.2.1
git flow hotfix finish 1.2.1
Workflow Configuration
# .github/workflows/git-flow.yml
name: Git Flow CI
on:
push:
branches: [main, develop, 'release/**', 'hotfix/**']
pull_request:
branches: [develop]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm test
release:
if: startsWith(github.ref, 'refs/heads/release/')
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm run build
- run: ./deploy-staging.sh
production:
if: github.ref == 'refs/heads/main'
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm run build
- run: ./deploy-production.sh
Trunk-Based Development
Overview
Trunk-Based Development emphasizes small, frequent commits directly to the main branch:
gitGraph
commit id: "Feature A"
commit id: "Feature B"
branch short-lived
commit id: "Quick fix"
checkout main
merge short-lived
commit id: "Feature C"
commit id: "Feature D"
Key Principles
- Short-lived branches (< 1 day ideally)
- Feature flags for incomplete features
- Continuous integration with fast feedback
- Small, frequent commits to main
When to Use Trunk-Based
| Good For | Not Ideal For |
|---|---|
| High-performing teams | Inexperienced teams |
| Continuous deployment | Regulated releases |
| Strong CI/CD | Weak test coverage |
| Experienced developers | Junior-heavy teams |
Feature Flags
Essential for trunk-based development:
// Feature flag implementation
const features = {
newCheckout: process.env.FEATURE_NEW_CHECKOUT === 'true',
darkMode: process.env.FEATURE_DARK_MODE === 'true',
};
function CheckoutPage() {
if (features.newCheckout) {
return <NewCheckout />;
}
return <LegacyCheckout />;
}
Implementation
# .github/workflows/trunk-based.yml
name: Trunk-Based CI
on:
push:
branches: [main]
jobs:
# Fast feedback is critical
quick-check:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- run: npm run lint
- run: npm run test:unit
# Deploy continuously
deploy:
needs: quick-check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm run build
- run: ./deploy.sh
Comparison Matrix
| Aspect | GitHub Flow | Git Flow | Trunk-Based |
|---|---|---|---|
| Complexity | Low | High | Medium |
| Release cadence | Continuous | Scheduled | Continuous |
| Main branch | Always deployable | Production | Always deployable |
| Feature branches | Yes | Yes | Minimal |
| Release branches | No | Yes | No |
| Hotfix process | Fix β main | hotfix β main + develop | Fix β main |
| Best for | Web apps | Versioned software | High-velocity teams |
Choosing the Right Strategy
Decision Flowchart
flowchart TD
A["Start"] --> B{"Deploy continuously?"}
B -->|Yes| C{"Team experienced?"}
B -->|No| D["Git Flow"]
C -->|Yes| E{"Strong CI/CD?"}
C -->|No| F["GitHub Flow"]
E -->|Yes| G["Trunk-Based"]
E -->|No| F
style D fill:#8b5cf6,color:#fff
style F fill:#3b82f6,color:#fff
style G fill:#22c55e,color:#fff
By Team Size
| Team Size | Recommended | Reason |
|---|---|---|
| 1-3 developers | Trunk-Based or GitHub Flow | Low overhead |
| 4-10 developers | GitHub Flow | Balance of structure and speed |
| 10+ developers | Git Flow or GitHub Flow | More coordination needed |
By Release Type
| Release Type | Recommended | Reason |
|---|---|---|
| Continuous (SaaS) | GitHub Flow or Trunk-Based | Fast iteration |
| Scheduled (monthly) | Git Flow | Release preparation time |
| Multiple versions | Git Flow | Version branch support |
| Mobile apps | Git Flow | App store review time |
Branch Naming Conventions
Consistent Naming
# Feature branches
feature/TICKET-123-user-authentication
feature/add-payment-gateway
# Bug fixes
fix/TICKET-456-login-error
bugfix/null-pointer-exception
# Releases (Git Flow)
release/1.2.0
release/2.0.0-beta
# Hotfixes
hotfix/1.2.1
hotfix/security-patch
# Other
docs/api-documentation
refactor/database-layer
test/integration-tests
Automation with Branch Rules
# .github/workflows/branch-naming.yml
name: Branch Naming Check
on:
pull_request:
jobs:
check-branch-name:
runs-on: ubuntu-latest
steps:
- name: Check branch name
run: |
BRANCH="${{ github.head_ref }}"
PATTERN="^(feature|fix|bugfix|hotfix|release|docs|refactor|test)/.+"
if [[ ! $BRANCH =~ $PATTERN ]]; then
echo "Branch name '$BRANCH' doesn't match pattern"
exit 1
fi
Best Practices
1. Keep Branches Short-Lived
flowchart LR
subgraph Good["Good: Short-lived"]
A["1-3 days"]
end
subgraph Bad["Bad: Long-lived"]
B["Weeks or months"]
end
Good --> C["Easy merge"]
Bad --> D["Merge conflicts"]
style Good fill:#22c55e,color:#fff
style Bad fill:#ef4444,color:#fff
2. Protect Your Main Branch
# Branch protection rules
- Require pull request reviews
- Require status checks to pass
- Require linear history (optional)
- Restrict who can push
- Require signed commits (optional)
3. Automate Everything
- Linting on PR
- Tests on PR
- Deploy previews
- Automatic merging when approved
4. Document Your Strategy
Create a CONTRIBUTING.md:
## Branching Strategy
We use GitHub Flow:
1. Create a feature branch from `main`
2. Make your changes
3. Open a PR for review
4. Merge after approval
5. Changes deploy automatically
### Branch Naming
- `feature/description` - New features
- `fix/description` - Bug fixes
- `docs/description` - Documentation
Summary
| Strategy | Best For | Avoid If |
|---|---|---|
| GitHub Flow | Web apps, SaaS, continuous deployment | You need versioned releases |
| Git Flow | Versioned software, scheduled releases | You deploy continuously |
| Trunk-Based | Experienced teams, fast iteration | Team lacks experience or CI/CD |
Choose a strategy that matches your team's needs and iterate as you grow. The best strategy is one your team follows consistently.
References
- O'Reilly - Version Control with Git, Chapter 18
- Packt - DevOps Unleashed with Git and GitHub, Chapter 3
- Atlassian - Git Workflows
- trunk-baseddevelopment.com