Workflows

Trunk-based development workflow

Trunk-based development vs branch-based development comparison, feature flag integration, short branch lifecycle, and small-step commit practices.

Who This Is For
  • Teams turning commands into repeatable routines
  • Readers who need sequencing, branch, and sync discipline
Prerequisites
  • Basic understanding of fetch, pull, push, and branches
  • A sense of how and why branches diverge
Common Risks
  • Copying a workflow without checking branch state
  • Choosing the wrong integration path on shared branches

The short version

Trunk-Based Development vs Branch-BasedTrunk-based development requires developers to work directly on main or use very short-lived branches (< 1-2 days). Feature flags control unreleased features.
Trunk-based (short branches → fast merge)
ABCD
main branch: main
Feature branches (long independent dev)
ABCM
BEF
Trunk-based + Feature Flags
ABCE'F'
main branch: feature

Trunk-Based Development (TBD) is a branching strategy where all developers work directly on trunk (main/master), or create extremely short-lived feature branches (< 1-2 days) that merge back to trunk quickly. This contrasts sharply with Gitflow and other long-lived branching strategies.

What is trunk-based development

Core principles

main ───●──●──●──●──●──●──●──●──
        ↑  ↑     ↑  ↑     ↑  ↑
      commit merge commit merge commit merge
      (frequent, small steps)
  • Single main branch: main (or trunk) is the only long-lived branch
  • Short-lived feature branches: feature branches live < 1-2 days
  • Frequent merges: merge to trunk at least daily
  • Continuous integration: every merge goes through automated tests

Comparison with Gitflow

Gitflow model

main ───────●──────────────●──────── (merge only at release)
             ↖            ↗
develop ───────●──●──●──●──●──●──
              ↗  ↗    ↗  ↗
feature1 ──●──●    ─●──●
feature2 ────●──●──●
release ───────────────●──●──●──

Characteristics:

  • Long-lived develop branch
  • Feature branches may survive for weeks
  • Release branches managed independently
  • Infrequent merges, high conflict risk

TBD model

main ───●──●──●──●──●──●──●──●──
       ↗  ↗  ↗  ↗  ↗  ↗  ↗
f1 ──●──●
f2 ─────●──●
f3 ─────────●
f4 ────────────●──●

Characteristics:

  • No develop branch
  • Extremely short feature branches (hours to 1-2 days)
  • Direct commits to trunk are common
  • Very frequent merges

Short branch lifecycle

Why branches should be short

# Problems with long-lived branches
# Day 1: Create feature branch from main
git checkout -b feature-huge main

# Day 7: Still developing, main has moved 50 commits
git fetch origin
git log main..origin/main | wc -l
# 50

# Day 14: Merge reveals massive conflicts
git checkout main
git merge feature-huge
# CONFLICT! Dozens of file conflicts...

Short branch strategy

# Day 1: Create branch, start work
git checkout -b feature-small main

# Day 1 afternoon: Feature complete, submit PR
git add .
git commit -m "feat: add small feature"
git push origin feature-small
# Create PR/MR

# Day 2 morning: Review approved, merged
# PR merged to main
git checkout main
git pull

Branch lifetime guidelines

Branch typeMax lifetimeDescription
Feature branch< 1 daySmall feature, one PR
Large feature branch< 2 daysNeeds feature flag splitting
Bug fix branch< 4 hoursUrgent fixes
Experiment branchAs neededUncertain if merging

Feature flag integration strategy

Why feature flags are needed

In TBD, unfinished features must also merge to trunk. Feature flags make unfinished features invisible to end users:

// Use feature flag to control feature visibility
if (featureFlags.isEnabled('new-search')) {
    // New search feature (still in development)
    renderNewSearch();
} else {
    // Old search feature
    renderOldSearch();
}

Feature flag implementation methods

Environment variables

const ENABLE_NEW_UI = process.env.ENABLE_NEW_UI === 'true';

if (ENABLE_NEW_UI) {
    renderNewUI();
}

Config file

// feature-flags.json
{
  "new-search": { "enabled": false, "rollout": 0 },
  "dark-mode": { "enabled": true, "rollout": 100 },
  "beta-api": { "enabled": true, "rollout": 10 }
}

Professional tools

// Using LaunchDarkly / Split.io etc.
import { LDClient } from 'launchdarkly-node-server-sdk';

const client = LDClient.init('your-sdk-key');
const showNewFeature = await client.variation('new-feature', user, false);

