Single Trunk Model Explained: Golden Branches, Feature Toggles, and Continuous Feature Delivery
Why modern teams keep one golden branch releasable, ship behind toggles, and handle hotfixes without GitFlow drama.

Abstract Algorithms
Helping engineers master software engineering topics.
TLDR: The single trunk model exists to reduce integration pain, shorten feedback loops, and keep one golden branch always releasable. Teams make it work with strong CI, short-lived feature branches, feature toggles, and a hotfix process that fixes trunk first instead of forking release history.
โ TLDR Summary: Why Teams Keep Returning to One Golden Branch
The single trunk model is the practical answer to a painful question: what happens when code stays apart for too long? The longer teams keep work on separate branches, the more they pay in merge conflicts, duplicated fixes, delayed feedback, and release anxiety.
That is why this model exists. It is not a Git ideology. It is a risk-reduction strategy.
The idea is simple:
- Keep one branch as the source of truth.
- Make that branch deployable at all times.
- Hide unfinished work with feature toggles instead of long-lived branches.
- Treat feature branches as temporary integration buffers measured in hours or days, not weeks.
- Apply hotfixes to trunk first, then patch deployed artifacts only if the situation truly requires it.
If you remember one thing, remember this: a golden branch is valuable only when the team can trust that anything merged into it has already passed the same quality gates production expects.
๐งญ Decision Matrix: When the Single Trunk Model Fits Best
| Situation | Recommended Approach | Why |
| Small team shipping daily | Single trunk plus direct merge or tiny PRs | Lowest coordination overhead and fastest feedback |
| Medium team with parallel work | Single trunk plus short-lived feature branches | Preserves review quality without long merge drift |
| Large product with risky unfinished work | Single trunk plus feature toggles and staged rollout | Lets you merge early without exposing half-built behavior |
| Regulated or high-availability system | Single trunk plus release tags and strict branch protection | Keeps history simple while preserving auditability |
| Mobile app with app-store lag | Single trunk plus runtime flags and kill switches | Decouples code deploy from feature release |
| Team still depending on multi-week branches | Start with branch lifetime limits before a full trunk shift | Reduces transition shock while moving toward continuous flow |
A lot of teams fail here because they ask, "Should we ban branches?" That is the wrong question. The right question is, "How quickly can unfinished work rejoin the main line safely?"
๐ Why the Single Trunk Model Exists at All
Traditional branching models came from a reasonable instinct: isolate change so unfinished work does not break everyone else. The problem is that isolation solves one risk by creating three others.
First, long-lived branches hide integration bugs until the most expensive moment, when several weeks of work collide with new trunk changes. Second, they create duplicate maintenance work, because bug fixes and refactors now have to be applied in multiple places. Third, they slow delivery because the branch becomes its own mini product line with its own test burden, release logic, and merge negotiation.
The single trunk model exists because software teams learned that integration is not a phase. Integration is the daily job.
That is where the phrase golden branch comes from. A golden branch is the branch the whole team trusts. It is not just the default branch in GitHub. It is the branch that should always be in a shippable state, protected by CI, tests, policy checks, and release discipline.
This is also why the model is tightly connected to Continuous Feature Delivery (CFD). In CFD, you do not wait for a giant release branch to accumulate enough value. You continuously move code toward production readiness and control visibility with flags, rollout rules, and operational safeguards.
๐ The Core Vocabulary Teams Need to Agree On
Before teams adapt this model, they need precise language. A lot of rollout confusion is really vocabulary confusion.
| Term | Practical meaning |
| Single trunk model | One main integration branch where the team converges continuously |
| Golden branch | The trusted, always-releasable version of trunk |
| Continuous Feature Delivery | Code can reach production continuously, while feature visibility is controlled separately |
| Feature toggle | A runtime switch that turns behavior on or off without branching history |
| Short-lived feature branch | A branch that exists briefly for review and cleanup, then merges back fast |
| Hotfix | An urgent production fix that must repair the live issue and preserve trunk as source of truth |
| Branch by abstraction | A technique for introducing big changes behind interfaces instead of long-lived code divergence |
Notice what is missing from that table: there is no rule saying feature branches are forbidden. Mature trunk teams still use them. They just manage them aggressively.
A healthy feature branch in this model usually has these properties:
- It is small enough to review in one sitting.
- It lives for hours or a couple of days, not multiple sprints.
- It merges back before trunk changes drift too far.
- It is backed by automated checks, not tribal trust.
That is the adaptation many teams miss. Single trunk is not "everyone commits recklessly to main." It is "everyone integrates early under strict safety rails."
โ๏ธ How Continuous Feature Delivery Works on One Branch
The mechanics matter more than the slogan. A team cannot adopt a golden branch by declaring one.
In practice, CFD on a single trunk usually works like this:
- A developer starts work on a small change, often in a short-lived branch.
- CI validates build, tests, linting, security scanning, and deployment packaging.
- The code merges into trunk quickly.
- Any incomplete user-facing behavior stays hidden behind feature toggles.
- The deployment pipeline continuously turns trunk into a releasable artifact.
- Product or engineering gradually enables the feature by environment, tenant, percentage, or role.
This is why feature toggles are central. They separate integration from release.
Without toggles, teams keep code off trunk because trunk equals immediate exposure. With toggles, teams can merge early while controlling when users actually see the behavior.
This also changes how feature branches are managed. In GitFlow-style thinking, a feature branch is where work "lives" until the team feels ready. In CFD, a feature branch is just a short staging area for review. The real home of the code is trunk.
That changes team behavior in useful ways:
- Scope gets cut smaller.
- Refactors get merged incrementally.
- Hidden integration bugs show up sooner.
- Release day becomes less dramatic because release is no longer a giant merge event.
๐ง Deep Dive: How Teams Actually Adapt to a Golden Branch
The cultural shift is usually harder than the Git change.
The Internals
Teams that succeed with a single trunk model usually redesign workflow in four internal layers.
1. Merge policy becomes operational policy. Protected branch rules, mandatory checks, code owners, and fast review SLAs stop being optional hygiene and become release controls.
2. Toggle ownership becomes explicit. Every feature flag needs an owner, a purpose, a default state, and a removal deadline. Teams that never clean up toggles eventually recreate the same complexity they were trying to remove from branching.
3. Work is sliced by behavior, not by epic. Instead of branching for "new checkout system," teams merge a new pricing interface, then a hidden implementation, then selective routing, then gradual rollout. The big feature is delivered as a sequence of safe integrations.
4. Hotfix rules are rehearsed before incidents happen. In a CFD team, the default rule is simple: fix trunk first. If production needs an immediate patch to an already deployed artifact, create the emergency patch from the released tag, but never let that patch become a parallel source of truth. The permanent history still flows back through trunk.
This is also where branch by abstraction becomes powerful. Instead of keeping a multi-week branch for a new payment flow, the team introduces an interface, keeps the old implementation live, merges the new implementation behind a toggle, and gradually moves traffic over. The codebase stays integrated even while the architecture changes. If you want the design principles behind those seams, the same move is easier to reason about through the lenses of the Open-Closed Principle and the Dependency Inversion Principle.
Performance Analysis
You can measure whether the model is working. The value is not philosophical; it shows up in delivery performance.
| Metric | What usually improves with single trunk |
| Lead time for changes | Smaller batches reach trunk faster |
| Merge conflict rate | Reduced because divergence window is shorter |
| Mean time to restore | Hotfix path is clearer when trunk remains the source of truth |
| Change failure rate | Flags and staged rollout lower blast radius |
| Review latency | Smaller PRs are easier to review quickly |
The main bottlenecks are different from branch-heavy teams:
- Weak CI turns trunk into a false promise.
- Poor toggle hygiene creates hidden complexity.
- Oversized pull requests recreate merge pain under a different name.
- Teams that keep long-lived branches "temporarily" end up running two models badly.
So the performance lesson is straightforward: the single trunk model works when batch size shrinks and safety automation rises at the same time. If only one of those happens, the model feels worse instead of better.
๐ Visualizing the Flow from Commit to Hotfix
The diagram below shows how a golden branch stays deployable while still allowing hidden work, gradual rollout, and urgent production repair. Read it left to right: work enters through a short-lived integration path, passes CI, lands on trunk, then gets exposed through toggles rather than branching history. The hotfix path loops back into the same trunk because the system stays maintainable only when one line of history remains authoritative.
flowchart TD
Dev[Developer change] --> Branch[Short-lived feature branch]
Branch --> Review[Review and CI gates]
Review --> Trunk[Golden trunk branch]
Trunk --> Toggle[Feature toggle default off]
Toggle --> Deploy[Deployable artifact]
Deploy --> Rollout[Progressive rollout]
Incident[Production incident] --> Fix[Hotfix commit]
Fix --> Review
Fix --> Patch[Optional emergency patch from release tag]
Patch --> Trunk
The important takeaway is that trunk remains the backbone of both normal delivery and incident response. Even when an emergency patch is cut from a release tag, the fix must return to trunk immediately so future releases do not regress.
๐ How Different Teams Adapt the Model in Real Life
A startup usually adopts this model first because speed hurts less when there is one place to integrate. Small teams can often begin with protected main, mandatory tests, and one simple feature flag service.
A platform team adapts it differently. Their challenge is not only speed but coordination. Multiple services, shared libraries, and migration waves make long-lived branches especially expensive. These teams benefit from branch-by-abstraction, compatibility windows, and contract testing.
A regulated enterprise team often assumes it cannot use single trunk because it needs auditability. In reality, a golden branch usually improves auditability because the history is simpler. Tagged releases, approval logs, code owners, and deployment evidence are easier to follow when the organization is not reconciling several semi-permanent release branches.
Mobile teams have a special case. They cannot always push a new binary instantly. That makes CFD even more useful. The app code can contain completed but hidden behavior, while runtime flags decide what users see after the binary is already installed.
โ๏ธ Where Teams Still Get Tripped Up
The most common misunderstanding is thinking the model removes the need for discipline. It does the opposite.
| Failure mode | What it looks like | Better practice |
| Long-lived branch disguised as a PR | One branch stays open for weeks | Split work into mergeable slices |
| Toggle sprawl | Old flags never removed | Assign owners and expiration dates |
| Hotfix branch becomes permanent | Emergency branch accumulates unrelated fixes | Patch live issue, then return to trunk |
| Golden branch is not actually green | Main is broken but people keep merging | Enforce blocking CI and fix-forward rules |
| Massive feature rollout | Toggle exists, but rollout is still all-or-nothing | Use progressive exposure by cohort or percentage |
Another trap is overcorrecting against feature branches. Teams sometimes say, "We are trunk-based now, so no branches at all." That usually backfires. A short-lived feature branch is not the enemy. A branch that becomes a parallel development universe is the enemy.
๐งช Practical Example: Replacing a Long-Lived Branch with a Toggle in Java
The easiest way to understand this shift is to compare a branch-shaped release mindset with a toggle-shaped delivery mindset. In the first version below, the team couples rollout behavior to deployment context and stale branching assumptions. In the second, the team keeps trunk integrated and makes the release decision explicit in code.
Before: branch-shaped delivery logic leaks into the application
public class CheckoutService {
public Receipt checkout(Order order) {
String deploymentTrack = System.getenv("DEPLOYMENT_TRACK");
if ("promo-engine-branch".equals(deploymentTrack)) {
return checkoutWithNewPromotionEngine(order);
}
return checkoutWithLegacyPricing(order);
}
private Receipt checkoutWithNewPromotionEngine(Order order) {
// incomplete logic living behind deployment branch assumptions
return new Receipt(order.id(), order.totalPrice());
}
private Receipt checkoutWithLegacyPricing(Order order) {
return new Receipt(order.id(), order.totalPrice());
}
}
This looks harmless, but it is really a branching workflow leaking into runtime behavior. The code assumes deployment tracks mirror branch identity, which makes hotfixes messy and production reasoning harder.
After: one trunk, explicit feature decision, safe rollout path
public interface FeatureFlagClient {
boolean isEnabled(String featureName, String customerId);
}
public final class FeatureDecisions {
private final FeatureFlagClient featureFlagClient;
public FeatureDecisions(FeatureFlagClient featureFlagClient) {
this.featureFlagClient = featureFlagClient;
}
public boolean usePromotionEngine(String customerId) {
return featureFlagClient.isEnabled("promotion-engine", customerId);
}
}
public class CheckoutService {
private final FeatureDecisions featureDecisions;
public CheckoutService(FeatureDecisions featureDecisions) {
this.featureDecisions = featureDecisions;
}
public Receipt checkout(Order order, String customerId) {
if (featureDecisions.usePromotionEngine(customerId)) {
return checkoutWithNewPromotionEngine(order);
}
return checkoutWithLegacyPricing(order);
}
private Receipt checkoutWithNewPromotionEngine(Order order) {
return new Receipt(order.id(), order.totalPrice());
}
private Receipt checkoutWithLegacyPricing(Order order) {
return new Receipt(order.id(), order.totalPrice());
}
}
Now the code can live on trunk safely even while the new engine is incomplete. The release decision is controlled centrally, not implied by Git shape.
For hotfixes in CFD, the same rule applies. If a pricing bug appears while the new promotion engine is still disabled, fix the bug on trunk in the shared path first. If production cannot wait for the normal pipeline, cut an emergency patch from the deployed tag, release it, and then ensure trunk already contains the same fix. The hotfix is an operational detour, not a second development history.
๐ ๏ธ Togglz: How It Supports a Golden Branch in Practice
Togglz is a Java feature flag framework that gives teams a concrete way to practice continuous feature delivery without depending on long-lived branches. It lets you keep code integrated while controlling visibility by flag state.
In practice, Togglz helps with three common needs:
- shipping incomplete code safely with defaults off,
- exposing features to selected users or environments,
- rolling back feature exposure without reverting the merge.
Here is a minimal Spring-oriented example:
import org.togglz.core.Feature;
import org.togglz.core.annotation.EnabledByDefault;
import org.togglz.core.context.FeatureContext;
public enum CheckoutFeatures implements Feature {
@EnabledByDefault
LEGACY_PRICING,
PROMOTION_ENGINE
}
public class PricingService {
public String selectPricingPath() {
if (FeatureContext.getFeatureManager().isActive(CheckoutFeatures.PROMOTION_ENGINE)) {
return "promotion-engine";
}
return "legacy-pricing";
}
}
This is the core CFD move: merge the new pricing path early, keep the flag off by default, and enable it progressively only after trunk is stable. If you model the old and new pricing implementations as interchangeable behaviors, the same rollout style also pairs well with the Strategy Design Pattern. For a full deep-dive on Togglz, see a planned follow-up focused on Java feature flag rollout patterns.
๐ Lessons Learned
- The single trunk model exists to shrink integration risk, not to enforce a Git aesthetic.
- A golden branch is only golden if CI and branch protection make it trustworthy.
- Feature branches still exist, but they should be short-lived and boring.
- Feature toggles are the technical bridge that makes continuous feature delivery practical.
- Hotfixes in CFD should repair trunk first and use emergency patch branches only as temporary operational escapes.
- Teams adapt best when they change batch size, review speed, CI quality, and toggle hygiene together.
๐ TLDR
Single trunk works because it moves risk forward in time, when risk is still cheap. Instead of discovering integration problems at release time, teams discover them continuously. Instead of keeping features invisible through branch isolation, they keep them invisible through toggles. Instead of treating hotfixes as special histories, they treat them as urgent fixes that still return to the same golden branch.
That is why the model exists. It reduces branching complexity so the team can spend its energy on delivery quality instead of merge archaeology.
Article tools
Reader feedback
Was this article useful?
Rate it if it helped, then continue with the next deep dive when you are ready.