Git Workflow Strategies: Trunk-Based vs GitFlow in 2025

Trunk-based development vs GitFlow: which git workflow fits your team? Learn the tradeoffs, CI/CD requirements, feature flag integration, and practical...

Y
Yash Pritwani
12 min read

The Git Workflow War

The debate between trunk-based development and GitFlow has been going on for years. In 2025, the industry has largely settled on trunk-based development for high-performing teams, but GitFlow still has valid use cases.

<div style="margin:2.5rem auto;max-width:600px;width:100%;text-align:center;"><svg viewBox="0 0 600 180" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;"><rect width="600" height="180" rx="12" fill="#1a1a2e"/><rect x="30" y="55" width="90" height="50" rx="8" fill="#6366f1" opacity="0.9"/><text x="75" y="85" text-anchor="middle" fill="#ffffff" font-size="12" font-family="system-ui">Code</text><rect x="150" y="55" width="90" height="50" rx="8" fill="#3b82f6" opacity="0.9"/><text x="195" y="85" text-anchor="middle" fill="#ffffff" font-size="12" font-family="system-ui">Build</text><rect x="270" y="55" width="90" height="50" rx="8" fill="#a855f7" opacity="0.9"/><text x="315" y="85" text-anchor="middle" fill="#ffffff" font-size="12" font-family="system-ui">Test</text><rect x="390" y="55" width="90" height="50" rx="8" fill="#2dd4bf" opacity="0.9"/><text x="435" y="85" text-anchor="middle" fill="#1a1a2e" font-size="12" font-family="system-ui">Deploy</text><rect x="510" y="55" width="60" height="50" rx="8" fill="#f59e0b" opacity="0.9"/><text x="540" y="85" text-anchor="middle" fill="#1a1a2e" font-size="12" font-family="system-ui">Live</text><path d="M122,80 L148,80" stroke="#e2e8f0" stroke-width="2" marker-end="url(#arrow1)"/><path d="M242,80 L268,80" stroke="#e2e8f0" stroke-width="2" marker-end="url(#arrow1)"/><path d="M362,80 L388,80" stroke="#e2e8f0" stroke-width="2" marker-end="url(#arrow1)"/><path d="M482,80 L508,80" stroke="#e2e8f0" stroke-width="2" marker-end="url(#arrow1)"/><defs><marker id="arrow1" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6" fill="#e2e8f0"/></marker></defs><text x="300" y="145" text-anchor="middle" fill="#94a3b8" font-size="11" font-family="system-ui">Continuous Integration / Continuous Deployment Pipeline</text></svg><p style="margin-top:0.75rem;font-size:0.85rem;color:#94a3b8;font-style:italic;line-height:1.4;">A typical CI/CD pipeline: code flows through build, test, and deploy stages automatically.</p></div>

GitFlow: The Feature Branch Model

GitFlow uses long-lived branches for features, releases, and hotfixes:

main ──────────────────────────────────────────────────────────
  \                    /          \              /
   develop ──────────────────────────────────────
     \         /   \         /
      feature/A     feature/B
# Start a feature
git checkout develop
git checkout -b feature/user-dashboard

# Work on feature (days/weeks)
git commit -m "Add dashboard layout"
git commit -m "Add chart components"
git commit -m "Add API integration"

# Merge back to develop
git checkout develop
git merge --no-ff feature/user-dashboard
git branch -d feature/user-dashboard

# Create a release
git checkout develop
git checkout -b release/2.1.0
# Bug fixes only on release branch
git commit -m "Fix dashboard loading state"

# Merge release to main and develop
git checkout main
git merge --no-ff release/2.1.0
git tag v2.1.0
git checkout develop
git merge --no-ff release/2.1.0

GitFlow advantages:

Clear separation between in-progress and released code
Multiple versions can be maintained simultaneously
Release candidates can be stabilized in isolation
Well-suited for software with versioned releases (mobile apps, libraries, on-premise)

