Trunk-Based Development
What high performing teams do to continually ship quality code, fast.

First let's review the...
DORA Metrics
To create high-quality software, we need to have a high-quality team; research
has been provided to help us measure high-performing teams:
- Development Frequency: Refers to the frequency of successful software releases to production.
- Lead Time For Changes: Captures the time between a code change commit and its deployable state.
- Mean Time To Recovery: Measures the time between an interruption due to
deployment or system failure and full recovery. - Change Failure Rate: Indicates how often a team’s changes or hot fixes lead to
failures after the code has been deployed.
What is Trunk-Based Development (TbD)?
TbD is predicated on the observation that the most high performing teams
continually ship code to production, fast. With these teams, there is a constant
stream of code going into production, often times, multiple times a day or even
multiple times an hour. This is what high performing teams do, not the result. The DORA metrics tell us this.
This means that getting code into main
or "trunk" needs to be as seamless, and as fast as possible, all without sacrificing quality. For many engineers, that may
have used GitFlow or a Sprint Release Cadence, this goal of "Get code intomain
as fast as possible, without sacrificing quality" will seem like an
Oxymoron, but this is simply not true. In fact, the opposite is true: too much process and governance hinders quality.
TbD aims to make shipping to production just part of the developer's everyday workflow. Make the right path the happy path, and vice versa.
In order to accomplish effective TbD, teams must rely on automation to ensure no code goes into production without passing quality checks. But first, what procedural level gains do we gain?
Minimize Diffs
As was mentioned before, most high performing teams ship code continuously into
production. But why does TbD best facilitate this? Couldn't you do this with
GitFlow or some other strategy? The problem with approaches like GitFlow arises
at scale – the diffs that are introduced into production at scale.
Typically with GitFlow, code is merged through a series of environment specific
branches; a branch for preview
, a branch for production
, etc. When a production
release is desired, a developer or CI can merge preview
into production
.
However, the longer that release is not performed, the larger the diff between
these two branches becomes, making the chances of conflicts more and more likely. And if something in those changes breaks production, developers now have a large diff to sift through. The result is typically increasing governance and process over the release cycle, all barriers for getting code live in production, a detriment to achieving DORA metric performance.
Developers will typically work on feature branches, branched off of a staging or
preview branch. The engineer builds out a complete feature, then submits a PR
to the shared staging
environment. This process is a hot bed for merge
conflicts, and engineers rushing to get their code in first, which leads
to shoddy code reviews, and cutting corners, which typically leads to more
process and governance. All things not good for engineers, and introduces more
friction – a further detriment to achieving DORA metric performance.
Hot Fixes and Patches introduce another complexity in GitFlow, more process that
must be documented, considered, and understood by the engineering team. All
places where friction can arise.
By always merging directly into trunk, the diffs introduced into production
are small and therefore much easier to debug if something, that was missed in
automated quality checks, breaks. AND if that happens, it is typically clearer
what checks may be added to automation in order to ensure the same break doesn't make it through quality checks, again.
Minimize Conflicts
We touched on this briefly in the last section, but TbD reduces conflicts,
specifically merge conflicts. This is fairly intuitive: the more often code is
merged into trunk, the smaller the diff, and therefore the smaller the chances
one engineer's diff will conflict with another engineer's small diff.
This means that engineers are encouraged to branch off of trunk, and
incrementally deliver their code changes, back into trunk. If a feature is not
ready, developers should employ Feature Flags to deliver code, but keep the feature turned "off". This allows quality checks to assert quality, and the diffs to
remain small, while keeping the incomplete feature hidden from users.
In situations where other teams have interest in limiting when a feature is released ie. product, marketing, sales, etc., feature flags allow engineers and the SDLC to remain unblocked, while other teams may control "flipping" the flag to release the feature
In other words, Feature Flags decouple Releases from Deployments.
Always Working on Up-to-Date Code
By constantly developing off of trunk, engineers are always working on the most
recent version of the code, and very rarely a stale version. This reduces diffs,
conflicts, and the chance that an engineer duplicates work or has to refactor
based on new changes.
Get To Production, Fast
To bring it full-circle, TbD is all about getting code into production, fast.
This is what high performing teams do. The less process to get code into
production, the faster working code can make it into the user's hands.
So how can a team ship fast with TbD, while ensuring quality? This is why automation is so important.
How to Ensure Quality with TbD
We've already talked about some of the ways we accomplish TbD, but automation is at the core what ensures quality.
Without automation, ensuring quality is nearly impossible in a fast paced flow like TbD
Continuous Integration and Automated Tests
Automated tests, ideally tests that assert the correctness of business
logic, should be ran before any code makes it into production.
Patterns like Dependency Injection, Open-Closed, and Ports and Adapters enable testing business logic using simple unit tests.
This isn't any different from a GitFlow with CI, but you can do GitFlow without automated tests. You'd be pretty hard pressed to do TbD without automated tests. In other words:
Trunk-Based Development forces you to have high quality automated tests. It is essential and a necessity.
Instead of convincing stakeholders that engineers need more time to test their
code, the pain and consequence of not testing is immediately felt, and cannot be
side-stepped with something like "Manual QA" or "Full Manual Regression Testing"
Continuous Deployment
With TbD, code is constantly being shipped to production. So a manual release
process becomes very difficult to perform, and still achieve throughput that
matches developer velocity.
So TbD forces you to automate the deployment process, continuous integration and continuous deployment.
TbD In a Nutshell
Every decision in Software Development should be understood as a set of
constraints. It is our job as engineers to weigh those constraints and decide
whether they are acceptable or not, for whatever reasons.
What Trunk-Based Development does is it forces development teams to accept a set of constraints on process, code, and automation that will only increase the quality of the codebase. Instead of these things being optional, TbD forces teams to build reliable, low noise and high signal, quality checks in the form of reliable tests, reliable
deployments, and small non-esoteric process. TbD is a set of constraints, that in turn gives rise to guard rails, that in turn set up an engineering team, and their products, for success.