Adopt Trunk Based Development

Introduction

Trunk-based development is a branching model used by DevOps teams to merge smaller updates with greater frequency to the main branch or “trunk”.

It’s a type of version control for source code that promotes the implementation of a continuous integration and continuous delivery (CI/CD) pipeline.

Why is this important?

Trunk-based development ensures teams release code quickly and consistently to produce high-quality software with less drama.

As codebase complexity and team size grow, trunk-based development is an essential practice for keeping production releases flowing.

Background Information

Code branching

Branches are independent copies of code that stem from a central codebase, referred to as the trunk, or main. Code branching allows multiple team members to work independently in their own branch.

Ideally, branches are short-lived, making it easier to merge code into the trunk more often. The ability to frequently and automatically merge branches is essential for continuous integration.

Frequent merging of code branches is encouraged in trunk-based development to reduce the risk of breaking the build. All branches should be merged at least once every 24-hour period to ensure the stability of the trunk.

Continuous integration

Integration is the practice of bringing together the different elements of the system to verify that everything works together as intended before deploying to production.

Continuous integration (CI) extends this idea to integrate and test the system after every change. It is possible to test changes in an integrated environment when using multiple branches.

However, until the changes are merged into the trunk and tested together, there is a risk that conflicts will cause things to break. Trunk-based development encourages smaller changes and more frequent integration, making it less likely to cause problems than merges from long-lived branches.

When development teams frequently merge branches into the trunk

  • They improve the flow of work.
  • Reduce the chances of instability.
  • Help ensure the codebase is always available for release.

Continuous integration is a necessary part of continuous delivery.

Continuous delivery

Continuous delivery is the practice of ensuring the codebase is always in a releasable state. Broken software is unacceptable. Continuous delivery enables teams to release changes on demand, with a high degree of confidence.

Continuous delivery allows all kinds of changes to be put into production safely, quickly, and in a sustainable way. This includes experiments, bug fixes and new features.

Trunk-based development supports continuous delivery by encouraging smaller changes to be merged into the trunk more often. This means:

  • Large features are broken down into small incremental changes.
  • Testing is performed more often.
  • Problems are identified much more quickly.

Gitflow

Gitflow, is a popular branching model that emerged in 2010. It supports multiple development scenarios, resulting in the need to maintain several branches that are simultaneously in use.

Gitflow, however, can quickly become complicated with long-lived feature branches and multiple primary branches. Feature branches can involve multiple developers and days or weeks of work, all of which contribute to “merge hell”.

It is difficult to introduce automation, which slows deployment. It also removes autonomy from the development team to merge changes.

Gitflow is in direct conflict with the goals of continuous delivery and maximised flow.

Trunk based development

Trunk-based development is a way to control source code regardless of how many people are working in a development team. It allows the whole team to share the same codebase while continually integrating changes. This helps improve delivery flow and results in lower cost, higher quality code.

Trunk-based development benefits from automated testing to allow small branches to be merged, more frequently.

Scaled trunk-based development is recommended for teams of all sizes. It requires the use of short-lived branches flowing through pair programming or pull request code reviews and automated tests before merging into the trunk.

What good looks like

Development teams are able to iterate quickly, implement CI/CD and accelerate release cadence. Automated integration testing and code coverage testing are triggered when new code is merged into the trunk.

Automation is essential to ensure a steady stream of commits flows into the main branch with minimal conflict.

Trunk-based development creates a single source of codebase truth. A trunk-based development model enables:

  • Reduction in the volume of code under development
  • Continuous code integration
  • Fast, efficient code reviews
  • Frequent deployment of code to production
  • Confidence in the stability of the main branch
  • On-demand release of the codebase.

Outcomes and Risks

Potential issues

Poor quality in the code causes issues when changes are merged to the main trunk. This can occur when trunk-based development is not fully adopted.

Multiple approvals for changes – Laborious code reviews discourage developers, who then tend to batch many changes into one merge. This can lead to procrastination on the reviewer’s part due to the complexity of large code reviews. As a result, defects in deployed code are more likely to occur. Ideally, all manual approvals into production should be removed.

Delay in performing code reviews – The longer a merge is delayed, the more likely conflicts will arise in the code. Synchronous reviews of code should be prioritised over other work. Pair programming alleviates the issue because the code has already been reviewed by another person.

Lack of automation when committing code - Automated testing allows developers to get code into the main branch more quickly and encourages smaller code changes. Smaller tests and more frequent iterations are known to produce higher-quality code.

Multiple, long-lived branches - Changes queue on a branch, waiting for release to production. This can increase the risk of regression. Divergence between branches adds complexity and those risks grow exponentially by the number of branches maintained.

Action Plans

1. Prepare the repository

If your organisation has not used trunk-based development, you’ll need to prepare your repository – or trunk. You want to start with a consolidated codebase.

  • Select a single pipeline – the trunk or main branch – to flow changes into production.
  • Ensure all shared branches are merged into your main branch.
  • Release any outstanding changes through your deployment pipeline to production.

2. Plan for change

Adopting a trunk-based development model will cause disruption in your DevOps team. Not everyone will be enthusiastic about these changes. You may encounter sceptics.

The thought of automated merges will terrify some people. You will need to plan how you will progress individual changes through to production with confidence.

The goal is to achieve continuous flow by merging to trunk as quickly, yet safely, as possible. You want incremental improvement, not big bang changes.

  • Set a limit for the number of branches that can be active on the repository at any one time. Aim for three or fewer branches.
  • Discourage code freezes to encourage a constant flow of updates.
  • Require every developer to merge code at least once every 24 hours.
  • All new code requires sufficient automated tests, before it gets merged into the trunk.

3. Find bottlenecks

Bottlenecks in the deployment process can increase lead time. They can be found in manual gates or processes, like change management. They can also occur in automation in lengthy automated test cycles, for example.

Bottlenecks can be exposed by moving to smaller changes and deploying more frequently. The following activities will reduce your lead times so you can confidently deploy multiple times per day

  • Automate as much testing as possible with a focus on minimising deployment times.
  • Remove all manual steps in the deployment process. Invest in code maintainability.
  • Adopt a lightweight, clear change process.

4. Create short-lived branches

Create short-lived branches directly from main for any new work. A branch should last no longer than two days. Avoid having more than one developer or programming pair per branch.

Separate the lifecycle of branches from “feature completeness”. A developer should be able to merge partially completed work safely.

5. Add a discussion point

Use daily check-in meetings to review progress or assign outstanding pull requests. Be prepared to discuss what’s working well for you and where you’re encountering difficulties.