GitFlow problems:

Merge conflicts accumulate on long-lived branches
Integration is delayed (find bugs late)
Release branches add process overhead
Branch management becomes complex with many features
Slows down deployment frequency

Trunk-Based Development: The Fast Lane

Trunk-based development has everyone committing to a single branch (main/trunk) with short-lived feature branches (max 1-2 days):

main ──●──●──●──●──●──●──●──●──●──●──●──●──────
       |     |  |        |  |        |
       └─●──●┘  └──●──●──┘  └──●────┘
       (hours)    (1 day)    (hours)
# Start work (branch lives < 24 hours)
git checkout main
git pull
git checkout -b yash/add-search-filter

# Small, focused commits
git commit -m "Add search input component"
git commit -m "Add filter logic to product grid"

# Push and create PR
git push -u origin yash/add-search-filter
# Create PR, get review, merge same day

# On merge, CI/CD deploys to production automatically

Trunk-based advantages:

Continuous integration (find bugs immediately)
Smaller PRs (easier to review, faster to merge)
No merge hell (conflicts are small and frequent)
Deploy multiple times per day
Forces modular, decoupled code

Trunk-based requirements:

Strong CI/CD pipeline (automated tests must be fast)
Feature flags (to deploy unfinished code safely)
Code review culture (small PRs reviewed quickly)
Good test coverage (confidence to merge to main)

Feature Flags Enable Trunk-Based

The key enabler for trunk-based development is feature flags. You can merge incomplete features to main without exposing them to users:

// Merge to main with flag off
if (featureFlags.isEnabled('new-checkout', user)) {
  return <NewCheckoutFlow />;
} else {
  return <LegacyCheckoutFlow />;
}

Deployment flow: 1. Merge code to main (flag off) 2. Deploy to production 3. Enable flag for internal team 4. Enable flag for 10% of users 5. Enable flag for 100% of users 6. Remove flag and dead code

<div style="margin:2.5rem auto;max-width:600px;width:100%;text-align:center;"><svg viewBox="0 0 600 170" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;"><rect width="600" height="170" rx="12" fill="#1a1a2e"/><circle cx="60" cy="85" r="25" fill="#f59e0b" opacity="0.85"/><text x="60" y="82" text-anchor="middle" fill="#1a1a2e" font-size="9" font-family="system-ui" font-weight="bold">Trigger</text><text x="60" y="94" text-anchor="middle" fill="#1a1a2e" font-size="8" font-family="system-ui">webhook</text><polygon points="175,55 210,85 175,115 140,85" fill="#6366f1" opacity="0.85"/><text x="175" y="88" text-anchor="middle" fill="#ffffff" font-size="9" font-family="system-ui">If</text><rect x="250" y="35" width="100" height="40" rx="6" fill="#2dd4bf" opacity="0.85"/><text x="300" y="55" text-anchor="middle" fill="#1a1a2e" font-size="10" font-family="system-ui">Send Email</text><text x="300" y="67" text-anchor="middle" fill="#1a1a2e" font-size="8" font-family="system-ui">SMTP</text><rect x="250" y="95" width="100" height="40" rx="6" fill="#a855f7" opacity="0.85"/><text x="300" y="115" text-anchor="middle" fill="#ffffff" font-size="10" font-family="system-ui">Log Event</text><text x="300" y="127" text-anchor="middle" fill="#ffffff" font-size="8" font-family="system-ui">database</text><rect x="400" y="55" width="100" height="40" rx="6" fill="#3b82f6" opacity="0.85"/><text x="450" y="75" text-anchor="middle" fill="#ffffff" font-size="10" font-family="system-ui">Update CRM</text><text x="450" y="87" text-anchor="middle" fill="#ffffff" font-size="8" font-family="system-ui">API call</text><circle cx="545" cy="75" r="18" fill="none" stroke="#2dd4bf" stroke-width="2"/><text x="545" y="79" text-anchor="middle" fill="#2dd4bf" font-size="9" font-family="system-ui">Done</text><defs><marker id="arrow10" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6" fill="#e2e8f0"/></marker></defs><line x1="87" y1="85" x2="138" y2="85" stroke="#e2e8f0" stroke-width="1.5" marker-end="url(#arrow10)"/><line x1="210" y1="72" x2="248" y2="55" stroke="#e2e8f0" stroke-width="1.5" marker-end="url(#arrow10)"/><line x1="210" y1="98" x2="248" y2="115" stroke="#e2e8f0" stroke-width="1.5" marker-end="url(#arrow10)"/><line x1="352" y1="55" x2="398" y2="68" stroke="#e2e8f0" stroke-width="1.5" marker-end="url(#arrow10)"/><line x1="352" y1="115" x2="398" y2="82" stroke="#e2e8f0" stroke-width="1.5" marker-end="url(#arrow10)"/><line x1="502" y1="75" x2="525" y2="75" stroke="#e2e8f0" stroke-width="1.5" marker-end="url(#arrow10)"/><text x="225" y="45" text-anchor="middle" fill="#2dd4bf" font-size="8" font-family="system-ui">true</text><text x="225" y="120" text-anchor="middle" fill="#a855f7" font-size="8" font-family="system-ui">false</text></svg><p style="margin-top:0.75rem;font-size:0.85rem;color:#94a3b8;font-style:italic;line-height:1.4;">Workflow automation: triggers, conditions, and actions chain together to eliminate manual processes.</p></div>

