Every year, the DORA report sets the benchmark for performance of software development teams everywhere. The separation between typical performance and exceptional behaviour seems to grow ever starker. This year's Accelerate State of DevOps report saw the top-performing group carry out code deployments nearly a thousand times more frequently than the study's lowest-performers. It's not surprising that these very same high-performance teams were more likely to have implemented trunk-based development within their processes.
“Our research has consistently shown that high performing organizations are more likely to have implemented trunk-based development, in which developers work in small batches and merge their work into a shared trunk frequently. In fact, elite performers who meet their reliability targets are 2.3 times more likely to use trunk-based development. Low performers are more likely to use long-lived branches and to delay merging.” - DORA Report 2021
There isn’t simply one reason why the best-in-class performers use this methodology. From the way that it compliments our internal rewards-based thought patterns, to its ability to support collaborative achievement of a flow state, trunk-based development supports a multitude of team goals. If done well, it can become the backbone to your development lifecycle.
What is trunk-based development?
At its core, trunk-based development is a version control management practice that falls within the various git-branching strategies you can employ in the development process. The goal of trunk-based development is to minimize complexity by shortening the lifespan of each branch to under one day. Every developer creates a short-lived branch for their changes. This is then merged into the main 'trunk' as soon as possible, with each branch created ideally consisting of a few commits.
The practice, a core staple within DevOps culture, works to streamline the merging and integration phases, preventing big-batch conflicts. The longer each branch lasts, the greater the opportunity it has to be iterated on, advancing from its original state. While this is excellent in an individual development setting for advancing the state of your build, it can also cause ample opportunities for merge conflicts to occur within the context of a team.
Why should you opt for trunk-based development?
Trunk-based development typically runs hand-in-hand with continuous integration and delivers a whole host of benefits for those that employ it. For the practice itself to prove successful, teams are required to work under the assumption that the main branch from which everyone is pulling is both stable and in a persistent production-ready state. This makes automated testing a critical component when executing a trunk-based strategy.
While the solid stream of minor fixes, releases, and updates abstract away the complexity of several persistent branches, for the process to yield the best results, it needs to be matched by a robust integration process involving testing and archiving or deletion of expired work. The nature of trunk-based development ensures that all final updates are available to all developers, producing an open model by design. Teams are collectively kept up to date on build decisions and are subsequently able to better adapt to shifting requirements and handling incidents. As opposed to working on long-lived, unwieldy branches that prove tricky to merge, trunk-based strategies make resilience a shared responsibility with everyone responsible for their latest iteration of the main build.
Limitations to batch size
The daily routine of developers checking their code to the trunk produces an instant feedback loop that reduces batch size and creates an iterative communication pattern. For instance, detecting a problem is easy if a commit to trunk breaks an automated test upon merge or interacts poorly with another part of the application. In that case, we only have to analyze less than a day's worth of work.
This practice compounds the benefits of CI/CD when, say, your pipeline is automated to reject all commits that fail integration testing and stand to take the main trunk out of its deployable state. Depending on the configuration of your pipeline, teams can notify individual developers of their breaking changes, allowing them to work on corrections without impacting anyone else's flow. The process works to reinforce collaborative progress executed independently.
Functional feedback loops
Trunk-based workflows solicit feedback from the build at the earliest possible point. While many may prefer working on their private branch to prioritize an individual developer's flow-state, trunk-based development champions collective flow. Its frequent testing and deployment nature prevents complexity from scaling alongside both time-size and codebase complexity. The method stabilizes the order of magnitude by which work can escalate.
Maintaining this level of frequent, in-built communication within your development practices can produce some real tangible benefits, as these high-performers note. But it might be worth examining how trunk-based development can work with our brain's in-built rewards-based dopamine release system. A previous version of the very same report, at the time produced by Puppet, cited trunk-based development as a critical indicator of higher job satisfaction in engineers, even going so far as to associate the methodology with lower rates of burnout and a highly-generative working culture.
Dopamine-driven development
The urge to repeat an action for which we're rewarded is built into our core functioning. We're wired to achieve success and chase our dopamine production. This is often the reason why test-driven development is adopted by teams as a method of achieving a continuous flow state off the back of consecutive passing tests. The same sort of effect can be achieved if we employ trunk-based development.
Every time we deploy, we're keeping batch sizes small; we're designing our processes to keep human error at an absolute minimum, creating lower-risk releases. With a smaller overall batch size, changes are more likely to be successfully merged into main, increasing the sense of accomplishment with each commit. Our brains are inherently wired to encourage behaviors resulting in a reward. Working in this manner produces a positive reinforcement cycle conducive to a greater overarching sense of fulfillment.
The challenges of adopting trunk
Taking on a trunk-based approach isn't easy for everyone and doesn't come without its challenges. A key indicator that trunk-based might be ill-suited to your organization, either for the time being or in general, is a monolithic repository. The renowned title, “The DevOps Handbook,” makes a strong case for loosely-coupled architecture as a method of accelerating the velocity of your development practices without sacrificing control. Designing your codebase to allow for feature-oriented abstractions can lessen the operational burden on your team, alongside the sheer cognitive load necessary to support the application itself.
Nuanced releases with feature flagging
Adopting feature flagging, or even a flag-driven development workflow, is complementary to a trunk-based approach in that developers can wrap their changes in a feature flag, switching them on at a predetermined time. More than serving as an excellent foundational piece for trunk-based operations, working with feature flags can effectively be your pathway away from the monolith, opening the door to greater agility across the board with CI/CD.
With feature flags, code can be deployed, and once validated, released in a phased approach to further nuance the feedback structure brought about by a small-batch change going live to the main trunk. What's more, flags can serve as a replacement for multiple repositories and branches, retaining the overarching integrity of the central brain, all with a greater level of control around feature availability. Your legacy codebase can be continually served to your broader user base, while your newly architected feature branches can slowly be rolled out via a canary release. This validation at-pace can essentially form the start of your feedback loop back to your core trunk, enabling you to refine your process as you increase your deployment frequency all the way through to daily.
What have you got to gain?
When designing for scale, reducing the cognitive load with each commit needs to be your north star. While other methods of git-flow and long-lived feature branches can help you optimize for individual clarity, a team's interdependence ultimately supersedes the needs of a lone 10x developer. Teams able to achieve a collective flow state won't waste excessive hours on refactoring; they will make decisions quickly, and this lower cognitive load won't allow the lifespan to escalate problems. By keeping batch sizes manageable within the timeframe of a single day, swarming to solve an issue will become a more feasible solution for complex problems that do manage to arise.
Despite going hand-in-hand with other agile practices, a trunk-based approach doesn't require continuous integration or testing automation to ultimately succeed. The beauty of trunk-based patterns is that they train us to become collectively continuous. If your organization frequently misses its SLOs and reliability targets, it might indeed be time for the trunk.
To start your journey on accelerating using the DORA metrics, check out this discussion between our very own CTO, John Kodumal and Dr Nicole Forsgren about her time at DORA and her thoughts when collating the infamous metrics upon which the eponymous report was based on.