Introduction to trunk-based development (TBD)
Trunk-based development is the practice of merging small and frequent code changes into a single branch, known as the trunk branch.
In trunk-based development, branches are short-lived, contain small batches of work, and ideally exist for only a few hours or days before being merged back into the trunk branch. On very small development teams, developers may simply commit new changes directly to the trunk branch.
Often, during a release, a new release branch is created off the trunk branch to be deployed into a production environment. It is the only long-running branch and developers never commit directly to this branch. In very fast or small teams, release branches are sometimes not needed—instead, teams deploy directly from the trunk branch.
When coupled with other DevOps best practices, trunk-based development enables continuous integration, where developers are able to frequently and easily merge changes into the codebase. With continuous integration, as well as test automation and test data management, developers can rapidly make changes to their codebase and receive faster feedback—making trunk-based development possible even for engineering organizations with hundreds or thousands of developers.
As a result, teams adopting trunk-based development can increase their velocity, code quality, and overall throughput by enabling the fast flow of work and avoiding complex merge conflicts, costly code freezes, and slow change approvals.
When should you use trunk-based development?
Trunk-based development is one of the core capabilities of high-performing engineering organizations, according to research from Google’s DevOps Research and Assessments (DORA) team.
Adopting trunk-based development requires a shift in thinking around the visibility of work, investments in the developer experience, and responsibility for managing changes to the codebase.
Trunk-based development is ideal for high-speed and high-trust engineering cultures. It is a fast-moving development style that requires collective code ownership; every member of a team is responsible for the deployability and quality of code merged into the trunk branch.
Engineering teams also need to invest time and resources into building sufficient guardrails that help developers avoid breaking production builds when merging their changes. Teams should provide tooling for testing, merging, and deploying code to make integration less painful, less risky, and easier to fix or revert.
Guardrails also create a positive feedback loop for teams using trunk-based development: well-designed guardrails boost team confidence, further accelerating development. Developers should feel comfortable frequently merging changes and confident that any errors will be uncovered long before they can break any production systems.
What are the benefits of trunk-based deployment?
Trunk-based development enables faster and more seamless code integration, which in turn enables faster feedback, better collaboration, and greater visibility into work in progress.
Instead of using trunk-based development, some teams use a development strategy that relies on feature branches. Feature branches require developers to fully build new features in long-lived branches with large batches of work. Developers continue to work on new features in isolation, often for weeks or months at a time. Once they complete the new feature, they merge the feature branch back into the trunk branch.
In certain scenarios, feature branches may work well for some development teams. Mature products or services don’t always require fast-paced development. Moreover, open source projects rely heavily on feature branches because maintainers often review code from large, public communities of developers.
Feature branches, however, often require continual coordination to merge changes. Over time, long-lived branches diverge more significantly from the trunk branch and from other work in progress. The result is frequent code freezes and time-consuming merge conflicts.
On the other hand, trunk-based development can lead to more frequent deployments and a more efficient release process. It also encourages teams to work in small batches of work with limited scope, allowing them to shorten their feedback loops and write higher quality code.
Common benefits of trunk-based development and continuous integration include:
- Continuous code review: Large batches of work are more likely to contain easily overlooked issues; reviewers need to read hundreds or thousands of lines of code before approving changes. With trunk-based development, code is continuously reviewed in small batches. Small changes are easier to review, debug, and update and lower complexity means fewer mistakes will slip through the merge process. Pull requests are also reviewed in the context of the entire codebase, not just specific branches, helping reviewers understand how each batch of work conforms to the entire system.
- Collective code ownership: In trunk-based development, every developer merges code to the trunk branch, so every developer must ensure that the main branch is in a deployable state and any merged changes were successfully integrated. They are following their code from their code editors to a production-like environment—rather than handing it off to another team member who has less context. More handoffs typically lead to more errors, as critical information is often lost at each step. With trunk-based development, teams are also constantly checking out and pulling their team’s changes to their local environment when testing, giving them insight into the quality and performance of the codebase.
- Quality and real-time feedback: Code is more likely to go stale in long-lived feature branches. When developers consistently check in code with trunk-based development, they can run it against a suite of tests and automations that can find issues earlier in the development process, when they are cheaper and easier to fix.
- Visibility among the team: Teams working in isolation can’t ensure their code works well with other ongoing changes to the codebase. In the long term, this creates more complex bottlenecks during the merge stage when teams suddenly need to combine several significant code changes that deviate significantly from the trunk branch or other work in progress. With trunk-based development, developers can consistently check their work against their teammates and make their work highly visible to others.
- Avoid code freezes. With trunk-based development, teams can avoid downtime when integrating new changes. When code is consistently merged into the trunk branch, developers no longer require code freezes to gather their teams and review large code changes.
What are the pitfalls of trunk-based development?
Trunk-based development has a few pitfalls that engineering teams should consider carefully if they are moving away from a different version control method.
Adoption of trunk-based development will be less successful if teams have:
- Heavy code review processes: Long and arduous code reviews block developers from quickly integrating their code. With long approval workflows, developers will revert to working in larger units of work and merging their changes less often in an attempt to delay the approval process.
- Slow and asynchronous code reviews: Stale pull requests are more likely to cause issues when teams eventually try to merge them. Fast reviews, with the help of automation, help teams avoid delays and backlogs of merge or pull requests.
- Lack of automated tests: Trunk-based development relies on frequent integration of new code, but this cause issues if it breaks builds or increases review workloads. To be successful, teams should allow developers to run tests against code changes before committing to identify any issues, bugs, or failures. Shorter feedback loops for developers makes integrating code changes less painful and makes testing a more deeply embedded part of their workflow.
Measuring trunk-based development
To measure trunk-based development and its impact, teams should improve their visibility into:
- Active branches on the application's code repository. High-performing teams typically use three or fewer active branches per repository.
- Code freeze periods. High-performing teams do not typically use code freeze periods.
- Frequency of merging branches to trunk. High-performing teams merge at least once per day.
- Time taken to approve code changes. If teams are merging code at least once per day, they should also try to approve changes the same day they are requested.
Gitflow vs trunk-based development
There are many other possible branching strategies, including Mainline, Cascade, GitHub Flow, and Gitflow. Most are outdated or less popular.
In particular, Gitflow is a legacy Git workflow that became popular before the advent of trunk-based development. While trunk-based development is more open, Gitflow requires strict control. Only certain developers can approve changes in the Gitflow workflow.
With Gitflow, teams use multiple primary branches with long-lived feature branches. Repositories use different branches for development, hotfixes, features, and releases.
Teams won’t merge a feature branch until the feature is complete. When it’s time to merge, it’s a complex process requiring input from the entire team to find and resolve issues. Moreover, multiple primary branches require more deliberate coordination between teams.
Gitflow has since fallen in popularity in favor of trunk-based development. The rise of continuous integration and other DevOps best practices have made trunk-based development the default choice for high-performing engineering teams.