Trunk-Based Development: The Branching Strategy Behind Google and Facebook

Explore Trunk-Based Development — the branching model used by Google, Meta, and Netflix. Learn about short-lived branches, feature flags, and continuous integration at scale.

published: reading time: 22 min read author: Geek Workbench updated: March 31, 2026

Trunk-Based Development: The Branching Strategy Behind Google and Facebook

Trunk-Based Development is the branching strategy that powers the largest software organizations on Earth. Google, Meta, Netflix, and Amazon all use variations of this model. It is the simplest branching strategy in existence and the hardest to adopt.

The core idea: everyone commits to a single shared branch (the trunk) at least daily. Feature branches, if they exist, last hours — not days. Code that isn’t ready for users is hidden behind feature flags, not isolated in long-lived branches.

This post explains why the world’s most productive engineering teams use this approach, what it takes to make it work, and why most teams fail when they try.

When to Use / When Not to Use

Use Trunk-Based Development When

  • Large engineering organizations — 50+ developers working on the same codebase
  • Continuous deployment — Multiple deployments per day to production
  • Strong CI/CD infrastructure — Automated testing that runs in minutes, not hours
  • Feature flag platform — Infrastructure to toggle features on and off at runtime
  • Experienced teams — Developers comfortable with small, incremental commits

Do Not Use Trunk-Based Development When

  • Small teams without CI — Without automated testing, trunk breaks are frequent and costly
  • Scheduled release cycles — If you ship monthly, the trunk model provides no structure
  • Junior-heavy teams — Requires discipline around small commits and code review
  • No feature flag infrastructure — Without flags, incomplete features block the trunk
  • Regulated environments — Formal change management processes conflict with continuous commits

Core Concepts

Trunk-Based Development is built on four pillars:

PillarDescription
Single trunkOne shared branch (usually main) that is always releasable
Short-lived branchesFeature branches last hours, not days. Maximum 1-2 days.
Feature flagsIncomplete features are hidden behind runtime toggles, not branches
Continuous integrationAutomated tests run on every commit, blocking broken code from trunk

The model assumes that the cost of integrating code increases exponentially with branch age. A branch that’s one day old has trivial merge conflicts. A branch that’s two weeks old has conflicts in every file.

graph LR
    A[main - Trunk] --> B[short feature 1]
    A --> C[short feature 2]
    A --> D[short feature 3]
    B -->|hours| A
    C -->|hours| A
    D -->|hours| A
    A --> E[Continuous Deploy]

Architecture and Flow Diagram

The complete Trunk-Based Development workflow with feature flags and CI gates:

graph TD
    A[main - Always Releasable] -->|fork| B[feature branch]
    B -->|commit| B
    B -->|push + CI| C{CI Pass?}
    C -->|No| D[Fix and retry]
    D --> B
    C -->|Yes| E[Code Review]
    E -->|Approved| F[Squash merge to main]
    F --> G[Deploy to Production]
    G -->|Feature Flag OFF| H[Code deployed but hidden]
    G -->|Feature Flag ON| I[Feature visible to users]

Step-by-Step Guide

1. Commit to Trunk Frequently

The golden rule: no developer should go more than one day without merging to trunk. In practice, top teams merge 3-10 times per day.

# Start with latest trunk
git checkout main
git pull origin main

# Create a short-lived branch
git checkout -b feat/add-search-filter

# Make small, focused changes
git add src/components/SearchFilter.tsx
git commit -m "feat: add search filter component skeleton"

# Push and open PR immediately
git push -u origin feat/add-search-filter

2. Use Feature Flags for Incomplete Work

Never let incomplete code block your merge. Hide it behind a flag:

// src/pages/SearchPage.tsx
import { useFeatureFlag } from '@/hooks/useFeatureFlag';

export function SearchPage() {
  const showAdvancedFilters = useFeatureFlag('advanced-search-filters');

  return (
    <div>
      <SearchInput />
      {showAdvancedFilters && <AdvancedFilters />}
      <SearchResults />
    </div>
  );
}

