What Is Trunk-Based Development?
Trunk-based development (TBD) is a branching model and methodology for software development where development teams merge every new feature, bug fix, or other code change to one central branch in the version control system. This one branch is called “trunk,” “mainline,” or in GitHub, “main.”
Trunk-based development makes it easy to satisfy the “everyone on the development team commits to trunk at least every 24 hours” requirement of continuous integration and lays the foundation for the codebase to be releasable at any time, as is necessary for continuous delivery and continuous deployment.
Trunk-Based Development Simplifies Codebase Management
Software development teams adopt trunk-based development because it simplifies codebase management, allowing them to deliver code changes faster with less effort and waste. Gone are the multiple long-lived branches called for by gitflow. A single, streamlined trunk reduces the time and effort required to reason about each change.
All of the benefits of trunk-based development flow from this core principle of simplicity. Code reviews are easier. Merge conflicts occur less often and are easier to resolve. Continuous integration is achieved for the entire app, not just each isolated feature branch. DevOps metrics, such as lead time for changes, change failure rate, deployment frequency and mean time to recovery (MTTR) are all easier to achieve. Most importantly, the benefits of trunk-based development grow as your solutions complexity or team size grows.
Complex Systems Don’t Have to Be Complicated
Alex Okrushko makes an excellent point about the value of using trunk-based development to avoid complicated workflows in his talk, Enabling Trunk-Based Development. Alex is a seasoned practitioner, part of the NgRx team, GDE in Angular, Angular Toronto organizer, and co-organizer of the official Angular Discord. Here’s what he said about complex systems and avoiding complications:
Complex does not mean complicated. Complicated is more chaos, a lot of things happening, a lot of organization, while complex systems still can be broken down to simple rules that are easy to follow and to understand. Think of a wedge of geese; they follow simple rules. They don’t get together one evening and decide, okay, how do we fly in this V? They all know that one bird is in front and the other birds would be to the right of it or to the left of it, right? Why do they do this? Well, it’s easier to fly. It reduces drag. It requires less effort to fly that way. And once the front bird gets tired, you know, it gets pulled back a little bit. Somebody else replaces it, but it’s a self organizing complex system. The key here is simplicity.
It’s the key to trunk based development: being the simple thing. Development, again is also complex problem. There are a lot of things happening, but we want certain things out of code and our development processes. What do we want? We want code to be frequently submitted, and the code to be frequently deployed. We want it to be simple. We want PRs to be simple, easy to understand. We want the processes around development to be simple and easy to understand, because again, it involves many different teams, not only product managers, QA and other folks as well, but also different development teams. The simpler it is, the easier it is to get everybody on board. The more successful your product will be.Alex Okrushko
Styles of TBD
Depending on the size of the development team, two different styles of trunk-based development emerge: small teams tend to merge every new change directly to the trunk, while larger teams use short-lived feature branches, owned by one person/pair, or a small team. These short-lived feature branches will be merged back into the trunk, ideally no less often than once per day.
Trunk-Based Development and Feature Flags
If trunk-based development requires you to commit your code changes back to the trunk no less often than daily, what do you do for changes that take several days, a week, or even longer to complete? You still commit them daily, but with the work in progress (WIP) encapsulated or “gated” behind a feature flag. That feature flag, or feature toggle as some prefer to call it, is turned off at the start. Your application behaves as if the new code is in a feature branch that hasn’t been merged and deployed yet, but to the codebase that code is merged and safely deployed. This practice is known as branch by abstraction.
When you branch by abstraction and merge your code daily, even major new features can be built without encountering the surprise of ”merge hell” that can happen when a long-lived feature branch is merged at the end of your work. As you may have already discovered, resolving merge conflicts gets significantly harder with each unmerged change that is made after an undiscovered conflict is first created. TBD keeps merge conflicts short-lived; they never get the chance to grow into the sort of complex mess that can take hours, days or weeks to unwind.
Why Not Just Comment Out WIP Code and Commit That?
Put simply, you don’t want to comment out WIP, because you want to be able to test it, early and often. Unit tests should work as early as possible. The same goes for any other sort of test you run. With feature flags, as your work approaches the definition of done, you can toggle the flag on for yourself or your team members to test locally, or in any environment, including production.
The key difference between commenting out, or using config files, or environmental variables, and feature flags, is that feature flags are evaluated at runtime on a user-by-user or even request-by-request basis using targeting rules that can be changed in the feature management platform instantly without changing your code. By individually targeting yourself or your team, you can test without exposing your new code to customers or to automated testing that it’s not yet ready for.
Further along in the development process, feature flags allow you to expose your code to regression tests, release the feature gradually as a feature-specific canary test, schedule the release for an exact time on an official launch date, or release it exclusively to customers who have opted into an early access program. All of this becomes possible without separate branches for each environment or use case.
With trunk-based development and feature flags, you systematically avoid accumulating multiple branches that must be merged back later with a hard-to-predict cost of time and effort. You keep the codebase simpler and easier to reason about, which allows your development team to focus on new features and refactoring, not managing complex merges.
The only long-lived branches in trunk-based development are release branches, managed by a lead developer, or “merge meister” as Paul Hammant, the most notable proponent of TBD, would say. Release branches are only needed if your team is still at an infrequent release cadence. If you are releasing daily or more often, you don’t want (or need) them.
Bugs Are Fixed in the Main Branch First, Not the Release Branch
Developers don’t make commits directly to release branches. Instead, they fix the bug with a code change to the main branch first. Verify it in the main branch via continuous integration. And then, the fix is “cherry-picked” by the merge meister to the release branch, where continuous integration tests are run once more.
Why Fix in Main First?
Fixing in a release branch first increases the risk of a regression if that change isn’t also made in main before the next release is cut. Think of it as a corollary to “never break the build” — we want to keep the trunk always free of regressions.
In a trunk-based branching strategy, release branches are never merged back to trunk. Instead, they are deleted when the next release is considered stable in production.
Pull Requests in TBD
Many people think of pull requests and imagine Gitflow, which is almost the polar opposite of trunk-based development (slow and fault-tolerant versus TBD which is fast-paced and developer-trusting). But pull requests do have a place in TBD for code review. We still want the value of collaboration around changes, whether that be about if the changes meet the desired functionality, or if there are minor semantic code quality issues such as style to address. A pull request is valuable as a consistent trigger for a code review, including automation that can check a developers work before a peer is asked to perform timely review on the new code.
When to Use Trunk-Based Development
There are two key features of TBD to consider when deciding whether or not to implement it. First, TBD supports moving very quickly. Second, it’s very trusting of developers: no matter what they do, they are trusted to not break the build. These are commonly espoused as benefits of trunk-based development — and they are — but no system works perfectly for everyone.
For example, a brand-new company that needs to create version 0.0.1 of its product as soon as possible and has a team comprised of experienced engineers will work perfectly with TBD: everyone who would be committing to trunk is trusted, and speed is critical. However, a group of developers maintaining an established open-source project will not work so well: speed is less important for them. They can’t possibly trust every random person who opens a GitHub pull request. The latter team would be better off using a more fault-tolerant process that accepts a workflow with multiple longer-lived feature branches (and the resulting greater complexity) in return for greater control (such as Gitflow).
- Watch Alex Okrushko’s talk, Enabling Trunk-Based Development. The core of his talk is only 18 minutes long. Alex’s slides do a great job visualizing these concepts, and the Q&A after his talk addresses many key implementation issues.
- Read A Complete Guide To Trunk-Based Development. This blog provides additional tips on adopting TBD and greater detail on the benefits.
- We have a lot to explore that can help you understand feature flags. Learn more about benefits, use cases, and real world applications that you can try.