Trunk-Based Development: Continuous Integration Done Right
Trunk-based development (TBD) means integrating code into main frequently to avoid merge hell.
Core Principles
- Short-lived branches: 1-2 days maximum, then merge
- Small commits: Each commit is a complete, working unit
- Always green: Main branch is always deployable
- Feature flags: Hide incomplete features in production
Feature Flags for Incomplete Work
// Feature flag service
class FeatureFlags {
private flags: Map<string, boolean>;
constructor(private config: FlagConfig) {
this.flags = new Map(Object.entries(config));
}
isEnabled(flag: string, userId?: string): boolean {
return this.flags.get(flag) ?? false;
}
}
// Usage: safely commit incomplete feature
async function processOrder(order: Order, flags: FeatureFlags) {
if (flags.isEnabled('new-payment-flow')) {
return await newPaymentService.process(order);
}
return await legacyPaymentService.process(order);
}
Branch Strategy
# Trunk-based (recommended)
main <-- single source of truth
|
+-- feature/user-auth (1-2 days max)
+-- fix/login-bug (hours max)
# NOT trunk-based (avoid)
main
develop
release/v1.2
feature/big-feature (3 weeks old!)
Pair/Mob Programming Alternative
# With short branches (< 1 day), integrate directly
git checkout -b feat/add-search
# work for a few hours
git add .
git commit -m "feat: add search endpoint with basic filtering"
git push origin feat/add-search
# create PR, get quick review, merge same day
CI Pipeline for TBD
# .github/workflows/ci.yml
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run typecheck
- run: npm run lint
- run: npm test -- --coverage
- run: npm run build
deploy:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- name: Deploy to production
run: ./deploy.sh
Preventing Big Bang Merges
// Instead of branching for 2 weeks, commit behind a flag
// Day 1: Add flag and stub
async function getRecommendations(userId: string) {
if (featureFlags.isEnabled('ml-recommendations')) {
// TODO: implement ML recommendations
return [];
}
return legacyRecommendations(userId);
}
// Day 2: Implement core logic behind flag
// Day 3: Enable flag for 10% of users
// Day 4: Enable for all users
// Day 5: Remove flag and old code
Branch Protection Rules
main branch protection:
- Require status checks (CI must pass)
- Require 1+ approvals
- Dismiss stale reviews on new commits
- No direct pushes (PR required)
- Linear history required (rebase/squash)
TBD with feature flags enables true continuous delivery—deploying multiple times per day safely.