Trunk-Based Development vs Git Flow: Choosing the Right Branching Strategy for Teams in 2026
Posted on: 4/21/2026 1:15:08 PM
Table of contents
- What is Git Flow?
- What is Trunk-Based Development?
- GitHub Flow and GitLab Flow
- Detailed Comparison: Git Flow vs Trunk-Based Development
- Feature Flags — The Key to Trunk-Based Development
- DORA Metrics and Branching Strategy
- When to Choose Git Flow vs TBD?
- Migrating from Git Flow to Trunk-Based Development
- Best Practices for Trunk-Based Development
- Real-World Adoption at Major Companies
- Conclusion
Your branching strategy determines how fast your team can ship code, the quality of code reviews, and the ability to rollback safely. Choosing the wrong Git model can turn your CI/CD pipeline into a merge conflict graveyard, block releases for weeks, and force developers to spend time resolving conflicts instead of building features.
This article provides a deep analysis of the two most popular models — Git Flow and Trunk-Based Development (TBD) — along with GitHub Flow, GitLab Flow, and data from DORA Research 2025–2026 to help you make the right decision for your team.
What is Git Flow?
Git Flow was introduced by Vincent Driessen in 2010 and quickly became the most popular branching model. It clearly defines the role of each branch in the software development lifecycle.
gitGraph
commit id: "init"
branch develop
checkout develop
commit id: "setup"
branch feature/login
checkout feature/login
commit id: "login-ui"
commit id: "login-api"
checkout develop
merge feature/login id: "merge-login"
branch release/1.0
checkout release/1.0
commit id: "bump-version"
commit id: "fix-typo"
checkout main
merge release/1.0 id: "v1.0" tag: "v1.0"
checkout develop
merge release/1.0 id: "sync-develop"
checkout main
branch hotfix/1.0.1
commit id: "critical-fix"
checkout main
merge hotfix/1.0.1 id: "v1.0.1" tag: "v1.0.1"
checkout develop
merge hotfix/1.0.1 id: "sync-hotfix"
Figure 1: Git Flow model with 5 branch types — main, develop, feature/*, release/*, hotfix/*
Branch Structure in Git Flow
| Branch | Role | Lifetime |
|---|---|---|
main | Production code, each commit represents a release | Permanent |
develop | Integration branch, where all features merge | Permanent |
feature/* | New feature development | Days → weeks |
release/* | Release preparation, final bug fixes | A few days |
hotfix/* | Emergency production fixes | Hours → 1 day |
Advantages of Git Flow
- Clear structure: Each branch has a specific purpose, easy to understand for new team members.
- Parallel development: Multiple features developed simultaneously without affecting each other.
- Controlled releases: Release process has a dedicated preparation step, suitable for scheduled releases (mobile apps, enterprise software).
- Isolated hotfixes: Production fixes don't wait for in-progress features.
Disadvantages of Git Flow
- Merge hell: Long-lived feature branches (weeks → months) accumulate large divergence, merging back to develop becomes a nightmare.
- Slow feedback loop: Code review happens when the feature is "completely done" → massive PRs, overwhelmed reviewers.
- Release bottleneck: Release branches add ceremony — version bumps, cherry-pick fixes, merging back to develop — easy to miss steps.
- Poor CI/CD fit: The "deploy anytime" philosophy conflicts with long-running release branch processes.
⚠ A Confession from Git Flow's Creator
Vincent Driessen, the creator of Git Flow, added a note to his original blog post in 2020: "If your team is doing continuous delivery of software, I would suggest to adopt a much simpler workflow (like GitHub flow) instead of trying to shoehorn git-flow into your team." — Even the author recommends against Git Flow for continuous delivery.
What is Trunk-Based Development?
Trunk-Based Development (TBD) is a model where all developers commit directly to a single main branch (trunk/main), or use short-lived feature branches (lasting at most 1–2 days) that merge immediately. No develop branch, no long-running release branches.
gitGraph
commit id: "feature-a"
branch feat/search
checkout feat/search
commit id: "search-v1"
checkout main
merge feat/search id: "merge-search"
commit id: "feature-b"
commit id: "feature-c"
branch feat/filter
checkout feat/filter
commit id: "filter-ui"
checkout main
merge feat/filter id: "merge-filter" tag: "auto-deploy"
commit id: "feature-d"
commit id: "feature-e" tag: "auto-deploy"
Figure 2: Trunk-Based Development — short-lived branches merge quickly, main is always deployable
Two TBD Variants
flowchart LR
subgraph A["Direct Commits to Trunk"]
A1["Developer A commit"] --> T1["main"]
A2["Developer B commit"] --> T1
A3["Developer C commit"] --> T1
end
subgraph B["Short-lived Feature Branches"]
B1["Developer A"] --> FB1["feat/x
(lives < 2 days)"]
FB1 --> T2["main"]
B2["Developer B"] --> FB2["feat/y
(lives < 1 day)"]
FB2 --> T2
end
style T1 fill:#e94560,stroke:#fff,color:#fff
style T2 fill:#e94560,stroke:#fff,color:#fff
style FB1 fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style FB2 fill:#f8f9fa,stroke:#e94560,color:#2c3e50
Figure 3: Two variants — direct trunk commits (small teams) vs short-lived branches (larger teams needing code review)
Advantages of TBD
- Minimal merge conflicts: Short-lived branches = less divergence = fewer conflicts.
- Natural CI/CD: Main is always deployable, pipeline runs continuously on every commit.
- Fast code review: Small PRs (tens to hundreds of lines) reviewed in 15–30 minutes instead of hours.
- Fast feedback loop: Developers know about broken code in minutes rather than at the end of a sprint.
- DORA-proven: DORA research shows TBD is a common trait of Elite performing teams.
Disadvantages of TBD
- Requires strong automated testing: Without good tests, broken code affects the entire team immediately.
- Feature flags necessary: Incomplete features must be hidden behind flags, adding management complexity.
- Demands discipline: Developers must commit frequently, break tasks into small pieces, and avoid hoarding code for days.
- Harder for junior-heavy teams: Requires understanding of CI/CD, testing, and feature flags.
GitHub Flow and GitLab Flow
Beyond Git Flow and TBD, there are two popular intermediate models:
flowchart TB
subgraph GHF["GitHub Flow"]
direction LR
M1["main
(always deployable)"]
F1["feature branch"] -->|PR + Review| M1
M1 -->|Deploy immediately| P1["Production"]
end
subgraph GLF["GitLab Flow"]
direction LR
M2["main"]
F2["feature branch"] -->|MR + Review| M2
M2 --> E1["pre-production"]
E1 --> E2["production"]
end
style M1 fill:#e94560,stroke:#fff,color:#fff
style M2 fill:#e94560,stroke:#fff,color:#fff
style P1 fill:#4CAF50,stroke:#fff,color:#fff
style E2 fill:#4CAF50,stroke:#fff,color:#fff
style E1 fill:#ff9800,stroke:#fff,color:#fff
Figure 4: GitHub Flow (simplest) vs GitLab Flow (adds environment branches)
| Model | Branch Types | Complexity | Best For |
|---|---|---|---|
| Git Flow | 5 (main, develop, feature, release, hotfix) | High | Scheduled releases, mobile apps, versioned software |
| GitHub Flow | 2 (main, feature) | Low | Web apps, SaaS, continuous deployment |
| GitLab Flow | 3–4 (main, feature, environment branches) | Medium | Needs staging/pre-prod environments |
| Trunk-Based | 1–2 (main, optional short-lived features) | Lowest | Mature teams, strong CI/CD, deploy on-demand |
Detailed Comparison: Git Flow vs Trunk-Based Development
| Criteria | Git Flow | Trunk-Based Development |
|---|---|---|
| Branch lifetime | Feature branches: weeks → months | Short-lived: hours → max 2 days |
| Merge frequency | When feature is "done" | At least once per day |
| PR size | Hundreds → thousands of lines | Tens → hundreds of lines |
| Merge conflicts | Frequent and complex | Rare and easy to resolve |
| Main branch state | Only contains released code | Always deployable |
| Release process | Release branch → QA → merge to main | Deploy from main anytime |
| Hotfix | Separate hotfix branch, merge to both main + develop | Fix directly on main, deploy immediately |
| Incomplete features | Live on feature branch, not merged | Merged to main, hidden behind feature flags |
| CI/CD fit | CI runs on develop, CD depends on release branch | CI/CD runs continuously on main |
| DORA performance | Medium → Low performers | High → Elite performers |
Feature Flags — The Key to Trunk-Based Development
Feature flags (or feature toggles) allow toggling features on/off without redeployment. They are an essential component that makes TBD safe — developers can merge incomplete code to main without affecting users.
flowchart LR
DEV["Developer merges
incomplete feature"] --> MAIN["main branch"]
MAIN --> DEPLOY["Deploy to Production"]
DEPLOY --> FF{"Feature Flag
ON / OFF?"}
FF -->|ON for internal| BETA["Beta Users
see new feature"]
FF -->|OFF for public| PROD["Public Users
see nothing"]
FF -->|Gradual rollout| CANARY["5% → 25% → 100%"]
style MAIN fill:#e94560,stroke:#fff,color:#fff
style FF fill:#ff9800,stroke:#fff,color:#fff
style BETA fill:#4CAF50,stroke:#fff,color:#fff
style PROD fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style CANARY fill:#2196F3,stroke:#fff,color:#fff
Figure 5: Feature flags decouple deployment from release — deploy anytime, release when ready
Types of Feature Flags
| Type | Purpose | Lifetime | Example |
|---|---|---|---|
| Release Flag | Hide incomplete features | Short (remove after release) | NEW_CHECKOUT_FLOW |
| Experiment Flag | A/B testing | Medium | PRICING_PAGE_V2 |
| Ops Flag | Circuit breaker, kill switch | Long-term | ENABLE_ELASTICSEARCH |
| Permission Flag | Entitlement, premium features | Permanent | ADVANCED_ANALYTICS |
✓ Golden Rule: Clean Up Feature Flags
Release flags are "planned technical debt" — after a feature is stable and rolled out to 100%, the flag and old code path must be removed. Teams should set expiration dates for each flag (e.g., 30 days after 100% rollout). Tools like LaunchDarkly, Flagsmith, or OpenFeature all support flag lifecycle management.
DORA Metrics and Branching Strategy
DORA (DevOps Research and Assessment) research from Google provides real data on the relationship between branching strategy and delivery performance. Since 2025, the DORA framework has expanded to include a 5th metric — Rework Rate — and transitioned from 4 tiers (Elite/High/Medium/Low) to 7 team archetypes based on both delivery performance and human factors.
flowchart TB
subgraph DORA["5 DORA Metrics (2025+)"]
DF["Deployment
Frequency"]
LT["Lead Time
for Changes"]
CFR["Change Failure
Rate"]
MTTR["Mean Time
to Recovery"]
RR["Rework Rate
(new in 2025)"]
end
subgraph ELITE["Elite Performers"]
E1["Deploy: multiple times/day"]
E2["Lead time: < 1 hour"]
E3["Failure rate: 0-5%"]
E4["Recovery: < 1 hour"]
E5["Rework: < 10%"]
end
TBD["Trunk-Based
Development"] -->|"enables"| ELITE
style TBD fill:#e94560,stroke:#fff,color:#fff
style DF fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style LT fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style CFR fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style MTTR fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style RR fill:#f8f9fa,stroke:#e94560,color:#2c3e50
Figure 6: 5 DORA Metrics and Elite Performer benchmarks — TBD is the foundation for high performance
📊 Data from DORA Research
Moving from Medium to High performance requires adopting trunk-based development or short-lived feature branches. Elite teams share common traits: no long-lived feature branches, automated tests on every commit, small batch sizes, and a deployment pipeline fast and reliable enough to feel routine.
When to Choose Git Flow vs TBD?
flowchart TD
START["Your Team"] --> Q1{"Deploy frequency?"}
Q1 -->|"Daily /
multiple times/day"| TBD_REC["→ Trunk-Based Development"]
Q1 -->|"Weekly /
monthly"| Q2{"Need to maintain
multiple versions?"}
Q2 -->|Yes| GF_REC["→ Git Flow"]
Q2 -->|No| Q3{"Team size?"}
Q3 -->|"≤ 5 people"| GHF_REC["→ GitHub Flow"]
Q3 -->|"> 5 people"| Q4{"Staging
environment?"}
Q4 -->|Yes| GLF_REC["→ GitLab Flow"]
Q4 -->|No| TBD_REC2["→ Trunk-Based
+ Feature Flags"]
style TBD_REC fill:#4CAF50,stroke:#fff,color:#fff
style TBD_REC2 fill:#4CAF50,stroke:#fff,color:#fff
style GF_REC fill:#2196F3,stroke:#fff,color:#fff
style GHF_REC fill:#ff9800,stroke:#fff,color:#fff
style GLF_REC fill:#9C27B0,stroke:#fff,color:#fff
style START fill:#e94560,stroke:#fff,color:#fff
Figure 7: Decision tree for choosing a branching strategy based on team and product characteristics
| Scenario | Recommendation | Reason |
|---|---|---|
| SaaS web app, continuous deployment | Trunk-Based Development | Main is always deployable, fast feedback |
| Mobile app (iOS/Android), App Store releases | Git Flow or GitLab Flow | Need release branches for each version, hotfixes for older versions |
| Open-source library, multiple versions | Git Flow | Maintain multiple release lines (v1.x, v2.x) |
| Small startup, minimal process | GitHub Flow | Simplest, least overhead |
| Enterprise, compliance requirements | GitLab Flow + Environment branches | Clear audit trail through environment promotion |
| Large team (>20 devs), monorepo | Trunk-Based + Feature Flags | Avoid branch explosion and merge conflicts |
Migrating from Git Flow to Trunk-Based Development
If your team currently uses Git Flow and wants to migrate to TBD, here's a phased roadmap instead of a "big bang" switch:
⚠ Don't Skip Phase 2
Trunk-Based Development without strong automated testing will cause chaos. When all developers commit to the same branch, broken code affects the entire team immediately. A test suite is a prerequisite, not a nice-to-have.
Best Practices for Trunk-Based Development
1. Small Batch Size
Each PR should change fewer than 400 lines of code. Research from Google shows that PRs over 400 lines have significantly reduced review quality — reviewers start skimming instead of reading carefully. Break large features into multiple sequential PRs, each self-contained and deployable.
2. Pre-commit Checks
# .husky/pre-commit or CI pipeline
npm run lint
npm run type-check
npm run test:unit -- --changed
Run lint, type-check, and unit tests on changed code before pushing. Reduces CI feedback time from minutes to seconds.
3. Branch Protection Rules
# GitHub branch protection for main
- Require pull request reviews (≥ 1 approval)
- Require status checks to pass (CI pipeline)
- Require branches to be up to date
- Do NOT require linear history (allow merge commits)
4. Commit Message Convention
# Conventional Commits
feat(auth): add OAuth2 login flow
fix(cart): resolve race condition in quantity update
refactor(api): extract validation middleware
chore(deps): bump express to 4.21.0
Conventional Commits help auto-generate changelogs, semantic versioning, and make git logs easy to read.
5. Ship/Show/Ask Framework
| Type | Action | When to Use |
|---|---|---|
| Ship | Merge directly, no review needed | Fix typos, update docs, bump dependencies |
| Show | Merge first, then open PR for team visibility | Small refactors, obvious changes, confident about correctness |
| Ask | Open PR, wait for review before merging | New features, architecture changes, complex code |
Real-World Adoption at Major Companies
Conclusion
No branching strategy is "correct" for every team. Git Flow still makes sense for software needing clear versioning and scheduled releases (mobile apps, SDKs, enterprise). But for web applications, SaaS, and any team wanting to deploy frequently, Trunk-Based Development combined with feature flags is the choice proven effective by DORA research.
The most important factor isn't which model you choose, but whether your team has strong enough automated testing to confidently merge code frequently, and a culture of trust where everyone commits small, reviews fast, and ships continuously.
References:
DuckDB 2026: Embedded OLAP Database — Data Analytics Without a Server
Idempotency Pattern — Designing Duplicate-Proof APIs for Distributed Systems
Disclaimer: The opinions expressed in this blog are solely my own and do not reflect the views or opinions of my employer or any affiliated organizations. The content provided is for informational and educational purposes only and should not be taken as professional advice. While I strive to provide accurate and up-to-date information, I make no warranties or guarantees about the completeness, reliability, or accuracy of the content. Readers are encouraged to verify the information and seek independent advice as needed. I disclaim any liability for decisions or actions taken based on the content of this blog.