// Feature flag configuration
// Can be toggled without code deployment
const FEATURE_FLAGS = {
  'advanced-search-filters': false, // Toggle to true when ready
  'new-search-algorithm': true,     // Already rolled out
};

3. Keep Branches Short-Lived

Break large features into small, mergeable increments:

# Instead of one massive branch for "search feature":
# Day 1: Search input component
git checkout -b feat/search-input
# ... implement, test, merge

# Day 1: Search results list
git checkout -b feat/search-results
# ... implement, test, merge

# Day 2: Search filters (behind flag)
git checkout -b feat/search-filters
# ... implement behind flag, test, merge

# Day 2: Search analytics
git checkout -b feat/search-analytics
# ... implement, test, merge

Each increment is independently mergeable and testable.

4. Code Review on Every Change

Code review is the primary quality gate in trunk-based development:

# Open PR with small, focused changes
gh pr create \
  --title "feat: add search input component" \
  --body "Part 1 of search feature. Adds the search input UI component.
Advanced filters and results come in follow-up PRs."

# Review should be fast — small PRs get reviewed quickly
# Target: review within 1 hour, merge within 4 hours

5. Automated Testing on Every Commit

Your CI pipeline must be fast enough to not block the flow:

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

jobs:
  fast-checks:
    # These run in parallel, target: < 5 minutes
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run lint
      - run: npm run typecheck
      - run: npm test -- --testPathPattern=unit

  integration-tests:
    needs: fast-checks
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm run test:integration

  deploy:
    needs: integration-tests
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/deploy.sh

Production Failure Scenarios

ScenarioWhat HappensMitigation
Trunk breakA bad commit breaks the build for everyoneCI blocks the merge; if it slips through, revert immediately; blameless postmortem
Feature flag leakA flagged feature is accidentally exposed to usersAutomated tests verify flag states; staging environment with production flag config
Merge conflict stormMultiple developers modify the same files simultaneouslyCommunicate in standup; use CODEOWNERS for sensitive areas; rebase frequently
Flag debt accumulationOld feature flags clutter the codebaseSchedule flag cleanup sprints; set expiration dates on flags; automate flag detection
Partial feature deploymentHalf a feature ships because the second PR isn’t readyDesign features to be incrementally valuable; use flags to hide incomplete pieces

Trade-off Analysis

AspectAdvantageDisadvantage
Integration costNear-zero — branches merge dailyRequires discipline to keep branches short
Deployment frequencyMultiple times per dayRequires robust CI/CD and monitoring
Code qualityContinuous review catches issues earlyRequires experienced developers
Feature managementFlags enable progressive rolloutFlag management adds complexity
Team coordinationEveryone works on the same versionConflicts are resolved immediately, not deferred
ScalabilityWorks for thousands of developersRequires significant infrastructure investment

Implementation Snippets

Feature Flag SDK Pattern

// src/lib/featureFlags.ts
interface FeatureFlagConfig {
  name: string;
  enabled: boolean;
  rolloutPercentage?: number;
  expirationDate?: string;
}

class FeatureFlagManager {
  private flags: Map<string, FeatureFlagConfig> = new Map();

  async load(): Promise<void> {
    const response = await fetch("/api/feature-flags");
    const flags: FeatureFlagConfig[] = await response.json();
    flags.forEach((f) => this.flags.set(f.name, f));
  }

  isEnabled(name: string, userId?: string): boolean {
    const flag = this.flags.get(name);
    if (!flag) return false;
    if (!flag.enabled) return false;

    if (flag.rolloutPercentage !== undefined) {
      const hash = this.hashUserId(userId || "anonymous");
      return hash % 100 < flag.rolloutPercentage;
    }

    return true;
  }

  // Check for expired flags
  getExpiredFlags(): FeatureFlagConfig[] {
    const now = new Date();
    return Array.from(this.flags.values()).filter(
      (f) => f.expirationDate && new Date(f.expirationDate) < now,
    );
  }