Comparison

Factor
GitFlow
Trunk-Based

|--------|---------|-------------|

Branch lifetime
Days to weeks
Hours to 1 day
Merge frequency
Weekly/biweekly
Multiple times daily
Merge conflicts
Common and painful
Rare and small
Deploy frequency
Weekly/monthly
Daily/multiple daily
Feature flags needed
Optional
Essential
CI/CD requirements
Basic
Strong (fast tests)
Code review speed
Can be slow
Must be fast (<4h)
Team size
Any
Works best 2-20
Risk per deploy
Higher (big changes)
Lower (small changes)
Best for
Versioned releases
Web apps, SaaS
Rollback method
Revert merge
Toggle feature flag

The Hybrid Approach

Many teams use a practical hybrid:

# Trunk-based with short-lived branches
main (protected, requires PR + CI)
  ├── feature/xyz (lives < 2 days)
  ├── fix/abc (lives < 1 day)
  └── release/v2.1 (only for mobile/versioned releases)

Rules: 1. Feature branches must be merged within 2 days 2. PRs must be under 400 lines changed 3. CI must pass before merge 4. Main is always deployable 5. Feature flags for incomplete features

Branch Protection Setup

# Gitea branch protection (API)
curl -X PUT "http://localhost:3000/api/v1/repos/yash/techsaas-website/branch_protections/main" \
  -u 'yash:Eagle@2904' \
  -H 'Content-Type: application/json' \
  -d '{
    "enable_push": false,
    "enable_push_whitelist": true,
    "push_whitelist_usernames": ["yash"],
    "require_signed_commits": false,
    "enable_status_check": true,
    "status_check_contexts": ["ci/build", "ci/test"],
    "block_on_rejected_reviews": true,
    "dismiss_stale_approvals": true
  }'

