branch-strategy
Branch Strategy
Branch Naming Conventions
Every branch name must follow a structured prefix convention. This keeps the repository navigable, enables CI/CD automation, and makes intent immediately clear.
Required Prefixes
| Prefix | Purpose | Example |
|---|---|---|
feature/ |
New functionality or capability | feature/user-avatar-upload |
fix/ |
Bug fixes for existing behavior | fix/login-redirect-loop |
hotfix/ |
Urgent production fixes | hotfix/payment-null-pointer |
release/ |
Release preparation and stabilization | release/2.4.0 |
chore/ |
Maintenance, dependencies, tooling | chore/upgrade-eslint-9 |
docs/ |
Documentation-only changes | docs/api-authentication-guide |
refactor/ |
Code restructuring without behavior change | refactor/extract-billing-service |
test/ |
Adding or fixing tests only | test/payment-edge-cases |
experiment/ |
Exploratory work, not intended for merge | experiment/graphql-federation |
Naming Rules
- Use lowercase with hyphens as separators:
feature/add-user-searchnotfeature/Add_User_Search - Keep names concise but descriptive:
fix/cart-totalnotfix/the-bug-where-cart-total-shows-wrong-amount - Include a ticket or issue number when one exists:
feature/PROJ-1234-user-avatar-upload - Never use personal names:
feature/search-apinotfeature/anthonys-search-work - Avoid generic names:
fix/login-redirect-loopnotfix/bugorfix/stuff - Maximum length: aim for under 50 characters after the prefix
Ticket Number Placement
When integrating with issue trackers, place the ticket number immediately after the prefix:
feature/PROJ-1234-user-avatar-upload
fix/GH-567-null-pointer-on-empty-cart
hotfix/INC-89-payment-gateway-timeout
This enables automated linking between branches, PRs, and issues.
Branching Models
Trunk-Based Development
The simplest model. All developers work on short-lived branches off main and merge back frequently.
main ─────●─────●─────●─────●─────●─────●─────
\ / \ / \ /
●─● ●─● ●
(feature) (fix) (feature)
When to Use Trunk-Based Development:
- Teams with strong CI/CD pipelines and automated testing
- Continuous deployment environments
- Small to medium teams (2-15 developers)
- Products that ship continuously (SaaS, web applications)
- Teams practicing feature flags for incomplete work
Rules:
- Branches live no longer than 1-2 days
- Every commit to
mainmust pass all tests - Use feature flags to hide incomplete functionality
- No long-lived branches except
main - Deploy from
mainon every merge (or at minimum daily) - Keep changes small and incremental
Branch Lifecycle:
# Create branch from main
git checkout main && git pull
git checkout -b feature/PROJ-123-add-search
# Work in small increments, commit frequently
git add -p && git commit -m "Add search index configuration"
git add -p && git commit -m "Implement basic search query endpoint"
# Rebase onto main before merging
git fetch origin && git rebase origin/main
# Merge via PR (squash or merge commit per team convention)
# Delete branch immediately after merge
Git Flow
A structured model with multiple long-lived branches for teams that need formal release management.
main ─────●──────────────────●──────────────
| |
develop ─────●────●────●────●───●────●─────────
\ / \ / | /
feature ●● ●● release ●
\ /
●─●
When to Use Git Flow:
- Products with scheduled releases (mobile apps, installed software)
- Teams that maintain multiple versions simultaneously
- Regulated industries requiring release audit trails
- Larger teams (15+ developers) needing coordination
- Projects with dedicated QA phases
Branch Structure:
| Branch | Lifetime | Merges Into | Purpose |
|---|---|---|---|
main |
Permanent | -- | Production-ready code, tagged releases |
develop |
Permanent | main (via release) |
Integration branch for features |
feature/* |
Temporary | develop |
New work in progress |
release/* |
Temporary | main and develop |
Release stabilization |
hotfix/* |
Temporary | main and develop |
Urgent production fixes |
Release Workflow:
# 1. Create release branch from develop
git checkout develop && git pull
git checkout -b release/2.4.0
# 2. Only bug fixes, documentation, and release prep on this branch
# No new features allowed
# 3. When stable, merge to main and tag
git checkout main && git merge --no-ff release/2.4.0
git tag -a v2.4.0 -m "Release 2.4.0"
# 4. Back-merge to develop
git checkout develop && git merge --no-ff release/2.4.0
# 5. Delete release branch
git branch -d release/2.4.0
Hotfix Workflow:
# 1. Branch from main
git checkout main && git pull
git checkout -b hotfix/payment-null-pointer
# 2. Fix the issue, commit
# 3. Merge to both main and develop
git checkout main && git merge --no-ff hotfix/payment-null-pointer
git tag -a v2.4.1 -m "Hotfix: payment null pointer"
git checkout develop && git merge --no-ff hotfix/payment-null-pointer
# 4. Delete hotfix branch
git branch -d hotfix/payment-null-pointer
GitHub Flow
A simplified model that sits between trunk-based and Git Flow.
When to Use GitHub Flow:
- Web applications with continuous deployment
- Open source projects
- Teams that want simplicity but still use pull requests
- When Git Flow feels too heavy but you want PR-based review
Rules:
mainis always deployable- Branch from
mainfor any work - Open a PR when you want feedback or are ready to merge
- Merge to
mainafter review and CI passes - Deploy immediately after merge
Branch Protection Rules
Recommended Protection for main
branch_protection:
main:
required_reviews: 1 # At least one approval
dismiss_stale_reviews: true # Re-review after new pushes
require_status_checks: true # CI must pass
required_checks:
- build
- test
- lint
restrict_pushes: true # No direct pushes
require_linear_history: true # Squash or rebase only
include_administrators: true # Rules apply to everyone
Protection by Branch Type
| Rule | main |
develop |
release/* |
feature/* |
|---|---|---|---|---|
| Require PR | Yes | Yes | Yes | No |
| Required reviewers | 1-2 | 1 | 1-2 | 0 |
| Require CI pass | Yes | Yes | Yes | Optional |
| Allow force push | No | No | No | Yes (owner) |
| Allow deletion | No | No | After merge | Yes |
| Require signed commits | Recommended | Optional | Optional | No |
Release Tagging and Semantic Versioning
Semver Format
MAJOR.MINOR.PATCH
| | |
| | └── Bug fixes, no API changes
| └──────── New features, backward compatible
└────────────── Breaking changes
When to Bump Each Number
MAJOR (breaking): Removing an API endpoint. Changing a response format. Renaming a public function. Dropping support for a platform.
MINOR (feature): Adding a new endpoint. Adding optional parameters. New configuration options. New UI features.
PATCH (fix): Bug fixes. Security patches. Performance improvements. Documentation corrections.
Pre-release and Build Metadata
2.4.0-alpha.1 # Alpha pre-release
2.4.0-beta.2 # Beta pre-release
2.4.0-rc.1 # Release candidate
2.4.0+build.1234 # Build metadata (ignored in precedence)
Tagging Checklist
Before creating a release tag:
- All tests pass on the release branch or main
- CHANGELOG has been updated with all changes since last release
- Version numbers updated in package files (package.json, pyproject.toml, etc.)
- Migration scripts tested if applicable
- Release notes drafted with user-facing summary
- Breaking changes documented with migration guide
- Dependencies audited for known vulnerabilities
Creating Tags
# Annotated tag (preferred for releases)
git tag -a v2.4.0 -m "Release 2.4.0: Add user search, fix cart totals"
# Push tags to remote
git push origin v2.4.0
# Or push all tags
git push origin --tags
Tag Naming Convention
- Always prefix with
v:v2.4.0not2.4.0 - Match the version in your package files exactly
- Use annotated tags (not lightweight) for releases
Feature Branch Lifecycle
Standard Lifecycle
1. CREATE ── Branch from main/develop with proper prefix
2. DEVELOP ── Commit regularly, push daily
3. SYNC ── Rebase/merge from upstream regularly
4. REVIEW ── Open PR, request review
5. REVISE ── Address feedback, push updates
6. MERGE ── Squash or merge commit per convention
7. CLEANUP ── Delete branch locally and remotely
Keeping Branches Current
# Option A: Rebase (cleaner history, use for personal branches)
git fetch origin
git rebase origin/main
# Option B: Merge (preserves history, use for shared branches)
git fetch origin
git merge origin/main
Stale Branch Policy
- Branches with no commits for 14+ days should be reviewed
- Branches with no commits for 30+ days should be closed or archived
- Automate stale branch notifications via CI/CD
Decision Guide
Use this flowchart to choose your branching model:
Do you deploy continuously to production?
├── Yes: Do you need PR-based code review?
│ ├── Yes ──► GitHub Flow
│ └── No ──► Trunk-Based Development
└── No: Do you maintain multiple release versions?
├── Yes ──► Git Flow
└── No: Do you have scheduled release cycles?
├── Yes ──► Git Flow (simplified)
└── No ──► GitHub Flow
Anti-Patterns to Avoid
- Long-lived feature branches -- Branches open for weeks accumulate merge conflicts and drift from main. Break work into smaller increments.
- Merging main into feature branches repeatedly -- Creates a tangled history. Prefer rebasing for personal branches.
- Skipping branch protection -- Even solo developers benefit from CI checks on main.
- Inconsistent naming -- Mixed conventions make automation impossible. Enforce via CI hooks.
- Forgetting to delete merged branches -- Stale branches clutter the repository. Configure auto-delete on merge.
- Direct commits to main -- Bypasses review and CI. Always use PRs except for truly trivial changes on solo projects.
- Release branches without version bumps -- Every release branch must update version numbers as its first commit.
- Cherry-picking without tracking -- If you cherry-pick a fix to multiple branches, document which branches received the fix.