if (showNewFeature) {
    // New feature
}

Feature flag lifecycle

1. Create flag (default off)
   ↓
2. Develop feature under flag protection
   ↓
3. Merge to trunk (flag off, users can't see)
   ↓
4. Enable flag in test environment
   ↓
5. Gradual production rollout (10% → 50% → 100%)
   ↓
6. Remove flag after full release

Clean up expired flags

# Regularly search and clean up no-longer-needed flags
grep -rn "featureFlags.isEnabled" src/

# Create cleanup tasks
# TODO(flag): Remove new-search flag after v2.0 release

Small-step commit practices

What are small-step commits

# Bad practice — big commit
git add .
git commit -m "complete user module"
# Contains 200 files of changes, impossible to code review

# Good practice — small commits
git add src/user/model.js
git commit -m "feat(user): add user data model"

git add src/user/service.js
git commit -m "feat(user): implement user service layer"

git add src/user/controller.js tests/user/
git commit -m "feat(user): add user controller and tests"

Benefits of small-step commits

  1. Easy code review: Each PR has small changes, high review quality
  2. Easy bug location: When issues arise, quickly pinpoint the specific commit
  3. Fewer merge conflicts: Frequent merges, small conflict scope
  4. Continuous feedback: Every commit goes through CI, early problem detection

Commit size recommendations

MetricRecommendation
Files per PR< 10 files
Lines per PR< 400 lines
Files per commit< 5 files
PR review time< 30 minutes

Team size and TBD

Small teams (1-5 people)

  • Recommendation: ★★★★★
  • Practice: Commit directly to main, or use extremely short feature branches
  • CI requirement: Basic automated tests are sufficient

Medium teams (5-20 people)

  • Recommendation: ★★★★☆
  • Practice: Short feature branches + PR review + CI
  • CI requirement: Complete test suite + automated deployment

Large teams (20+ people)

  • Recommendation: ★★★☆☆
  • Practice: Requires feature flags + code ownership + strict CI
  • Considerations: May need to split into sub-repos or monorepo by module

When TBD doesn't fit

Unsuitable scenarios

  1. Strictly regulated industries: Healthcare, finance requiring strict change approval
  2. Projects without automated testing: Embedded systems, hardware-related
  3. Long release cycle projects: Traditional software released a few times per year
  4. Open source with many external contributors: Can't guarantee commit quality

Alternatives

ScenarioRecommended workflow
Large enterprise, strict processGitflow
Web apps, continuous deploymentGitHub Flow
Multi-version maintenanceGitflow or GitLab Flow
Fast-iterating SaaSTBD + Feature Flags

CI/CD integration

TBD requires strong CI

# .github/workflows/ci.yml
name: CI
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 1

      - name: Run tests
        run: npm test

      - name: Run lint
        run: npm run lint

      - name: Build
        run: npm run build

  deploy:
    needs: test
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to staging
        run: ./deploy.sh staging

Branch protection

# Protect main branch
# GitHub: Settings > Branches > Branch protection rules
# - Require pull request reviews
# - Require status checks to pass
# - Require branches to be up to date
# - Include administrators

Best practices summary

  1. Merge at least daily: Don't let branches survive more than a day
  2. Feature flags are essential: Protect unfinished features
  3. CI must be fast and reliable: Slow CI blocks frequent merges
  4. Small-step commits: Keep each PR reviewable in size
  5. Automate everything: Tests, lint, build all automated
  6. Clean up flags promptly: Remove flags after features go live
  7. Code review is not optional: TBD doesn't mean no review

Key takeaways

  1. TBD ≠ "no branches": Feature branches are still useful, just very short-lived
  2. Requires team discipline: Frequent merges need everyone's cooperation
  3. CI is foundational: TBD without reliable CI is a disaster
  4. Adopt incrementally: Start by reducing branch lifetime, transition gradually
  5. Monitor merge frequency: Track PR survival time, optimize continuously

Summary

DimensionTBDGitflow
Branch countFew (1 main)Many (main + develop + feature + release)
Merge frequencyMultiple times dailyWeekly / per release cycle
Feature releaseFeature flagsRelease branches
CI requirementHigh (must be fast & reliable)Medium
Suitable teamsAgile, continuous deliveryTraditional, fixed releases
Learning curveMedium (needs habit change)High (complex process)

TBD is an increasingly popular branching strategy in modern software development. It reduces integration risk through frequent merges, enables flexible releases through feature flags, and is one of the best practices for continuous delivery.