  private hashUserId(id: string): number {
    let hash = 0;
    for (let i = 0; i < id.length; i++) {
      hash = (hash << 5) - hash + id.charCodeAt(i);
      hash |= 0;
    }
    return Math.abs(hash);
  }
}

export const featureFlags = new FeatureFlagManager();

Pre-commit Hook — Enforce Small Commits

#!/bin/bash
# .git/hooks/pre-commit
# Warn if commit message doesn't follow convention

MSG_FILE=$1
MSG=$(cat "$MSG_FILE")

# Check for conventional commit prefix
if ! echo "$MSG" | grep -qE '^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?:'; then
  echo "WARNING: Commit message should follow conventional commit format."
  echo "Example: feat: add search filter component"
  echo "Continue anyway? (y/n)"
  read -r answer
  if [ "$answer" != "y" ]; then
    exit 1
  fi
fi

Branch Age Monitor

#!/bin/bash
# scripts/check-branch-age.sh
# Alert when feature branches exceed 2 days

MAX_AGE_DAYS=2
THRESHOLD=$(date -d "$MAX_AGE_DAYS days ago" +%s 2>/dev/null || date -v-"${MAX_AGE_DAYS}d" +%s)

git branch -r --format='%(refname:short) %(committerdate:unix)' | \
  grep 'origin/feature/' | \
  while read -r branch timestamp; do
    if [ "$timestamp" -lt "$THRESHOLD" ]; then
      echo "WARNING: $branch is older than $MAX_AGE_DAYS days"
    fi
  done

Observability Checklist

  • Logs: Log every feature flag evaluation in production for audit trails
  • Metrics: Track branch age distribution, merge frequency per developer, and CI pipeline duration
  • Traces: Trace each deployment back to the specific commits and PRs that triggered it
  • Alerts: Alert when branch age exceeds 2 days, CI pipeline exceeds 15 minutes, or trunk break rate exceeds 1 per week
  • Dashboards: Display trunk health metrics: build success rate, average merge time, flag count, and deployment frequency

Security and Compliance: Trunk-Based Development

  • Feature flag governance: Treat production feature flags as deployment controls. Only authorized personnel should toggle flags in production.
  • Flag access control: Implement role-based access for flag management. Developers can create flags in staging; only release managers toggle production flags.
  • Flag audit trail: Log every flag change with timestamp, user identity, and business justification. This is critical for SOC 2 and HIPAA compliance.
  • Flag expiration enforcement: CI should fail builds that contain flags past their expiration date. Stale flags are a security risk — they expand the attack surface.
  • Code review enforcement: Use branch protection to require reviews before any merge to trunk. Automated CI pipelines serve as change approval records.
  • Compliance mapping: Each trunk merge is a change event. Map merge commits to change tickets for audit purposes.
  • Secret management: Never store flag configuration or toggle values in code. Use a secure flag management service with encryption at rest.

Common Pitfalls / Anti-Patterns

  1. The “Trunk” That Isn’t — Teams that call it trunk-based but allow week-long branches are just doing Git Flow with different names. Enforce branch age limits.
  2. Flag Sprawl — Hundreds of stale feature flags make the codebase unreadable. Set expiration dates and clean up regularly.
  3. Skipping Tests — Without fast, reliable tests, trunk breaks become daily events. Invest in CI infrastructure first.
  4. Big Bang Merges — Merging a week’s worth of work in one PR defeats the purpose. Break work into daily mergeable chunks.
  5. Flag Coupling — Features that depend on each other’s flags create complex toggle combinations. Design features to be independently flaggable.
  6. No Rollback Plan — When a trunk merge breaks production, you need instant rollback. Automated rollback on error rate spikes is essential.
  7. Ignoring Code Review — Fast merges without review lead to quality degradation. Small PRs enable fast review — don’t skip it.

Quick Recap Checklist

  • Everyone commits to trunk at least once per day
  • Feature branches exist for hours, not days
  • Incomplete features are hidden behind feature flags
  • CI pipeline runs on every commit and blocks broken code
  • Code review is required for every merge to trunk
  • Feature flags have expiration dates and are cleaned up regularly
  • Trunk is always in a deployable state
  • Failed deployments trigger automatic rollback
  • Branch age is monitored and alerts fire for old branches
  • Feature flag changes are logged and audited