<div style="margin:2.5rem auto;max-width:600px;width:100%;text-align:center;"><svg viewBox="0 0 600 200" xmlns="http://www.w3.org/2000/svg" style="width:100%;height:auto;"><rect width="600" height="200" rx="12" fill="#1a1a2e"/><rect x="0" y="0" width="600" height="28" rx="12" fill="#2d2d44"/><rect x="0" y="12" width="600" height="16" fill="#2d2d44"/><circle cx="18" cy="14" r="5" fill="#ef4444"/><circle cx="34" cy="14" r="5" fill="#f59e0b"/><circle cx="50" cy="14" r="5" fill="#2dd4bf"/><text x="300" y="18" text-anchor="middle" fill="#94a3b8" font-size="10" font-family="system-ui">docker-compose.yml</text><rect x="0" y="28" width="35" height="172" fill="#1e1e32"/><text x="25" y="48" text-anchor="end" fill="#94a3b8" font-size="10" font-family="monospace" opacity="0.5">1</text><text x="25" y="66" text-anchor="end" fill="#94a3b8" font-size="10" font-family="monospace" opacity="0.5">2</text><text x="25" y="84" text-anchor="end" fill="#94a3b8" font-size="10" font-family="monospace" opacity="0.5">3</text><text x="25" y="102" text-anchor="end" fill="#94a3b8" font-size="10" font-family="monospace" opacity="0.5">4</text><text x="25" y="120" text-anchor="end" fill="#94a3b8" font-size="10" font-family="monospace" opacity="0.5">5</text><text x="25" y="138" text-anchor="end" fill="#94a3b8" font-size="10" font-family="monospace" opacity="0.5">6</text><text x="25" y="156" text-anchor="end" fill="#94a3b8" font-size="10" font-family="monospace" opacity="0.5">7</text><text x="25" y="174" text-anchor="end" fill="#94a3b8" font-size="10" font-family="monospace" opacity="0.5">8</text><text x="25" y="192" text-anchor="end" fill="#94a3b8" font-size="10" font-family="monospace" opacity="0.5">9</text><text x="45" y="48" fill="#a855f7" font-size="11" font-family="monospace">version</text><text x="100" y="48" fill="#e2e8f0" font-size="11" font-family="monospace">: &quot;3.8&quot;</text><text x="45" y="66" fill="#a855f7" font-size="11" font-family="monospace">services</text><text x="105" y="66" fill="#e2e8f0" font-size="11" font-family="monospace">:</text><text x="55" y="84" fill="#3b82f6" font-size="11" font-family="monospace"> web</text><text x="80" y="84" fill="#e2e8f0" font-size="11" font-family="monospace">:</text><text x="55" y="102" fill="#2dd4bf" font-size="11" font-family="monospace"> image</text><text x="110" y="102" fill="#e2e8f0" font-size="11" font-family="monospace">: nginx:alpine</text><text x="55" y="120" fill="#2dd4bf" font-size="11" font-family="monospace"> ports</text><text x="102" y="120" fill="#e2e8f0" font-size="11" font-family="monospace">:</text><text x="55" y="138" fill="#e2e8f0" font-size="11" font-family="monospace"> - &quot;80:80&quot;</text><text x="55" y="156" fill="#2dd4bf" font-size="11" font-family="monospace"> volumes</text><text x="118" y="156" fill="#e2e8f0" font-size="11" font-family="monospace">:</text><text x="55" y="174" fill="#e2e8f0" font-size="11" font-family="monospace"> - ./html:/usr/share/nginx</text><rect x="365" y="164" width="2" height="14" fill="#6366f1" opacity="0.8"/></svg><p style="margin-top:0.75rem;font-size:0.85rem;color:#94a3b8;font-style:italic;line-height:1.4;">A well-structured configuration file is the foundation of reproducible infrastructure.</p></div>

Our Recommendation

For web applications and SaaS products, use trunk-based development. The faster feedback loops, smaller merge conflicts, and continuous deployment capabilities are worth the investment in CI/CD and feature flags.

For mobile apps, libraries, and software with versioned releases that need long-term support, GitFlow or a modified version with release branches makes more sense.

At TechSaaS, we use trunk-based development with branch protection on all our Gitea repositories. Every push triggers CI (build + security scan), PRs require passing CI before merge, and we deploy from main multiple times per day. Feature flags through Directus CMS let us merge code without exposing unfinished features.

#git#trunk-based-development#gitflow#ci-cd#workflow

Need help with devops?

TechSaaS provides expert consulting and managed services for cloud infrastructure, DevOps, and AI/ML operations.