What Is Trunk-based Development and How Can It Benefit Your Product?
Building products in an agile, iterative manner is quite a challenge. So, looking for ways of simplifying the process is our obsession. In this article, we introduce one of the effective workflow models that reduces the cost of product development, speeds up product delivery, and helps to improve team performance.
Table of contents
Why is it worth getting familiar with trunk-based development?
Imagine you are working on an article with a few other colleagues. You all decide to split the work into sections and work on them individually. When the sections are all finished, you combine them into the final document. There is a good chance that you will have to spend a lot of time merging changes and the final document may still not live up to the standard you envisioned for it. Each section might be written using a different style, and duplicating ideas. The feedback on the whole document will likely be delivered very late and in the end some parts might have to be rewritten completely.
The alternative approach is to deliver the document by writing small sections that will be combined relatively fast with the rest of the article. In this approach, you focus on delivering smaller changes incrementally to get early feedback. More frequent integration could reduce duplication and improve the communication between collaborators. This approach could produce articles of higher quality much faster.
While writing articles and code is vastly different, the chosen workflow method has a big impact on the final result. That’s why you should look into why trunk-based development may be important for your team.
What is trunk-based development? A definition
Trunk-based development (TBD) is a source control workflow model that enables continuous integration. Its key ideas are correlated with DevOps practice. The main goal of trunk-based development is to avoid long-lived branches by merging partial changes to the whole feature. To do so developers can commit directly to the main branch or use short-lived branches with an efficient code review process. By definition, branches should only last a couple of days (source).
Merging partial changes results in initial feedback, reduces merging complexity and reduces duplicated work if other developers are working on related changes. When the merging process is efficient, the developer doesn’t lose focus by context switching to other tasks. Each change doesn’t break the build, because the main branch, often called the trunk, should be ready to deploy at any moment. There are practices, such as automated testing, feature flags or branching by abstraction that enable implementation of this workflow in the team.
Benefits of using trunk-based development
Reduced complexity
One of the main trunk-based development benefits is the reduced complexity of merging different branches into one. This approach aims to avoid merge hell, a situation when different pieces need to be combined for the first time which leads to unexpected bugs, integration issues and blocks the team from deploying. This could happen just before the end of the sprint, which also increases the level of stress. The more branches you have, the more complexity developers have to deal with. Using trunk-based development means that most of the developers’ time is spent on producing code, rather than performing merges.
Increasing speed of delivery
Implementing and using trunk-based development in the long term could increase team discipline and a feeling of teamwork by establishing clear processes and giving more opportunities for collaboration. When properly implemented it can also increase the speed and predictability of delivery. According to the State of Devops 2021 report (source) produced by Google, high performing organizations are more likely to have implemented trunk-based development.
Shortening the feedback loop
Building in short feedback cycles could also help verify initial design assumptions. Because the trunk should be always stable, the code is potentially releasable to customers for getting early feedback.
Suggested practices for implementing TBD
Before implementing trunk-based development some steps should be taken. Technical requirements such as automation and a stable test suite are advised to keep the trunk green.
Automation
Automating the building, testing and deployment software are key enablers of trunk-based development. It allows for making quick iterations while reducing the chance of breaking the main branch. The test suite should be fast so as not to block the iteration cycles. Automating the deployment process makes it more resilient, because manual deployment processes are error prone.
Feature flags
Using feature flags you can decouple deploying the new code to the production environment from releasing the feature to the users. Feature flags (also called feature toggles) could be defined during the application building stage or controlled in run-time by external management software. Feature flags don’t come without their issues though. They increase the testing surface by adding new combinations of behavior. Reusing or not deleting unused feature flags could also lead to an increase in technical debt.
Using feature flags, you can decouple deploying the new code to the production environment from releasing the feature to users. Deploy is a preparation step, while release is a publication step. Canary release technique can be used to test new features on limited user groups.
Branching by abstraction
Branching by abstraction is a technique of safely introducing large changes that won’t break the system. It’s done by introducing an abstraction between the existing implementation and the module that needs to be replaced. When the abstraction is in use, it can gradually switch to the new module. When the transition is completed, the old module is deleted.
This technique allows developers to split their work into smaller commits instead of creating a single merge request that could be difficult to manage. Branching by abstraction prevents introducing breaking changes and allows you to pause and resume the change over time, because the application is working throughout the process.
Efficient code reviews
Good communication is a crucial element of high-performing teams and it’s very important while trying to implement a trunk-based development approach. There are techniques such as pair programming or mob programming that encourage more communication between developers in the team. Those techniques allow developers to contribute and familiarize themselves with the changes without an extensive and time-consuming code review process.
A trunk-based development approach doesn’t advise against code reviews but suggests synchronous processes that meet the requirement of short-lived branches. The problem with the asynchronous review process is that when a developer finishes a feature, they request a review and start working on a new task. This could generate a large number of changes to review which reduces the time spent on each one and thus the quality of the reviews. A synchronous review process prioritizes quick reviews with the goal of merging the code fast. Here are some tips that could speed the process of code review:
- Define a rule that when a developer finishes their merge request they should review other merge requests.
- Have dedicated sessions for code reviews.
- Automate common code review checks (e.g. applying common formatting, static analysis tools like SonarQube).
- For complex features, have a design meeting before implementation.
While seniority helps avoiding common struggles on the tool level, it’s not the crucial requirement for implementing trunk-based development. Techniques like pair programming or mob programming, and a mentoring culture can ease differences in seniority.
Common fears
The biggest fear around introducing trunk-based development is about changing an already defined flow to something new and untested. Digital development projects often have lots of other priorities and waiting improvements. One option is to introduce techniques that will support trunk-based development in the future, such as test and build automation, feature flags, pair programming, or smaller user stories.
Releasing
There are two main ways to release software. The first is deploying straight from the trunk, which might be a good choice for product development teams with a high release cycle. The second approach is centered around using dedicated release branches. In this approach whenever a team wants to release, a branch is created which doesn’t block commits to the trunk (see more at the trunk-based development website).
Testing
Often questions arise about manual testing or acceptance testing. One approach would be to create a dedicated environment where feature flags are enabled. In this approach, after automatic testing and merging to the main branch, the code would be deployed to an environment where an additional check could be performed.
Fixing the trunk
There could be concerns regarding common development processes like fixing bugs. When the trunk is broken, it’s a priority to fix it. In some teams the broken commits are reverted but more often a quick fix is applied. When using the branch for releases, hotfixes can be either applied directly on the branch, or in the trunk and transferred to the release branch by Git cherry-picking. Runtime feature flags allow disabling the broken parts of the system. Whatever approach is selected, a stable and repetitive process must be established.
Alternatives to TBD
Trunk-based development is not the only viable branching model and it’s often compared to the Gitflow and GitHub flow models.
Gitflow
Gitflow is a branching model focused on releasing completed features. Once the code is finished it goes through different supporting branches (for example, develop, release and main) before it is released. This approach may be beneficial for projects when the speed and continuity of delivery is less important than the control of what and how is released.
GitHub flow
Github Flow is presented as a simpler option that can be applied to more projects because it reduces the number of primary branches to one. While it’s a different branching model, similarly to trunk-based development the main branch must always be releasable. The biggest difference between GitHub flow and trunk-based development is the inclusion of long-lived branches. In this model, code is released from feature branches not the main branch. This approach doesn’t fully support continuous integration because developers integrate only the main branch, not other feature branches.
Caveats
Open source
There are projects where trunk-based development may not be the best solution. The primary type of project where a long-lived feature branch model would be a better fit is an open source project, where everyone can be a contributor. In this case, a strong code review culture is needed, while it might be also necessary to have few testing stages before releasing the product.
Shared repositories
While not every developer might have a chance to contribute to open source software, trunk-based development may also not be the best option for a company’s cross-team repositories. Whether it’s a utils package, design system library, or other software shared across teams, there should be a core team that maintains the project and reviews the proposed changes. Even when a team works in trunk-based development there might be cases when a detailed merge request or long-lived feature branch is needed. One example situation might be a self-contained contribution from an external developer.
Where to start?
Before implementing trunk-based development in your team, make sure your product has a strong foundation such as test and build automation, feature flags, pair programming, and smaller user stories. To simplify the merging process, GitHub flow can be a good transition step, and for some teams, it might be enough. Keep in mind that whatever decision is made about the selected branching model, it should be known to the whole Scrum team. Branching models impact not only the code but the process of planning features, releasing code and fixing issues.
Here are some starter recommendations:
- Have an automated suite of tests that your developers trust to detect regressions.
- Try to split user stories into simple and concise elements that can be delivered separately.
- Try to create smaller and self-contained merge requests that are easier to review.
- Try to adopt pair programming to improve the collaboration and knowledge sharing in your development team.
If you’re considering developing a digital app, choose an agile development team that uses good practices and works in short iterations. Finding a development partner who knows and implements trunk-based development within their agile culture may be a good idea. Good luck!
Share this article: