Branching strategy
Trunk based development with feature flags: branching strategy, vs gitflow, vs feature branches
Every guide tells you to merge to trunk daily and keep trunk releasable. Almost none of them are honest that those two rules contradict each other unless something hides unfinished work from users. This page covers the actual DORA criteria, where the model breaks, and the three techniques that make it survivable.
The short answer
Trunk based development is a branching model where everyone commits to one shared branch and long-lived branches are deliberately avoided. DORA states the criteria precisely: three or fewer active branches, merged to trunk at least once a day, and no code freezes and no integration phases. Compared with gitflow, it does not remove merge pain so much as spread it into many small pieces instead of one large one before each release. The part most guides underplay: keeping trunk releasable while merging unfinished work daily is only possible if something decouples merging from releasing. That something is a feature flag, and for changes too big to flag, branch by abstraction. Adopt the branching rule without those two techniques and you get an unstable trunk.
Last updated July 2026 · criteria verified against dora.dev and trunkbaseddevelopment.com
01 · The definition, not the vibe
Trunk based development has measurable criteria
Most write-ups describe trunk based development as a philosophy. DORA, whose research on the practice draws on the 2016 and 2017 State of DevOps reports, states it as a checklist you can audit your repository against this afternoon.
| Criterion | The threshold | How to check it |
|---|---|---|
| Active branches | Three or fewer in the repository | git branch -r --no-merged origin/main, then count what has moved this week |
| Merge frequency | Every branch merges to trunk at least once a day | Median age of merged pull requests. If it is over 24 hours you are not there yet. |
| Branch lifetime | Typically hours, from one developer workstation | Time from first commit on the branch to merge, at the 90th percentile |
| Code freezes | None, and no separate integration phase | Does your calendar contain a date after which people stop merging? That is a freeze. |
| Trunk state | Releasable at all times | Could you cut a release from this commit right now without asking anyone? |
The last two are where most teams fail, and they fail together. A team that cannot cut a release at any moment introduces a freeze to make releases safe, and the freeze is what tells you unfinished work is reaching trunk unprotected.
02 · Two legitimate variants
Committing straight to trunk is not the only version
A common objection is that trunk based development means no pull requests and no code review. It does not. The canonical description includes a scaled variant built specifically for teams that need review.
Variant A
Commit directly to trunk
Developers push straight to main. A build server verifies each commit did not break the build. This works for small, high-trust teams with strong test coverage, and it is the purest form of the model.
- Best for teams of roughly one to eight developers.
- Requires a fast, trustworthy test suite. A red trunk blocks everyone at once.
- Review happens after the fact, or in pairs while the code is written.
Variant B · the common one
Scaled trunk based development
Short-lived feature branches exist purely for code review and CI verification, then merge. The canonical guidance describes them as the product of a single developer workstation, whether written solo, paired or mobbed. They are never used to build release artifacts.
- This is what most companies mean when they say they do trunk based development.
- The branch is a review vehicle with a lifetime measured in hours.
- It is still trunk based development because the branch never becomes a parallel line of development.
- The larger the team, the shorter the branches need to be, not the longer.
The scale objection, answered
"This cannot possibly work at our size"
It is usually raised by teams of forty. trunkbaseddevelopment.com documents Google operating with roughly 35,000 developers and QA automators in a single monorepo trunk, and trunk based development has been adopted in healthcare, finance and other heavily regulated industries where an unreviewed change reaching production is a serious matter. Scale does not break the model. What breaks at scale is committing straight to trunk without verification, which is why variant B exists.
03 · Against the alternatives
Trunk based development vs gitflow vs GitHub flow
These models differ mainly in how long code sits unintegrated and where the risk collects. Note that gitflow's own author has publicly walked back its use for continuously delivered web applications, while still considering it reasonable for versioned software with multiple supported releases.
| Dimension | Trunk based development | Gitflow | GitHub flow |
|---|---|---|---|
| Long-lived branches | One (trunk) | Two or more (main and develop), plus release and hotfix lines | One (main) |
| Branch lifetime | Hours | Days to weeks | Hours to days |
| When integration pain hits | Continuously, in small pieces | At the end, in one large piece | Per pull request |
| Releases from | Trunk, or a just-in-time release branch | A dedicated release branch | Main, on merge |
| Needs feature flags | Yes, structurally | No, the branch hides the work instead | Usually, for anything multi-day |
| Fits continuous deployment | Designed for it | Fights it | Yes |
| Fits versioned or on-premise software | Workable with release branches | This is its strongest case | Poorly |
| Main failure mode | Flag debt and a red trunk blocking everyone | Merge hell and long-lived divergence | Unreviewed work reaching main quickly |
| Team size ceiling | None demonstrated | None, but cost grows with parallel lines | Works best under strong CI |
If your decision is specifically between holding a branch open and hiding work behind a toggle, we compared those two directly in feature flags vs feature branches.
04 · The load-bearing part
Merging daily and staying releasable only works if the two are decoupled
Here is the contradiction at the center of every trunk based development guide. Rule one says merge unfinished work to trunk every day. Rule two says trunk must be releasable at every moment. Both cannot hold unless merging a change and exposing it to users are separate events.
A feature toggle is what separates them. The code ships dark, disabled by default, and the decision to expose it becomes a runtime change rather than a deployment. That is also what turns a rollback into a kill switch you flip in seconds instead of a revert, a rebuild and a redeploy.
// Merging to trunk daily does NOT mean shipping daily.
// The flag is what separates "integrated" from "released".
import { toggled } from '@toggled/sdk';
export async function checkout(cart: Cart, user: User) {
// New pricing engine is merged to trunk and running in CI,
// but only staff see it until the rollout starts.
const useNewPricing = await toggled.isEnabled('pricing-engine-v2', {
userId: user.id,
attributes: { plan: user.plan, isStaff: user.isStaff },
});
const total = useNewPricing
? await pricingV2.total(cart) // <- unfinished last Tuesday, still merged
: await pricingV1.total(cart); // <- what customers get until we flip
return total;
}
Once the flag exists, exposure becomes a dial rather than a switch: staff first, then one percent, then the rest. That progression is covered in detail in our guide to running a gradual rollout, and the deployment-level equivalent in canary deployment.
05 · Where it actually breaks
Four failures that get blamed on the branching model
None of these are arguments against trunk based development. They are the things you have to solve before the branching rule can hold, and teams that skip them conclude the model does not work.
Wall 1
A red trunk blocks the entire team
When everyone shares one branch, one broken merge stops all of them. This is the real prerequisite and it is not a branching problem, it is a test speed and test reliability problem. If your suite takes forty minutes and fails intermittently, fix that before you change your branching model, because trunk based development converts flaky tests from an annoyance into an outage.
Wall 2
Some changes are too big to hide behind a flag
Replacing an ORM, a payment provider or an authentication system does not fit in an if statement. The answer is branch by abstraction: introduce a seam over the current implementation, build the replacement behind that seam, move traffic across gradually, then delete the old path. It keeps the change in trunk without keeping a branch open for six weeks.
Wall 3
Database changes do not roll back like code
A flag flips in a second. A migration that dropped a column does not. The discipline is expand and contract: add the new structure, write to both, migrate reads behind the flag, and only drop the old structure once the flag has been fully on long enough that you will not need to reverse it. Deploy schema changes and the code that depends on them separately.
Wall 4
Flag debt accumulates faster than anyone removes it
This is the most common real complaint, and it is legitimate. Every flag is a branch in your code that must be tested in both states. Give each flag an owner and an expected removal date at creation, and treat a stale release flag as a bug rather than a housekeeping task. GitHub built a script using git grep and rubocop-ast that deletes the dead branches of retired flags and opens the pull request automatically, which tells you how seriously the problem needs taking.
06 · Getting there
How to move from feature branches without a big bang
Do not announce a branching policy change on a Monday. The branching rule is the last step, not the first, because it is the only one that fails loudly if the groundwork is missing.
Step 1
Make the suite fast and trustworthy
Measure how long CI takes and how often it fails for reasons unrelated to the change. Until a developer can merge and know within a few minutes whether they broke trunk, sharing a branch is unsafe. Quarantine flaky tests rather than tolerating them.
Step 2
Introduce flags while still on branches
Add a flag system and use it on two or three real changes before touching the branching model. The team learns the pattern while the old safety net is still in place, and you find out early which parts of your codebase are awkward to flag.
Step 3
Shrink the branches
Set an explicit expectation that a pull request opens and merges inside one working day. This forces work to be broken down smaller, which is the change that actually delivers the benefit. Most of the value of trunk based development arrives at this step, before you have changed anything structural.
Step 4
Delete the develop branch
Once branches genuinely live hours, the intermediate integration branch has nothing left to do. Removing it is anticlimactic if the previous steps worked, and revealing if they did not.
Step 5
Remove the code freeze
Replace the freeze with a release checklist and the ability to disable any in-flight feature. If somebody is nervous, that nervousness is pointing at a specific feature that is not properly flagged. Go and flag it.
Step 6
Put flag cleanup in the definition of done
A release flag that has been fully on for a month is dead code with a config entry. Assign an owner and a removal date when the flag is created, and audit the list monthly. This is the step everybody skips and later regrets.
The habits that keep this working long term, naming conventions, ownership, expiry and cleanup, are collected in our feature flag best practices guide. If you are still choosing a platform to do it with, the best feature flag tools comparison covers what each vendor charges and where each one stops.
07 · FAQ
Questions people actually search
What is trunk based development?
Trunk based development is a source control branching model where every developer commits to a single shared branch, usually called trunk or main, and long-lived development branches are deliberately avoided. DORA defines the practice concretely: three or fewer active branches in the repository, branches merged to trunk at least once a day, and no code freezes or integration phases.
What is the difference between trunk based development and gitflow?
Gitflow keeps several long-lived branches (develop, release, hotfix, plus a feature branch per change) and integrates them at the end. Trunk based development keeps one, and integrates continuously. The practical difference is when you feel merge pain: gitflow defers it to a big merge before release, trunk based development spreads it across many small merges that each take minutes.
Do you need feature flags for trunk based development?
For anything beyond a very small team, yes. If unfinished work merges to trunk daily and trunk must stay releasable, something has to keep that work from reaching users. A feature flag is that something. Without flags your only options are to hold the branch open (which is no longer trunk based development) or to ship half-built features.
Is trunk based development the same as continuous integration?
They are closely related but not identical. Continuous integration is the practice of merging and verifying work frequently. Trunk based development is the branching model that makes real continuous integration possible, because you cannot continuously integrate into a branch nobody else is on. In practice teams adopt them together.
How long should branches last in trunk based development?
Hours, not days. trunkbaseddevelopment.com describes short-lived feature branches as the product of a single developer workstation, opened for code review and CI verification and then merged. DORA sets the outer bound at one day. Once a branch lives longer than that, it starts accumulating the merge conflicts the model exists to prevent.
Can large teams use trunk based development?
Yes, and the largest examples are the most cited ones. trunkbaseddevelopment.com notes Google runs roughly 35,000 developers and QA automators in a single monorepo trunk. Scale changes the mechanics rather than the model: bigger teams use short-lived review branches and stronger automated verification instead of committing straight to trunk.
What is branch by abstraction?
Branch by abstraction is the technique for changes too large to hide behind a simple flag, such as replacing a database layer or a payment provider. You introduce an abstraction over the existing implementation, build the new implementation behind it, switch traffic across gradually, then delete the old one. It replaces a long-lived branch with a long-lived seam in the code.
What are the disadvantages of trunk based development?
It shifts cost from merging to discipline. You need fast automated tests, because a broken trunk blocks everyone. You need flag hygiene, because flags accumulate faster than teams remove them. Code review has to happen in hours rather than days. Teams that adopt the branching model without those three things usually end up with an unstable trunk and blame the model.
Early access · launching soon
Merge daily. Release when you are ready.
Unlimited flags and seats, one evaluation core across every language, and a kill switch that works in seconds. Join the early-access list, no card required.
No card required · your data stays yours
Keep exploring
More feature flag resources
- Canary Deployment Made Simple With Feature Flags
- Feature Toggle Management for Engineering Teams
- Gradual Rollout
- Feature Flag Kill Switch
- React Feature Flags
- Next.js Feature Flags
- Node.js Feature Flags for APIs and Backends
- Python Feature Flags for Django, Flask and FastAPI
- Go Feature Flags With Sub-Millisecond Checks
- Feature Flags Platform
- Managing Feature Flags
- Feature Flag Tools