[object Object]

What Is Trunk-Based Development? Benefits, Workflow, and Best Practices

Trunk-based development means merging small changes to one shared branch every day. Learn how the workflow scales, best practices and trade-offs.

POSTED ON SEPTEMBER 15, 2026

Every engineering team hits the same wall. A feature branch that started life two weeks ago finally gets merged, and suddenly nothing builds. Dozens of files clash. Someone renamed a function the branch still calls. The whole afternoon is gone, lost to merge conflicts nobody caused on purpose. Teams call this “merge hell”.

Trunk-based development exists to save that afternoon. Rather than hoard changes in separate branches for weeks, developers merge small updates into one shared branch every day. The teams that top the industry’s delivery benchmarks work this way, and so do the biggest engineering shops on the planet.

What is trunk-based development?

Trunk-based development is a version control practice where developers merge small, frequent code changes into a single shared branch, usually called trunk or main. The defining rule: every developer integrates their work into that shared branch at least once every 24 hours.

That daily rhythm is the whole point. A branch that lives for a few hours barely has time to drift from the mainline. A branch that lives for three weeks drifts so far that a painful merge is all but certain.

There are two approaches. Small teams often commit straight to trunk. Larger teams use short-lived branches that open a pull request and merge back within hours. Both share the same commitment to avoiding long-lived branches.

Trunk-based development isn’t new. Early version control systems like CVS and Subversion worked this way by default. A shared mainline was simply how you worked together. The term came roaring back as a pushback against heavier branching models, GitFlow chief among them, that piled on extra branches and slowed merging down.

Today the practice sits at the base of continuous integration and continuous delivery. You can’t really claim to do CI while your developers work alone for a week at a stretch. Running builds on branches that rarely touch main is sometimes called “CI theater.” The dashboards are green, but the merging keeps getting put off.

Trunk-based development vs. GitFlow and GitHub Flow

Branching strategy is a spectrum, and trunk-based development sits at the fast, low-isolation end. Seeing it next to the alternatives makes the trade-offs concrete.

DimensionTrunk-Based DevelopmentGitFlowGitHub Flow
Mainline branchesSingle main / trunkDual long-lived: main and developSingle main
Branch lifespanHours, under 24 (or direct to trunk)Weeks to months per featureDays to weeks per feature
Integration frequencyMultiple times per dayAt feature or release completionOn feature completion
Primary release sourceTrunk or short-lived release branchesmain via dedicated release branchesmain after PR merge
Conflict resolutionFrequent, small, low-riskInfrequent, large, high-riskModerate
Production decouplingHigh (feature flags, branch by abstraction)Low (relies on branch isolation)Moderate (environment isolation)
Best fitSaaS, microservices, CI/CD, enterprise appsPackaged software, multi-version supportWeb apps with simple release cycles

GitFlow’s main weakness in a continuous delivery setting is its pair of long-lived branches, develop and main. That layout forces teams to merge in stages across environments. As the branches drift apart, the conflicts pile up faster than the calendar would suggest.

GitHub Flow lands in the middle. It keeps a single main branch and merges through pull requests, which is closer to trunk-based development’s spirit. The gap is discipline around branch lifespan. Flow doesn’t cap how long a feature branch can live, so a team can technically follow it while still letting branches age for weeks.

What a trunk-based development diagram looks like

The two models look completely different on a timeline, and the shapes tell the story better than any paragraph.

Diagram showing trunk based development vs non-trunk based development

Trunk-based development is one dominant line with short branches feeding in and rejoining within hours, plus a release branch peeling off at ship time.

Non-trunk-based development is several long parallel branches weaving over and under each other, meeting at big knotty merge points. Those knots are where conflicts, regressions, and code-freeze periods pile up, so the visual density of merge points is a decent proxy for how much integration pain a strategy causes.

How the trunk-based development workflow scales

