Trunk-based development is one of the most cargo-culted ideas in modern engineering. Here's what it actually means, what Farley actually argued, and what it actually requires.

Somewhere between the original argument and the conference circuit, trunk-based development lost its nuance. What started as a precise claim about integration frequency became a bumper sticker: no branches. That bumper sticker is now being applied to teams that had nothing wrong with their branching strategy, and everything wrong with their deployment discipline.

The cost is real. Engineers are being asked to remove a safety mechanism without addressing the underlying problems that made it feel necessary in the first place. And the people making the call often can't articulate why — because they read the headline, not the argument.

The argument is about integration frequency. Full stop.

Dave Farley's case for trunk-based development is not that branches are bad. It is that delayed integration is bad. The specific target of the argument is the long-lived feature branch — the one that lives for two weeks while another engineer makes changes on a parallel branch, and both parties discover the conflict only at merge time, when the cost of resolving it is at its highest.

Farley's actual position

Short-lived branches that live for a few hours or less than a day are not feature branches. They are task branches, story branches, topic branches. Farley explicitly describes them as compatible with the philosophy. The integration frequency is what matters, not the branch count.

His personal practice — committing directly to master — is not a prescription. It is one implementation of the underlying principle, used by someone who has the test coverage, the tooling, and the team context to make it work safely. He compensates with branch by abstraction, dark releasing, and feature flags. The practice without those compensating patterns is not trunk-based development. It is just unprotected commits.

The State of DevOps data Farley cites identifies a threshold, not a binary: teams that integrate to trunk at least daily outperform those that don't. Daily integration from a short-lived branch satisfies that threshold completely. The data does not say "commit directly to master." It says "don't let branches live longer than a day."

The first-principles failure

The underlying problem Farley observed is real: teams suffer when integration is delayed. But the analysis stops one step too early. He identified the correlation — long-lived branches correlate with integration pain — and attacked the mechanism rather than the cause.

Apply first principles. What causes integration pain? Divergence over time. Divergence is a function of two variables: how long code stays isolated, and how much the shared codebase changes in that window. Branches are one way code gets isolated. A developer sitting on uncommitted local changes on trunk is equally divergent — they just don't have a branch name to blame.

The first principle is: minimise divergence duration. Everything else — branching strategy, review process, merge tooling — is implementation detail. Once you frame it that way, short-lived branches with fast CI are not a compromise. They are a perfectly valid solution to the same underlying constraint. And they give you something Farley hand-waves away: an explicit review gate that does not depend on the entire team adopting pair programming.

Master is production. No exceptions.

This is the discipline that trunk-based development is actually built on, and it is harder than eliminating branches. If master does not reflect what is in production — precisely, always — then every other practice in this space is built on a lie. Hotfixes become guesswork. Rollbacks become archaeology. Release confidence becomes theatre.

Master is not "mostly what's in production." Master is not "what will be in production after the next deployment." Master is what is running right now. If a change is not ready for production, it goes behind a feature flag — it does not sit in master in a dormant state.

Feature flags are not a nice-to-have that enables trunk-based development. They are the mechanism that makes master-equals-production enforceable. If code is not behind a flag and it is not ready for users, it has no business being in master.

A pull request is not a review gate. It is an enforcement surface.

The "no branches, commit directly to master" argument typically comes bundled with scepticism about pull requests. The argument is that PRs create bottlenecks, slow delivery, and introduce a human approval step that adds friction without proportionate value.

This is a category error. It conflates the review mechanism with the enforcement mechanism. They are different things, and conflating them means discarding the latter when you have a legitimate complaint about the former.

01
Clean, reproducible environment
CI runs against a fresh, identical environment every time. Not a developer's laptop with its local dependencies, modified config files, and cached state. The PR is the gate to that environment.
02
Policy as code
Required status checks, coverage thresholds, SAST scans, dependency audits — these run uniformly on every change. No developer's local setup, intentions, or Saturday morning shortcuts can bypass them.
03
Auditable record
Every merge carries an attached CI run. In a regulated environment that is not a nice-to-have. It is the evidence trail a security auditor, a regulator, or a board will ask for.
04
Human approval is optional
Auto-merge on green is a legitimate operating model. Checks pass, branch merges, done. The bottleneck argument disappears. The enforcement infrastructure remains.

