Trunk-Based Development

What high performing teams do to continually ship quality code, fast.

Trunk-Based Development
Photo by Mathias Reding / Unsplash

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:

The DORA Metrics

  • 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 into
main 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.

Subscribe to TillaTheBlog

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe