Trunk-Based Development
Trunk-based development is one branch, short-lived feature work, and a main that is always shippable. The model is simple. The plumbing that makes it work in a real engineering org is what teams skip and regret. This guide covers both.
In one paragraph
Everyone commits small, frequent changes to one shared branch. Branches live hours, not weeks. Main has to stay green or the model collapses, so trunk-based development is paired with feature flags, fast CI, and a merge queue. Used by Google, Meta, Netflix, and most modern SaaS teams. Backed by DORA as one of the practices that separates high-performing engineering teams from low-performing ones.
The problem trunk-based development solves
flowchart LR M1["main"] M2["main"] M3["main<br/>BROKEN"] FA1["feature/A"] FA2["feature/A"] FA3["feature/A"] FB1["feature/B"] FB2["feature/B"] FB3["feature/B"] M1 --> M2 --> M3 M1 --> FA1 --> FA2 --> FA3 --> M3 M1 --> FB1 --> FB2 --> FB3 --> M3 style M3 fill:#FDECEA,stroke:#E53935,color:#1A1D24 style FA3 fill:#FFF4E5,stroke:#F27B2A,color:#1A1D24 style FB3 fill:#FFF4E5,stroke:#F27B2A,color:#1A1D24
Two long-lived branches running in parallel for a few weeks. Each one is internally fine. Reintegrating them into main is where the cost lives.
The classic alternative to trunk-based development is feature branching: an engineer (or a sub-team) takes a branch, works on it for a week or two, then merges back. It looks clean from inside the branch. The cost shows up at the merge boundary.
By the time a two-week branch comes back to main, hundreds of unrelated commits have landed. Functions have been renamed, APIs changed, dependencies bumped. Git resolves the textual conflicts. Nobody resolves the semantic ones. Main breaks, the branch author spends a day debugging code they did not write, and the team coins the term "merge week."
The cost is not linear in branch lifetime, it is exponential. A two-day branch is roughly a regular merge. A two-week branch is a project. A two-month branch is the kind of thing teams quietly abandon.
Trunk-based development cuts the problem at its source. Branches do not survive long enough for the world around them to change. The integration cost stays small because integration happens constantly.
How trunk-based development works
flowchart LR M0["main"] M1["main"] M2["main"] M3["main"] M4["main"] PR1["PR #1<br/>2h"] PR2["PR #2<br/>4h"] PR3["PR #3<br/>1h"] PR4["PR #4<br/>3h"] M0 --> PR1 --> M1 M1 --> PR2 --> M2 M2 --> PR3 --> M3 M3 --> PR4 --> M4 style M0 fill:#E6F8F2,stroke:#1CB893,color:#1A1D24 style M1 fill:#E6F8F2,stroke:#1CB893,color:#1A1D24 style M2 fill:#E6F8F2,stroke:#1CB893,color:#1A1D24 style M3 fill:#E6F8F2,stroke:#1CB893,color:#1A1D24 style M4 fill:#E6F8F2,stroke:#1CB893,color:#1A1D24
One main branch. Short-lived PRs land throughout the day. Main stays green between every merge.
The workflow itself is unremarkable. An engineer pulls main, branches off for a few hours, opens a PR, gets review, runs CI, merges. The next morning they pull main again. Most engineers who do this for the first time find the workflow itself simpler than what they were doing before.
What makes it work in practice is a small set of practices that have to be in place. Skip any of them and trunk-based development quietly degrades back into feature branching with a different name.
Short-lived branches
Branches that live longer than one day are a smell. The DORA research draws the line at 24 hours, and high-performing teams in the survey are well under that. Anything bigger gets split: into stacked PRs (link a chain of dependent branches together), into a refactor PR followed by a feature PR, or into shippable behind-a-flag steps.
Feature flags
For any change bigger than a few hours of work, the code lands behind a flag. The flag is off in production, on for the engineers shipping the feature, and gradually rolled out to users when the work is done. Without flags, trunk-based development falls back into long-lived branches because engineers hold their work locally until everything is done. With flags, the branch lifetime is decoupled from the feature lifetime, which is the entire point.
Branch by abstraction
Feature flags do not work for refactors. You cannot put a flag on "the new database client". The pattern is branch by abstraction: introduce an interface, run the old and new implementations side by side behind it, switch traffic over, then delete the old code. Each step is a small PR. None of them require a long-lived branch.
Fast CI
If CI takes 45 minutes, nobody is going to merge four times a day. The CI pipeline has to fit inside the cycle of "make a change, push, wait, merge." Around 10 to 15 minutes is the upper bound where most teams stay productive. Beyond that, parallelization, selective testing (run only the tests affected by the change), and two-step CI are the levers.
A green main, enforced
This is where teams trip. With everyone merging into one branch all day, two PRs that each pass CI in isolation can break main when they land together. A function gets renamed in PR #1 while PR #2 adds a caller. Both pass their own CI. Main is red the moment the second one merges. The fix is a merge queue that tests each PR against the future state of main before it merges. Without one, trunk-based development at any reasonable team size becomes a permanent firefighting exercise.
See the Mergify merge queue docs for what the configuration looks like in practice.
What a trunk-based day looks like
A simplified view of one engineer's day on a trunk-based team:
| Time | Action | State of main |
|---|---|---|
| 09:00 | Pull main, branch feat/billing-ui-1 | Green |
| 10:30 | Push, open PR, CI passes | Green |
| 11:15 | Review approved, queued | Green |
| 11:35 | Merged behind flag billing_v2 | Green |
| 11:40 | Pull main, branch feat/billing-ui-2 | Green |
| 14:30 | Push, review, merge | Green |
| 17:00 | Sign off. Feature ships dark, behind flag. | Green |
Two PRs in one day, both small, both reviewable in under fifteen minutes, both merged behind a flag that protects users from the half-finished feature. Tomorrow, the engineer pulls main and continues. There is never a "merge week." Integration is just what mornings look like.
Going deeper
The model is one branch. The interesting questions are how to keep branches short, how to ship unfinished work safely, and how trunk-based development compares to the alternatives most teams started with.
Trunk-based development vs Gitflow
Two models, two release cadences, two eras of software. When Gitflow made sense, why most teams now skip it, and the migration path.
Read more →Trunk-based vs feature branches
Feature branches live for weeks. Trunk branches live for hours. The cost difference shows up in merge conflicts, integration time, and the size of your release blast radius.
Read more →Short-lived branches in practice
How to ship work that does not fit in a single day. Stacked diffs, branch by abstraction, and the patterns that keep branch lifetimes under 24 hours.
Read more →Feature flags and trunk-based development
Why trunk-based development needs feature flags to stay honest. Flag types, flag debt, and how to retire them before they become permanent config.
Read more →The two blockers most teams hit
When a team tries trunk-based development and it does not stick, it almost always comes down to two things.
"Our CI takes too long"
If CI takes 45 minutes, nobody merges four times a day. The fix is to make CI faster, and the levers are well known. Selective testing (only run tests affected by the change) typically cuts CI time by 50 to 80 percent on a monorepo. Two-step CI runs lightweight checks on every PR and the full suite only inside the merge queue. Parallel queues let a frontend PR merge without waiting on a backend one. None of these require rewriting CI from scratch.
"We can't ship half-finished features"
This is what feature flags are for. If the product team is uncomfortable with code landing in main before the feature is done, the answer is dark launches: the code is in main, the flag is off, no user sees it. When the feature is ready, the flag turns on for 1 percent of users, then 10, then everyone. The branch never has to live for a month, because the flag does the work the branch was doing.
Both of these are infrastructure problems, not workflow problems. Asking engineers to "be more disciplined about merging" without fixing them does not work.
Should your team adopt this?
For most modern software, yes. The exceptions are real but narrow.
Trunk-based development pays off
- ● 10+ engineers touch the same repo and feature branches keep colliding at merge time.
- ● You ship to production daily or weekly, not on a release calendar.
Either one and trunk-based development pays for itself within a sprint.
Stick with what you have
- ○ Solo project or two-person team with no merge contention.
- ○ Software you actually ship on a calendar (regulated, embedded, app store with long review).
Long-lived release branches exist for real reasons. Do not adopt trunk-based development just because it is fashionable.
FAQ
What is trunk-based development?
Trunk-based development is a source control model where every engineer commits small, frequent changes to a single shared branch (usually main or trunk). Branches live hours, not weeks. The model relies on a few practices working together: short-lived branches, feature flags so unfinished work can ship hidden, fast CI, and a mechanism that keeps main green even when many engineers merge in parallel.
What is the difference between trunk-based development and Gitflow?
Gitflow uses long-lived develop, release, and feature branches and a scheduled release cadence. Trunk-based development uses one shared branch and continuous integration. Gitflow was designed for software shipped on a calendar (boxed software, mobile apps with store review). Trunk-based development is designed for software shipped continuously, which is most modern SaaS.
Is trunk-based development the same as continuous deployment?
No. Trunk-based development is about how you integrate code. Continuous deployment is about how you ship it to production. The two pair naturally because trunk-based development keeps main shippable at all times, but you can practice trunk-based development with weekly releases too.
How long should branches live in trunk-based development?
Hours, not days. The DORA research that backs trunk-based development as a high-performance practice defines short-lived as under one day. In practice, healthy teams aim for branches that open in the morning and merge by end of day. Anything multi-week is a sign the work was not split correctly or the team needs feature flags.
Can you do code review with trunk-based development?
Yes. Trunk-based development is sometimes confused with direct-to-main commits, which is a separate practice. Most modern trunk-based teams use short-lived branches with pull requests, code review, and CI checks before merge. The constraint is that the review cycle has to be fast (hours, not days) or branches start piling up.
Does trunk-based development work for monorepos?
It works particularly well for monorepos, which is why most monorepo-heavy companies (Google, Meta, Uber) practice it. Monorepos amplify the cost of long-lived branches because the surface area for conflicts is huge. The pieces that have to be solid for a monorepo are selective testing (only run tests affected by the change) and a merge queue (so two green PRs cannot break main when merged together).
Do you need feature flags for trunk-based development?
For anything bigger than a few hours of work, yes. Feature flags are how you ship code that is not finished yet without exposing it to users. Without them, trunk-based development collapses back into long-lived branches because engineers hold their changes locally until everything is done. Branch by abstraction is the alternative for refactors that do not fit a flag.
Trunk-based development falls apart without a green main.
Mergify's merge queue is what keeps main green when the whole team is merging at once. The page customers usually start on.