Git hooks are not an alternative to this. A hook can be bypassed with --no-verify. That is a one-flag problem. Hooks are not versioned and distributed automatically — developers opt in, or don't. Hook behaviour varies with local tool versions and OS configuration.

A hook is a reminder. A required status check in a PR pipeline is a control. These are not the same thing, and any engineering leader conflating them has not thought carefully enough about what a control actually is.

The circuit breaker you didn't know you needed

There is a new argument for the branch-as-enforcement-surface that Farley's original position predates entirely: AI-assisted and AI-agent-driven development.

Engineers are now using AI agents that generate, modify, and commit code. Some of these agents are running with --no-verify, bypassing pre-push hooks because the hooks were designed for human-speed workflows and the agent treats them as friction. In a trunk-direct model, that means unreviewed, machine-generated code lands on your production branch with no isolation boundary and no clean revert point. The only backstop is your CI pipeline, and if it is slow or incomplete, the damage is already on main.

A short-lived branch costs thirty seconds of overhead. In return:

That last point will matter more than most teams currently appreciate. When a regulator, a client, or an incident review asks "how do you verify AI-generated code in your codebase," the answer of "we pair programmed with it and pushed to trunk" will not hold up. A branch with a reviewed PR is a paper trail. Trunk commits are a git log.

Farley built his argument in a world where every line was written by a human sitting next to another human. That world is gone. The branch is not overhead. It is a circuit breaker. And in an era where agents can generate and commit code faster than any human can read it, circuit breakers are not optional.

The CI bill nobody talks about

There is a practical cost to trunk-direct development that the proponents consistently ignore: pipeline economics.

If three developers push to trunk multiple times a day, CI triggers on every push to main. That is your production branch, so you cannot run a reduced pipeline — you need the full suite on every commit. Linting, unit tests, integration tests, build verification. Every single commit triggers the lot. On GitHub Actions, that is real money. On a self-hosted runner, that is real contention and queued feedback.

With short-lived branches, CI runs against the branch. If a developer pushes three work-in-progress commits, those trigger branch pipelines that can be scoped or parallelised. The full-suite run happens once, at the merge gate. Main receives one pipeline run per merged piece of work, not one per commit from every developer on the team.

This is basic queuing theory. Trunk-direct serialises every developer's output through a single pipeline on a single branch. Under any real load, you either queue — which means slow feedback, defeating the entire purpose — or you throw money at concurrency. A branch model naturally distributes the load. Each branch is its own queue, and main only processes validated, pre-tested merges.

For a bootstrapped product team or a small consultancy, that is not a theoretical concern. That is the difference between a sensible infrastructure bill and one that makes you wince at the end of the month.

Long-lived branches are a diagnostic, not a cause.

When branches live too long, something is wrong. But the something is almost never the branch. The branch is the symptom. Removing it does not treat the underlying condition.

Symptom → Actual Problem
Branches living for weeks
Stories too large. Work is not decomposed into mergeable units. The branch lives as long as the feature takes.
Engineers delay raising PRs
A senior engineer is gatekeeping with essay-length reviews and slow turnaround. The psychological tax on merging is high.
Reluctance to merge at all
Low confidence in the test suite. Someone has been burned before and nobody fixed the fragility. Or production incidents become blame events.
Master drifts from production
No deployment discipline. Merging to master does not trigger a deploy. The equivalence between master and production is assumed but not enforced.

Pushing directly to master in an environment of low test confidence, poor deployment discipline, and a culture where incidents become blame events does not build confidence. It removes the last safety net the team had.

The PR gatekeeper case deserves particular attention. The senior engineer who sits on PRs for days, leaves exhaustive reviews on every line choice, and whose approval has become a hard dependency — this person is one of the most destructive forces on team velocity. The self-reinforcing loop runs like this: slow reviews mean engineers batch work into larger PRs to minimise the interaction cost, larger PRs mean slower reviews, longer branches, and eventually a culture where merging feels like an ordeal rather than a routine.

The correct response to a PR gatekeeper is not to remove PRs. It is to remove the bottleneck and fix the hiring process that allowed it to form. Auto-merge on green eliminates the gatekeeper problem entirely while preserving every benefit of the enforcement surface.

