Trunk Based
Trunk-based development for continuous integration with short-lived branches
You are an expert in trunk-based development for continuous integration and rapid delivery. ## Key Points - Integrate at least once per day; branches older than two days signal a process problem - Use feature flags to separate deployment from release, allowing incomplete features to ship safely - Require linear history with fast-forward merges or squash merges to keep the trunk clean - Letting branches live too long, which reintroduces the merge-conflict pain trunk-based development is designed to avoid - Relying on feature branches for isolation instead of feature flags, which delays integration and hides conflicts
skilldb get git-workflow-skills/Trunk BasedFull skill: 117 linesTrunk-Based Development — Git Workflows
You are an expert in trunk-based development for continuous integration and rapid delivery.
Overview
Trunk-based development is a branching strategy where developers integrate small, frequent changes directly into a single shared branch (the trunk, typically main). Short-lived feature branches may exist but are merged within one to two days. This model minimizes merge conflicts and supports continuous delivery.
Core Concepts
The trunk: A single long-lived branch (main) where all developers integrate their work.
Short-lived branches: Feature branches that live for hours to a maximum of two days before merging.
Feature flags: Decouple deployment from release by hiding incomplete features behind runtime toggles.
# Start work on a short-lived branch
git checkout -b feat/add-search main
# ... make small, focused changes ...
git push -u origin feat/add-search
# Keep the branch fresh (daily or more)
git fetch origin
git rebase origin/main
# Merge quickly (same day or next day)
git checkout main
git merge --ff-only feat/add-search
git push origin main
git branch -d feat/add-search
Feature flag pattern (application code):
// Runtime toggle — ship code that isn't user-visible yet
if (featureFlags.isEnabled('new-search')) {
renderNewSearch();
} else {
renderLegacySearch();
}
Implementation Patterns
Stacked diffs / dependent PRs:
# First PR
git checkout -b feat/api-layer main
# ... implement API ...
git push -u origin feat/api-layer
# Second PR builds on the first
git checkout -b feat/ui-layer feat/api-layer
# ... implement UI ...
git push -u origin feat/ui-layer
# After first PR merges, rebase the second onto main
git checkout feat/ui-layer
git rebase origin/main
git push --force-with-lease
Enforcing trunk health with CI:
# GitHub Actions — require passing checks before merge
on:
pull_request:
branches: [main]
jobs:
gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm test && npm run lint
Core Philosophy
Trunk-based development is built on a single insight: integration pain grows exponentially with branch lifetime. A branch that lives for one day has a small diff and a trivial merge. A branch that lives for two weeks has accumulated conflicts with every other branch that merged during that period, and the merge becomes a risky, time-consuming event. By keeping branches short-lived (hours to one or two days) and integrating into the trunk continuously, trunk-based development converts one large, scary integration into many small, routine ones. The total work is less because conflicts are caught and resolved when they are small.
Feature flags are the enabling technology that makes trunk-based development practical. Without feature flags, the only way to hide incomplete work from users is to keep it on a branch. With feature flags, incomplete code can be merged to the trunk, deployed to production, and remain invisible to users until the flag is enabled. This separates two concerns that traditional branching conflates: deployment (putting code on servers) and release (exposing features to users). An organization that deploys every commit but releases features deliberately through flags has the best of both worlds: continuous integration with controlled rollout.
Trunk health is the non-negotiable invariant. If the trunk is broken, every developer on the team is blocked. This means the bar for merging to trunk must be high and automated: a comprehensive CI pipeline that runs fast enough to provide feedback before the developer moves on to the next task. Branch protection rules, required status checks, and fast CI are not optional guardrails — they are the immune system that keeps the trunk in a permanently releasable state. A team that adopts trunk-based development without investing in CI speed and reliability will quickly find that a broken trunk is worse than long-lived branches.
Anti-Patterns
-
Long-lived feature branches. Branches that live for a week or more accumulate merge conflicts, diverge significantly from the trunk, and reintroduce the integration pain that trunk-based development is designed to eliminate. If a feature cannot be completed in one to two days, break it into smaller increments and use feature flags to hide incomplete work.
-
Using branches instead of feature flags for isolation. Keeping code on a branch to "hide it from users" delays integration and creates merge conflicts. Deploy the code to production behind a feature flag so it is integrated continuously but invisible to users until it is ready.
-
No CI gate on the trunk. Allowing direct pushes to main without passing CI means a single broken commit blocks every developer. Require passing status checks (tests, lint, build) before any merge to the trunk, and invest in CI speed so the gate is fast enough to not be a bottleneck.
-
Rebasing instead of fast-forward merging without intent. Frequent rebasing rewrites history, which can confuse tooling and other developers. Use fast-forward merges (
--ff-only) or squash merges for a clean linear history, and rebase only when the branch needs to incorporate upstream changes before merging. -
Feature flags that never get cleaned up. Flags introduced to hide incomplete work that are never removed after the feature is fully released become permanent branching logic in the codebase. Establish a flag lifecycle: create, activate, fully roll out, remove the conditional code, delete the flag.
Best Practices
- Integrate at least once per day; branches older than two days signal a process problem
- Use feature flags to separate deployment from release, allowing incomplete features to ship safely
- Require linear history with fast-forward merges or squash merges to keep the trunk clean
Common Pitfalls
- Letting branches live too long, which reintroduces the merge-conflict pain trunk-based development is designed to avoid
- Relying on feature branches for isolation instead of feature flags, which delays integration and hides conflicts
Install this skill directly: skilldb add git-workflow-skills
Related Skills
Code Review
Code review best practices for constructive, efficient pull request reviews
Conventional Commits
Conventional Commits specification for structured, machine-readable commit messages
Git Bisect Debug
Debugging with git bisect and advanced git log techniques to pinpoint regressions
Git Hooks
Git hooks automation with Husky and lint-staged for pre-commit quality gates
Gitflow
Gitflow branching model for structured release-oriented development workflows
Monorepo Management
Monorepo strategies with Nx and Turborepo for scalable multi-project repositories