Interview Questions

1. How does Trunk-Based Development differ from GitHub Flow?

Both use a single main branch, but the key difference is branch lifetime. GitHub Flow allows feature branches to exist for days or weeks while Trunk-Based Development enforces branches lasting hours to a maximum of 1-2 days.

Trunk-Based Development also relies more heavily on feature flags to manage incomplete work, whereas GitHub Flow may leave features in branches until they're complete. At scale, trunk-based requires more infrastructure investment but delivers higher integration velocity.

2. What happens when a developer breaks the trunk?

The immediate response is revert the commit — not fix forward. Getting the trunk green again is the highest priority. Then conduct a blameless postmortem to understand why the CI pipeline didn't catch the issue.

Prevention is better than cure: fast CI pipelines that run on every push, required code review, and small commits all reduce the probability of trunk breaks. Google reports that their trunk break rate is less than 1% of all commits.

3. How do you manage feature flags at scale?

Use a dedicated feature flag platform like LaunchDarkly, Split, or an internal solution. Key practices include:

  • Expiration dates — Every flag has a planned removal date
  • Automated cleanup — CI checks for flags past their expiration
  • Rollout percentages — Gradually increase exposure from 1% to 100%
  • Access control — Only authorized personnel can toggle production flags
  • Audit logging — Every flag change is recorded with who, when, and why
4. What are the key differences in integration frequency between trunk-based development and GitFlow, and how does this affect overall team velocity at scale?

In GitFlow, developers work on feature branches for days or weeks before merging, resulting in integration happening infrequently. Trunk-based development mandates daily integration, with top teams merging 3-10 times per day. At scale, this near-continuous integration keeps merge conflicts small and manageable, while GitFlow's infrequent integration creates complex, multi-file conflicts that take days to resolve. Google's internal data shows their integration velocity is 30-50x higher than teams using long-lived branching strategies.

5. How does trunk-based development handle long-running features that span multiple days, and what is the recommended approach for breaking down work?

Long-running features are decomposed into vertical slices that can be completed and merged within hours. Each slice should deliver some user value independently, even if hidden behind a feature flag. For example, a search feature might be broken into: (1) basic search input UI, (2) search results display, (3) filter controls, (4) search analytics. Each piece merges to trunk on its own day, with flags hiding incomplete functionality. This requires significant upfront design work but eliminates the integration debt that accumulates with long branches.

6. What is the minimum CI/CD infrastructure required before adopting trunk-based development, and what happens without it?

The minimum viable infrastructure includes: (1) Fast unit tests completing in under 5 minutes, (2) Automated build verification on every push, (3) Branch protection blocking merges when CI fails, (4) Feature flag platform for hiding incomplete work. Without these, trunk breaks become daily events. The team spends more time fixing integration issues than building features. Netflix and Google invested millions in CI infrastructure before adopting trunk-based development — the model only works when CI is faster than manual testing would be.

7. How do feature flags create operational complexity in trunk-based development, and what practices manage this complexity?

Feature flags add operational complexity through: flag proliferation (hundreds of flags become unreadable), flag dependency chains (features requiring multiple flags create toggle matrix problems), and stale flags that never get cleaned up. Management practices include: mandatory expiration dates on every flag enforced by CI, automated flag detection and alerting, quarterly flag cleanup sprints, and treating flags as production infrastructure rather than code. LaunchDarkly reports that teams with mature flag practices spend 15% less time on release management.

8. What metrics should engineering teams track to measure trunk-based development health and adoption success?

Key metrics include: Integration frequency (commits to trunk per day, target: 5+), Branch age (average hours from branch creation to merge, target: under 24), Trunk break rate (build failures per week, target: under 1), Cycle time (commit to production, target: under 1 hour), Flag count (total active flags, tracked for cleanup), Rollback frequency (emergency reversions per month). DORA research shows elite performers achieve: less than 1 hour cycle time, less than 1% trunk break rate, and multiple daily deployments.