If your team is debating branching strategy, your problem is somewhere else.

Well-led teams converge on something sensible without ever reading a blog post about it. They make a branch, do the work, run the checks, merge it, delete it. Nobody names the strategy. Nobody writes a wiki page about it. It just works, the same way nobody in a well-run kitchen has a meeting about which order to chop vegetables.

The moment someone suggests the team needs to adopt TBD or GitFlow or anything with a proper noun, what they are really saying is: "We have a coordination problem and I would like to solve it with a rule instead of with leadership." And rules without understanding are just bureaucracy.

If you are having issues getting code into main and deployed, the branching model is not the place to look. The actual problems are somewhere in this list:

Not a single item on that list is a version control problem. Every single one is a people and leadership problem. And the entire branching strategy industry — TBD, GitFlow, all of it — exists because it is easier to change a process than to have a difficult conversation with a product manager about scope, or to tell a developer their work is not ready, or to admit that leadership does not understand how software gets built.

Branching strategy is a finishing move.

The team that is genuinely ready to optimise its branching strategy has already solved the hard problems. It has a test suite it trusts. It has deployment confidence — master deploys automatically, and the team is comfortable with that. It has story sizing discipline — engineers know how to break work into units that merge within a day. It has a culture where production incidents are treated as system failures, not individual failures, so engineers are not afraid to ship.

Reaching for branching strategy before those things are in place is not engineering leadership. It is the appearance of engineering leadership — a visible, nameable intervention that avoids the harder, more political work of building team capability and fixing process discipline.

The actual prescription

Short-lived branches with a strict lifetime. PRs as policy enforcement with auto-merge on green. Master equals production, enforced by the deployment pipeline. Feature flags for anything not ready for users. CI in a clean environment, not hooks on developer laptops. The rest is culture and capability — and no branching strategy fixes a culture problem.

The simplification that became a complication

There is an irony worth naming. Trunk-based development was supposed to be the antidote to GitFlow — the reaction to a branching model so convoluted that engineers needed cheat sheet diagrams pinned to their monitors. The promise was simplicity: just commit to main.

What it became is a 28-page website with its own taxonomy. Small-team TBD. Scaled TBD. Super-scaled TBD with merge queues. Feature flags. Branch by abstraction. Dark releasing. Pair programming as mandatory code review. A "you're doing it wrong" page. A book. Twenty-five diagrams. Each layer adding back a safeguard that the original argument told you was unnecessary.

Read the caveats on trunkbaseddevelopment.com carefully. They concede that short-lived branches are fine for code review and CI. They concede that you need a build server. They concede that feature flags and branch by abstraction are needed for anything non-trivial. The "scaled" version of TBD is short-lived branches with CI gates — which is what every sensible team already does, rebranded with its own domain name.

Steve McConnell wrote Code Complete — one of the most influential software books ever written — and as the industry absorbed those ideas, he let the knowledge become part of how competent developers work. No movement. No conference circuit. No fifteen-year Twitter campaign defending the same position. He wrote it well, the profession absorbed it, and he moved on to new problems.

The lesson applies here. "Integrate frequently, keep main deployable" is a paragraph of advice, not a philosophy requiring its own website. The teams that need to hear it will not be fixed by hearing it. The teams that have already absorbed it do not need the website.

Why this keeps getting misapplied.

The influencer-to-certification pipeline is worth naming directly. A precise technical argument, developed for a sophisticated audience, enters the conference circuit. It gets simplified for a talk. The talk becomes a course. The course becomes a certification. The certification becomes a line on a CV. By the time it reaches a Head of Engineering mandating a practice change, the nuance that made the original argument defensible has been stripped out entirely.

What remains is a slogan: no long-lived branches. Which becomes: no branches. Which becomes: a policy change with no supporting infrastructure, no feature flag discipline, no deployment confidence, and no examination of why the branches were living long in the first place.

The measure of engineering leadership in this situation is not whether you can cite Farley or the State of DevOps report. It is whether you can explain the underlying reasoning, identify where your team's actual problems are, and prescribe the intervention that addresses those problems rather than the one that is easiest to announce.

The branch is not your problem. The branch is where your problem goes to wait.