The workflow isn’t one-size-fits-all. It bends around team size, commit volume, and risk tolerance. Broadly, three modes cover most organizations.

WorkflowTeam sizeBranch lifespanCode reviewIntegration gate
Direct to trunk1–5 devsNonePair / mob programmingLocal pre-commit + post-push CI
Short-lived PRs5–500 devs1–24 hoursAsync or sync PR reviewPre-merge CI build + review
Automated merge queues500+ devs (monorepo)Under 4 hoursStacked diffs / botsSpeculative build queue + bot merge

Small teams: committing straight to trunk

With two to five developers, ceremony gets in the way. These teams usually push straight to the shared trunk without pull requests. A tight daily rhythm keeps the mainline stable:

  • Start the day by syncing. Pull the latest trunk head so your local copy matches everyone else’s before you write a line.
  • Run local checks before pushing. Compile, run unit tests, verify integration locally. The trunk only sees code that already passed on your machine.
  • Commit in small increments all day. Frequent small pushes stop your local copy from drifting away from the remote.
  • Review in real time. Pair or mob programming handles code review as the work happens, which removes the need for asynchronous pull request gates entirely.

The push triggers CI on the server, which confirms the mainline is still green. Simple, fast, and it works precisely because the group is small enough to hold context in their heads.

Larger teams: short-lived branches and pull requests

Once you scale past a handful of developers, direct commits to main buckle under the load. Too many people pushing at once turns build checks into a bottleneck. Scaled trunk-based development solves this with short-lived branches and pull requests.

A developer branches off trunk, adds one to three focused commits over a few hours, and opens a pull request. That PR becomes the integration gate: automated tests run, a teammate reviews, and once both pass the branch merges into trunk and gets deleted immediately. Key habits keep this humming:

  • Branches live hours, not days. A hard cap of 24 hours, ideally far less.
  • One developer owns a branch. No nested dependencies between branches, which keeps merges clean.
  • Reviews get prioritized. Teams treat code review as urgent work measured in minutes and hours, not days.

Massive scale: automated merge queues

At hundreds or thousands of developers sharing a monorepo, humans can’t merge fast enough to keep the build green. Automated merge queues take over. They test each candidate pull request against the very latest trunk head before merging, so even under a firehose of commits the mainline stays deployable.

Google is the classic example. Tens of thousands of engineers commit into one shared trunk all day long. Automated systems run millions of test suites against speculative commits before they land.

Releasing from trunk

Two release patterns dominate, and which one you pick depends on how you ship.

Release directly from trunk. In fast-moving SaaS and continuous deployment shops, every commit that passes its tests on trunk is ready to ship. So ship it. If a bug slips through, you fix forward: commit the fix to trunk and roll it out, rather than rolling back. This works when you control the deploy and can push a fix in minutes.

Release from branches off trunk. Packaged software, mobile apps, and enterprise systems that support multiple versions need a different approach. A release branch splits off trunk at a milestone, and hardening, testing, and final tagging happen on that branch. The critical rule: release branches never merge back into trunk. If hardening surfaces a bug, the fix goes into trunk first, then gets cherry-picked down to the release branch. That one-way flow prevents branch contamination and the tangled merge loops that plague GitFlow.

Technical practices that make it possible

Merging unfinished work into a shared branch every day sounds reckless until you see what makes it safe. Three practices carry most of the weight.

Feature flags

Feature flags separate deploying code from releasing a feature. You wrap an in-progress code path in a conditional that checks a flag: if the flag is on, the new logic runs; if it’s off, the system falls back to the stable path. That lets half-built features live in production, merged into trunk, invisible to users until you’re ready.

Martin Fowler sorts feature toggles into four types, and knowing which kind you’re dealing with keeps your flag system from turning into a swamp.