9. How does trunk-based development interact with microservices architectures, and what conflicts arise with independent service deployments?

Trunk-based development works well with microservices when each service independently practices trunk-based development. However, conflicts arise when services have different release cadences — Service A may be on trunk while Service B is weeks behind. This creates version skew at runtime. Solutions include: (1) contract testing between services, (2) shared trunk for tightly coupled services, (3) API versioning strategies. Amazon's "two-pizza team" model assigns each team full ownership of their services, allowing independent trunk-based adoption per service while maintaining compatibility through API contracts.

10. How does the risk profile of frequent small commits differ from infrequent large commits in trunk-based development?

Frequent small commits spread risk across time — each commit has a smaller blast radius if it breaks trunk. A bug in one day's worth of commits affects a small percentage of the codebase. In contrast, a week's worth of work in one merge creates a large blast radius that's harder to diagnose. However, small commits require discipline: each must be coherent and testable in isolation. The CI pipeline becomes the safety net — if tests pass, the commit is safe. The trade-off is investing in fast, reliable tests that don't create false positives.

11. How does trunk-based development handle emergency hotfixes when the trunk contains incomplete features?

The feature flag architecture enables safe hotfixes: completed code is already on trunk and deployed, while incomplete code is hidden behind flags. For hotfixes, the developer creates a short branch from the current trunk, fixes the bug, merges through CI, and deploys. The incomplete features remain hidden by their flags and do not affect the fix. If a hotfix requires changes to flagged code, it can be applied directly — flags hide functionality, not code structure. The hotfix deploys, and users see the fix regardless of what flags are on or off.

12. What cultural and organizational changes are required to successfully adopt trunk-based development, particularly around accountability?

Trunk-based development requires shifting accountability to the individual developer — when you merge broken code, you block everyone immediately. Cultural changes include: (1) Blame-free postmortems when trunk breaks, focusing on system improvements, (2) Developer-owned quality — no separate QA phase to catch what CI missed, (3) Small PR culture — code review must happen in hours, not days, (4) Feature flag discipline — treating flags as first-class infrastructure. Teams transitioning from GitFlow typically see 2-3 months of lower productivity before velocity increases as integration costs disappear.

13. How do you prevent feature flag debt from accumulating in a trunk-based development model?

Feature flag debt accumulates when flags outlive their purpose. Prevention strategies: (1) Expiration dates on every flag — CI fails if a flag expires without removal, (2) Flag ownership — each flag has an owner responsible for removal, (3) Quarterly flag audits — dedicated sprint time for flag cleanup, (4) Automated flag detection — scripts flag code that references deleted flags, (5) Flag count dashboards — visibility into flag growth trends. Google's internal guidelines require all flags to have expiration dates, with CI blocking merges for expired flags.

14. What security and compliance considerations arise from maintaining a constantly deployable trunk branch?

Continuous deployment creates compliance challenges: (1) Change management — regulators may require documented approval before production changes, (2) Audit trails — every trunk merge must be traceable to a change request, (3) Segregation of duties — developers write code, but who approves deployment?, (4) Secret management — flag configurations should not contain credentials. Solutions include treating the CI pipeline as the approval record, implementing mandatory code review as change approval, and integrating flag management platforms with compliance tools. SOC 2 Type II audits require demonstrable controls over production changes.

15. How does trunk-based development handle database migrations and schema changes, particularly with feature flags?

Database migrations in trunk-based development follow backward-compatible patterns: (1) Add new columns as nullable or with defaults before using them, (2) Deploy schema change to all instances before activating flag-controlled features, (3) Use read-your-writes consistency to handle cases where the new code runs before the migration completes. For risky migrations, the pattern is: migrate schema in one trunk commit, deploy, then add the code using the new schema in a subsequent commit behind a flag. This ensures the database is always compatible with whatever code is currently deployed.

16. What strategies exist for managing technical debt in a trunk-based development model where refactoring cannot always be completed incrementally?

