Trunk-Based Development¶
Modern software teams increasingly use trunk-based development: a workflow in which all engineers integrate their work into a single shared branch (typically main or trunk) many times a day, with short-lived feature branches if any, and with incomplete work hidden behind feature flags rather than held in separate branches.4
The case for trunk-based development is one of the most empirically supported in software engineering. The research consistently finds that high-performing teams integrate frequently and use small branches; lower-performing teams use long-lived feature branches and integrate rarely.12
Why Long-Lived Branches Are Expensive¶
The intuitive argument for long-lived branches is that they isolate work in progress. Engineers can ship a complete feature without intermediate states leaking into the trunk. This is true. It also creates costs that grow non-linearly with the age of the branch:
- Merge conflicts compound. Every day a branch lives separately from trunk is another day the code on trunk drifts away from the code on the branch. The cost of reconciling them at merge time grows faster than the cost of integrating continuously.
- Integration surprises are deferred, not prevented. Issues that would have surfaced immediately under continuous integration instead surface at merge time, often days or weeks after the work that caused them. By then, the engineer who can fix them is working on something else.
- Feedback loops slow down. Code that is not on trunk is not being exercised by the full test suite, the deployment pipeline, or other engineers' work. The team learns about problems later, when fixes are more expensive.
- Code review becomes a giant batch. Large pull requests are reviewed less carefully than small ones. The marginal defect caught per minute of reviewer attention drops as PR size grows.
- Knowledge silos form. Code that lives on a branch for weeks is code that one engineer understands well and the rest of the team has not seen.
- Risk concentrates at merge time. Releasing a long-lived branch is a high-variance event. Releasing many small changes is a series of low-variance events that the team can recover from individually.
This is why the modern engineering consensus runs strongly against long-lived branches, even when the per-change overhead of integration feels higher than the per-change overhead of branch isolation.
Core Practices¶
A working trunk-based workflow rests on a few mutually reinforcing practices:
- Short-lived branches, or none. Feature branches, if used at all, are measured in hours or a few days at most. Many teams skip branches entirely and commit directly to trunk.
- Small changes. Each integration is a small, reviewable, releasable unit. "Small" is not measured in lines of code but in scope: one logical change, not five.
- Continuous integration. Every change is automatically built and tested against trunk on its way in. The signal is fast enough to act on, and the test suite is fast enough to be run on every change.
- Feature flags for incomplete work. Work that is not yet ready for users is gated behind a flag rather than held on a branch. The flag-controlled code lives on trunk and is exercised by every build.3
- Branch by abstraction for larger changes. When a change is too large to fit into a small PR, the team introduces an abstraction layer that lets old and new implementations coexist, then migrates incrementally rather than in one cutover.
- Pull requests as conversation, not gate. When PRs are small and integration is frequent, review becomes a fast conversation about a focused change rather than a multi-day inspection of a sprawling delta.
Common Objections and the Realities Behind Them¶
- "We need to keep unfinished work isolated." That is what feature flags are for. Hiding incomplete work behind a flag is cheaper than hiding it on a branch.
- "Our tests are too slow to run on every change." Slow tests are a quality problem regardless of the branching model. The right response is to make tests fast, not to integrate less often.
- "We need to keep certain changes out of production until a specific date." Feature flags solve this too. Code merged to trunk is not necessarily code released to customers; that decision is independent of integration.
- "It's risky to commit half-finished work." Half-finished work behind a flag, exercised by the full pipeline every day, is much safer than half-finished work on a branch that has not been integrated in two weeks.
- "We need long-lived branches for parallel releases." This is occasionally true, particularly for products with multiple supported versions. The pattern there is short-lived feature branches off a release branch, not long-lived feature branches off main.
What This Looks Like in Practice¶
- Aim for daily integration. If a typical change does not land on trunk within a day, the team is operating closer to long-lived-branch mode than to trunk-based.
- Make the build pipeline fast. The acceptable feedback loop for a CI run is minutes, not hours. Investment in test speed and parallelization pays for itself many times over.
- Use feature flags routinely, not exceptionally. A team that treats feature flags as a normal part of writing code finds that long-lived branches stop being necessary.3
- Decompose changes more aggressively. The first move when a PR feels too large is to ask whether it can be split into two PRs that ship one after the other. The answer is almost always yes.
- Watch the age of open branches. Branches that have been alive for more than a few days are early warning signs that the integration discipline is slipping.
Key principle
Smaller changes reduce risk. The team that integrates often, in small pieces, has tighter feedback loops, smaller failure modes, and a lower cost per change than the team that batches work into large branches and large merges.
See also: Feature Flags, Deployment Strategies, Quality Is Designed In, Architecture Is About Change.
-
Nicole Forsgren, Jez Humble, and Gene Kim, Accelerate: The Science of Lean Software and DevOps (IT Revolution Press, 2018). The empirical research on engineering performance, including the strong and consistent finding that high-performing teams integrate work into trunk frequently in small batches and use short-lived branches. ↩
-
Jez Humble and David Farley, Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation (Addison-Wesley, 2010). The canonical text on the pipeline practices that make trunk-based development practical, including continuous integration, deployment automation, and the discipline of keeping the trunk releasable at all times. ↩
-
See Feature Flags for the longer treatment. ↩↩
-
Paul Hammant and contributors, trunkbaseddevelopment.com. The canonical practitioner-maintained reference for the practice, including the distinction between "scaled trunk-based development" (with very short-lived feature branches reviewed and merged within a day) and direct-to-trunk commits, and the operational requirements for each: https://trunkbaseddevelopment.com. ↩