Toggle typeLongevityDynamismPurposeOwner
ReleaseDays to weeksStatic / build-timeHide incomplete code during daily mergesDevelopers
ExperimentWeeks to monthsPer request / user segmentA/B testing, feature validationProduct / data science
OpsSystem lifetimeRuntime reconfigurableCircuit breakers, load fallbackSRE / ops
PermissionSystem lifetimePer authenticated userAccess tiers, early accessProduct ops / admins

Branch by abstraction

Some changes are too big for a simple flag, like swapping out a database framework or rewriting an encryption core. Branch by abstraction handles those on the mainline without breaking anything.

You start by inserting an abstraction layer in front of the component you’re replacing. Then you route all the existing client code through that new interface, which still delegates to the old implementation underneath. With the abstraction stable, you build the replacement in small daily commits alongside the legacy code, using toggles inside the abstraction to send test traffic to the new path while production stays on the old one.

Once the new component passes every test for behavior and speed, you flip production over for good and delete the old code and the abstraction layer. Big rewrite, no big-bang branch.

Fast automated testing

None of this holds together without tests. Merging to trunk daily demands continuous verification, and that verification has to be fast. Commit builds should compile, run unit tests, and validate contracts inside a ten-minute window. Slower than that, and developers start batching changes to avoid the wait, which quietly unravels the whole model.

Test-driven development pairs naturally with trunk-based development. Writing tests before code forces you to break problems into small, mergeable pieces and shrinks the feedback loop to seconds. And when a commit does break the build, discipline says fix it or revert within ten minutes to get trunk back to green.

Advanced pipelines go further with automated fitness functions. These check things like test coverage, security holes in dependencies, and code quality. If a change fails the bar, the merge is blocked before it ever reaches the mainline.

Benefits of trunk-based development

Integration stops being scary. The main win is batch size. Conflicts scale non-linearly with branch age: a branch with three weeks of drift can touch dozens of files and create a cross-file conflict matrix nobody wants to untangle by hand. A change merged within 24 hours usually touches a handful of files, turning conflict resolution into a two-minute job. Shrink the batch, shrink the pain.

Code review gets faster and better. Small commits are easy to read. A reviewer can actually reason about ten changed lines, whereas a 2,000-line pull request gets skimmed, rubber-stamped, and shipped with defects intact. Small batches make review a real quality gate instead of a formality.

The mainline stays deployable. Because every commit passes automated tests before landing, trunk stays green and ready to ship at any moment. That gives teams the option to deploy on demand, even multiple times a day.

The DORA metrics move in the right direction. DORA has studied how thousands of teams ship software. Time and again, trunk-based development is one of the habits that sets the best teams apart.

DORA metricEffect of trunk-based developmentWhy
Deployment frequencyHigher (multiple times/day)Mainline is always releasable
Lead time for changesLower (under an hour)No long review queues or complex merges
Mean time to restoreLower (under an hour)Small batches make root causes obvious; fix-forward or flag-off is fast
Change failure rateLower (0–15%)Small changes have a small blast radius; tests catch errors early

Challenges of trunk-based development

Feature flag sprawl. You accumulate tech debt if you lean too hard on flags. Every active flag doubles the theoretical test matrix, and forgotten flags leave dead code paths littered through the codebase. Instead, link every flag to a ticket, give it an owner and an expiration date, and treat removing the flag and its old code path as part of the feature’s definition of done. CI checks can break the build when a flag outlives its expiration date.

It demands a higher skill floor. This model moves quality checks earlier, onto developers. They need to write solid unit tests, use toggles well, and refactor in small steps. Teams with thin test coverage or less experience can turn direct commits into a stream of broken builds. Trunk-based development rewards discipline and punishes its absence.

People push back. Developers used to the safety of isolated branches feel exposed merging unfinished work to main. Managers worry about losing release control. Smooth transitions keep pull requests for review but cap branch lifespans hard. They also invest heavily in automated tests before pulling down any manual quality gates. A heavy review process that needs several sign-offs over days works against trunk-based development. It pushes developers straight back into big batches.