Technical debt in trunk-based development is managed through: (1) Strangler fig patterns — new code gradually replaces old code behind flags, allowing incremental migration, (2) Debt budgets — allocate 20% of sprint capacity to debt reduction, tracked like any other work, (3) Automated refactoring tools — linters and codemods handle mechanical transformations, (4) Dedicated debt sprints — periodic focused efforts on specific debt areas. The key constraint: debt work must be mergeable in small increments, not sprawling refactors that take weeks. If a refactor cannot be broken down, it is a candidate for the strangler fig pattern.

17. How do you structure code reviews to maintain quality in trunk-based development without becoming a bottleneck that slows merge velocity?

Effective code review in trunk-based development requires: (1) Small PRs under 400 lines — reviewers can complete reviews in under 30 minutes, (2) Async-first review culture — non-blocking comments distinguish blocking ("must fix") from non-blocking ("nit:"), (3) Review SLAs — target 1-hour response time, enforced by team norms, (4) Automated pre-review checks — linting, formatting, and tests run before human review, (5) Reviewer rotation — distribute knowledge and prevent bottlenecks. Google's research shows that PRs under 400 lines have a 78% first-review approval rate within 24 hours, compared to 35% for PRs over 1000 lines.

18. What approaches can teams use to validate feature flag configurations before deploying to production?

Feature flag validation strategies include: (1) Staging environment with production-equivalent flag config — test flag combinations before production release, (2) Canary deployments — enable flags for 1% of users first, then gradually increase, (3) Automated flag testing — integration tests verify flag-gated code paths are exercised with both flag states, (4) Flag audit logs — every flag change triggers review in staging before production, (5) Progressive rollout percentages — start with internal users, then beta, then gradual public rollout. LaunchDarkly's SDKs include kill switch capabilities for instant flag disable if issues arise.

19. How does trunk-based development integrate with automated rollback mechanisms, and what triggers should initiate automatic versus manual rollback?

Automated rollback in trunk-based development triggers when: (1) Error rate spikes — predefined thresholds on specific metrics (e.g., 5% increase in 5xx errors), (2) Latency degradation — p99 latency exceeds baseline by 50%, (3) Health check failures — continuous /health endpoint failures. Rollback is automatic when the trigger is unambiguous. Manual intervention is required when: the root cause is unclear (need to investigate before rolling back wrong thing), flags are involved (need to determine if flag or code caused issue), or business metrics are degraded without clear technical cause (e.g., conversion rate drops). The goal is to recover to known good state within 5 minutes of detecting an issue.

20. How does trunk-based development compare to Git Flow in terms of deployment pipeline complexity and required tooling?

Trunk-based development requires significantly more tooling investment: (1) CI/CD pipeline must be fast (< 10 min) and reliable — Git Flow can tolerate slower pipelines because integration is less frequent, (2) Feature flag platform is mandatory — Git Flow doesn't need flags, (3) Rollback automation is essential — manual rollback is too slow for multiple daily deploys, (4) Environment parity — staging must match production flag states. Git Flow's deployment model is simpler: merge to release branch, test, deploy. At scale, however, Git Flow's integration complexity far exceeds the tooling complexity of trunk-based development.

Further Reading

Conclusion

Trunk-based development demands discipline — short-lived branches, feature flags, and continuous integration. The payoff is minimal merge pain, fast feedback, and the ability to deploy any time. It’s the strategy that scales to hundreds of developers.

Category

Related Posts

Choosing a Git Team Workflow: Decision Framework

Decision framework for selecting the right Git branching strategy based on team size, release cadence, and project type.

#git #version-control #branching-strategy

Git Flow: The Original Branching Strategy Explained

Master the Git Flow branching model with master, develop, feature, release, and hotfix branches. Learn when to use it, common pitfalls, and production best practices.

#git #version-control #branching-strategy

Git Release Branching and Hotfixes: Managing Versions in Production

Master release branching and hotfix strategies in Git. Learn version branches, emergency fixes, backporting, and how to manage multiple production versions safely.

#git #version-control #release-management