How to Use Feature Flags for Trunk-Based Development
Using trunk-based development without feature flags is like skydiving without a backup chute—no fun and unnecessarily dangerous. But to understand why, let’s first explore what trunk-based development is and how it can improve your development workflows.
What is trunk-based development?
Often held up as the ideal branching strategy for fast-paced and productive teams, trunk-based development can completely transform how your team develops and releases code. It unblocks devs, removes bottlenecks, and encourages innovation. But first, you need to be ready for it. We’ll get into this, explore its pros and cons, and uncover the answer to this Reddit user’s brilliantly blunt question: is it all just “a crock of sh*t?” in the following article.
Let’s start with where it came from. Trunk-based development is not a new branching model (it’s been in use since the mid-nineties), but it’s gained a lot of momentum in the past decade as CI/CD and DevOps tooling has improved and engineers have looked for ways to optimise workflows.
It’s an alternative to version control systems like Git Flow and GitHub Flow, which rely on long-lived development branches to develop and test code. It entails frequent (sometimes daily) integration, and, when paired with feature flags, it results in a sizable reduction in merge conflicts and the aptly named “merge hell”. It keeps your code base clean and up-to-date, accelerating CI/CD.
There are two approaches to trunk-based development:
- Using short-lived feature branches that are merged into the main (trunk) branch, with releases coming from the main branch—more common and less risky.
- Merging all your code directly into the main branch every time you deploy—less common and more risky.
We’re going to discuss the first approach, which allows you to use trunk-based development at scale, though you can use both techniques with feature flags.
Here’s a typical development workflow before and after trunk-based development:
Why is trunk-based development without feature flags risky?
Merging all of your code into a production branch is risky. There’s no way around it. You’ve got a lot of new development that ends up in a single branch, and if one of those features happens to be broken, this can break everyone else's work.
By introducing feature flags into the equation, you’re enabling a safety net—or that all-too-important backup chute. This helps keep your software bug-free and minimises panic-inducing hotfixes that have you living on the edge.
How do you use feature flags in trunk-based development?
The biggest challenge in trunk-based development is safely releasing code. Feature flags solve this by letting you decouple deploy and release. Here’s how to safely release features using feature flags.
- Create the feature flag before writing any code. This is very important—you don’t want it to be an afterthought. Successful trunk-based development with feature flags involves covering every exposure of the new feature, which is harder to do if you don’t develop with a “feature flag first” methodology.
- Before you write any business logic, create a skeleton code wrapper for where your code is going to live. Again, this emphasises considering the feature flag before writing the code, so nothing is missed.
This is what that might look like in a React Application:
- Merge to main! In theory, you could do this at any point in time, even if the feature is not 100% complete. This compliments trunk-based development's goal of cutting down PRs and merge conflicts.
- Choose when you want to release the feature. With your feature deployed and wrapped in a feature flag, you can choose when and how to release it. This also prevents product owners and QA teams from having to wait on developers, removing a common bottleneck.
Here’s an example of different steps you could take to release a feature using feature flags:
- Turn on a feature flag for a single user. Flagsmith lets you send an identity when requesting flags (this is normally your user id within your application when you log in). Once you’ve done that, you’ll be able to navigate to your user within the Flagsmith dashboard and turn on a feature flag without affecting anyone else using your application. This can be a great step to check the feature as an individual developer or as an automated test prior to releasing to your QA team and maybe interrupting their day with a broken feature.
- Release to your internal team. Flagsmith lets you define user segments—groups of users that adhere to a set of rules—for example, this could be anyone logging into your application with your company email address. Once you have defined your segments, you can turn a feature on for all of them. This enables your team to test the feature in any environment, even production, without affecting other users. We’re big believers in testing in production as it gives you a lot more confidence that everything is working.
- Release to a percentage of users or beta test groups. As well as being able to create a segment for internal users, you can create percentage-based segments, for example, 10% of your users, or a segment of customers that have opted in to a beta test program. This can be used as an additional safety measure prior to release.
- Turn the feature on for everyone. Once you’re confident the feature is stable, you can turn it on for all of your users. If the worst-case scenario does happen, and there’s a bug in your feature, you simply turn it off. You avoid the perilous bottleneck that results from other devs continuing to push to the main branch on top of the broken feature, causing a back-up of issues.
Once the feature flag has served its purpose of ensuring stability in production, you should go back and remove it from your code and delete the feature flag once the removal has been deployed.
The benefits of trunk-based development with feature flags
Fewer merge conflicts
If you have multiple environments that are getting a lot of commits—plus even more branches that are also getting commits—merges can get messy, especially with a larger team. With trunk-based development, the process is more streamlined; the code is just going directly to one branch, you can understand the state of things, rather than trying to guess how far ahead or how far behind a development branch is. You can focus on the branch that is going to be released to production—that is, the code that contains what your customers will actually see—rather than investing a lot of time and resources into other environments.
Resources saved
Massive pull requests and a lot of conflicts take up precious development time that could be better used elsewhere. As an example of this, an insurTech customer of ours, Rabbit Care, has saved countless hours moving from Git Flow to trunk-based development with feature flags. Previously, each time they had to deploy a branch, they would experience conflicts that could take 4-6 hours to solve. They now integrate continuously, deploying to production four times a day, and experience significantly fewer conflicts.
Increased development velocity
With trunk-based development, devs no longer need to wait on colleagues to push their code. This is much more efficient and results in less time wasted. This can also boost team morale and productivity because instead of concentrating on merging branches and promoting code to the main branch, they can just focus on writing code and getting it in front of customers. Rabbit Care saw this when they moved from massive pull requests averaging around 2,000 lines of code, that took months to sort out, to deploying to production multiple times a day.
Less time spent on testing
Running QA and testing in a development environment that several engineers are pushing a feature to, can lead to a lot of very expensive problems in addition to a slower development velocity.
Having a development environment rather than using trunk-based development means the goalposts are constantly being moved for QA teams. Testing small individual changes is far less complex than testing an environment that has diverged massively compared to production.
In traditional setups, there often has to be a lot more coordination between developers, QA, and product owners as the deployment of a feature is tightly coupled to features being released. Feature flagging and trunk-based development mean that the code is all ready for the QAs to test the feature when they are ready.
With the code already in production and wrapped in feature flags, you often get to choose which environment you want to test it on. This leads to teams being less reliant on development environments, which means QAs have to spend less time repeating tests on so many pre-production environments.
5 helpful rules to getting started with trunk-based development
You don’t have to have all of these in place before you start, but choose the ones that feel manageable and go from there.
- Start with feature flags. Feature flags provide the confidence needed to reduce reliance on development environments and accelerate code rollout. At Flagsmith, we've always used trunk-based development on the front end, but initially, we relied heavily on a staging environment. As we became more confident in feature flags, we started to skip staging and tested our features by enabling them for our team in production under a feature flag. As a result, we merge pull requests faster and invest more time and energy in our production environment.
- Implement end-to-end integration tests. At Flagsmith, we run a version of our website that is part of CI/CD, that is clicking through the website, pointed to production, registering an account, logging in, doing all of the things a standard user would, against production, and all of those checks are constantly running, which protects us from regression. Automated tests like this are crucial for us, trunk-based development requires us to have more confidence than pushing to development branches.
- Be sure you are confident enough to really do it. Moving to a new branching strategy can feel daunting, especially if you’re working with an archaic code base that relies on a lot of manual checks and testing to get things right. Trunk-based development is not a magic cure-all solution. You need to have put the proper time into setting up the right automations (and feature flag safety net), so that you feel confident that everything will work before you implement it.
- Be aware of the cultural change this will require. This requires more than a shift in processes, it requires a shift in thinking and the space for that. We often field questions from engineers working in large, slower-moving organisations about the best way to introduce feature flags and trunk-based development to their teams. It can be tempting to jump in head first, without first considering organisational readiness, but we recommend taking the time to map out the changes that will need to take place in order to be successful.
- Don’t bite off more than you can chew. The best way to move forward with trunk- based development will really depend on your set-up. Regardless, it’s best to explore implementation options that start small rather than moving to a new branching method in one fell swoop. An example of this could be deciding that for any new feature that’s isolated and behind the safety net of a feature flag, you will push it straight to the main branch, ignoring the other branches and then merging those changes back into your other branches. This can help get you used to the idea of pushing straight to production. Implementing a working group is another good way to introduce feature flags and trunk-based development into your organisation before introducing them more widely to your company.
Last words: Why is trunk-based development such a divisive topic?
Let’s return to the question that drove us to write this article in the first place.
Why the ire?
Probably because change can be painful.
Small teams can decide from day to day if they want to start using trunk-based development, but for larger organisations, it requires more planning.
The engineers who are against trunk-based development often can’t see the light at the end of the tunnel. Making the move will require planning, testing, and iterating. There’s real work involved—with a massive pay-off—but it will take some time to get there.
We’ll leave you with this piece of wisdom from Piyush Chauhan from Rabbit Care, on why he moved their team of 14 developers to trunk-based development from Git Flow, “In my first two weeks, I saw a couple of big PRs averaging around 2,000 lines of code. Back then, everybody worked to deliver smaller features, but nothing was delivered. It would take months to resolve things like those big PRs. So I realised, “Oh this cannot go on. Who likes to stay up at night and manage all of this?” We couldn’t agree more, Piyush.
If you’re interested in understanding how feature flags and trunk-based development could work for your organisation, talk to one of our engineers.