Distributed systems need coordination. Across microservices, or a split frontend and backend, shipping a half-built feature that spans services can cause brief failures when API contracts drift apart. The fix is to keep every contract change backward-compatible with an expand-and-contract pattern: add the new field or endpoint next to the old one, move traffic over, then retire the old one. A shared feature flag platform keeps flag values in sync across every service.

A phased adoption roadmap

Teams migrating off GitFlow or long-lived branches tend to move through four phases:

1. Build the test and CI foundation. Stand up unit tests around core logic, wire CI gates to run on every commit, and add pre-commit hooks and static analysis to catch problems locally before they’re pushed.

2. Speed up builds and reviews. Parallelize the CI pipeline to finish inside ten minutes. Set review SLAs and keep pull requests small (under 200 lines) so reviews wrap in hours, not days.

3. Standardize feature flags. Roll out a shared flagging tool with rules for who owns each flag, when it expires, and how it gets removed, so teams can merge half-built features safely.

4. Enforce short branches and continuous deployment. Cap every branch at 24 hours, retire the long-lived development branches, make trunk the single source of truth, and connect continuous delivery pipelines straight to it.

I’ts important to do it in this order. Enforcing 24-hour branches before you have fast tests and quick reviews just breaks the build.

Frequently asked questions

How is trunk-based development different from feature branch development, and how long can a branch live?

The split is scope and lifespan. Feature branch development isolates a chunk of work, often across several developers, for days or weeks before merging it back. Trunk-based development caps any branch at 24 hours (shorter is better, and small teams often skip branches entirely), so work merges into the shared branch in small pieces.

Feature branches favor isolation, so they push conflicts into one large, risky merge. Trunk-based development favors integration, so it spreads those conflicts across many tiny, cheap merges. Once a branch outlives a day it drifts from the mainline and the merge stops being trivial, which defeats the purpose.

How do you handle unreleased or incomplete features in trunk-based development?

Feature flags for most work, branch by abstraction for large architectural changes. Both let unfinished code live on trunk and integrate continuously while staying dormant until it’s ready. The Feature flags and Branch by abstraction sections above cover the mechanics.

How does trunk-based development relate to continuous integration and CI/CD?

They lean on each other. Trunk-based development is the branching part: merge small changes to a shared trunk every day. Continuous integration adds a fast test suite that runs after every commit. Continuous delivery goes one step further and keeps the trunk ready to ship at all times. You can’t do real CI while developers work alone for a week, so trunk-based development is treated as a must-have for it.

How do you prevent breaking the trunk (main) branch?

A few layers stack up before a commit ever lands. Developers run compile and unit tests locally before they push. CI runs a fast build on every commit. Protected branches or merge queues block anything that fails from merging. If a break still slips through, the ten-minute fix-or-revert rule from the automated testing section kicks in to restore a green trunk.

Can trunk-based development work with GitHub?

Yes, and it’s common. Teams use short-lived branches, open pull requests, and rely on GitHub’s protected branches to require passing checks before a merge. Automated status checks and merge queues on GitHub map directly onto the scaled workflow, which is why GitHub trunk-based development is a well-worn path rather than an exotic one.

Conclusion

Trunk-based development trades the false comfort of isolated branches for the real safety of continuous, small-batch integration.

Cap branch lifespans, keep the mainline ready to ship, and lean on feature flags and fast tests.

You need fast automated testing, disciplined flag hygiene, and strong CI. If you meet those, the practice scales from a two-person startup to a monorepo shared by thousands without changing its core logic. This is why it’s become the default branching model for high-performing engineering teams.

Henry Ameseder

AUTHOR

Henry Ameseder

Henry is the COO and a co-founder of Mimo. Since joining the team in 2016, he’s been on a mission to make coding accessible to everyone. Passionate about helping aspiring developers, Henry creates valuable content on programming, writes Python scripts, and in his free time, plays guitar.

Learn to code and land your dream job in tech

Start for free