Does your team dread release days? Do your customers complain that upgrades break more things than they fix? Is your team burnt out trying to shove disjointed code together before each deployment? You’re not alone. Many development teams get bogged down trying to coordinate complex feature branches. Months of hard work get stuck waiting in pull request purgatory. Unfortunately, our team is in that camp, too, with Gitflow branching and release model.
Recently, I stumbled upon the idea of trunk-based development, which has been quite popular across the industry. Tech leaders like Google, Amazon, and Facebook use this approach to foster rapid production cycles without compromising quality. Still, it seems applicable to mid-sized teams, not just enterprises. Engineers integrate work frequently in small increments by developing directly on the main code branch. Features get refined iteratively through constant feedback instead of festering alone on separate branches.
This approach does sound risky, but when combined with continuous integration and delivery, it can increase stability while boosting release velocity. Instead of long-lived feature branches, small changes flow smoothly into the mainline branch daily. Automated testing and coordination protect quality despite increased change frequency. The result? Developers spend more time building customer value and less time untangling knots of merged code.
Escaping Pull Request Purgatory
Your talented engineers have been working hard to build great new features for your product, but your users want to see them faster.
Lately, your deployments have slowed to a crawl. It takes weeks for a new feature to go from initial code commit to being rolled out. Product managers are frustrated that their shiny new features remain stuck in development limbo. Users wonder why they aren’t seeing frequent upgrades with additional value. Developers work in isolation on complex feature branches, often waiting days or weeks to integrate their changes with the main codebase. Before each release, you suffer a painful integration phase trying to merge huge feature branches into the development branch. Bugs emerge constantly, setting back the timeline even further.
The long lead time from development to deployment makes it hard to respond quickly to customer needs. Your team culture focuses more on committing code rather than delivering value. Your product changes reach customers in big batch updates rather than in a smooth stream matched to their changing needs.
Isn’t there a smoother path to production? There must be a better approach to getting code out faster without compromising quality.
Getting To Know Trunk-Based Development
Release days can be brutal. All those features you’ve coded away on for weeks get jammed into one huge merge. Of course, bugs creep in. Next thing you know, you’re scrambling to fix issues. Not exactly delightful, but very familiar process.
There seems to be a better way: trunk-based development. Instead of keeping changes isolated on separate branches that take forever to develop, you regularly commit little code tweaks directly to the main branch multiple times per day. We’re talking small enough that integrating them is smooth as butter.
To make sure nothing breaks, you run automated tests on each merge to catch issues early so quality stays high even with frequent contributions.
This trunk gets deployed straight to users regularly. We’re talking about constant deliveries with little upgrades rather than the old “ship it once every X weeks”
Trunk-based development depends on everyone constantly committing small improvements to one main code branch. By splitting work into bite-sized bits merged often, you get a stream of new functionality delivered regularly via automated pipelines.
Feel the Flow with Trunk-based Development
Adopting a trunk-based approach can make a world of difference for your team. Instead of long periods of isolation developing complex features and then a period of integration, engineers collaborate continuously as they integrate code changes. Syncing work daily becomes effortless without coordination troubles.
Everyone gains much clearer visibility as you share a single mainline branch. It becomes way easier to stay in sync with what teammates are building. Constant collaboration brings you together.
Feedback loops become much faster when changes get integrated daily. Bugs rarely compound since they’re detected rapidly. Issues get swarmed and resolved promptly before things snowball, at least in theory.
Deployment velocity accelerates dramatically thanks to the main branch containing releasable code at any time. No more scrambling to stabilize branches before each release.
Plus, adopting trunk-based development can supercharge continuous delivery, making life easier for everyone. Instead of relying on your team to pull off risky big-bang merges and upgrades, have developers merge smaller changes several times daily. This gives your deployment tools a nice smooth flow of improvements to work with.
Choosing Between Trunk-based and Gitflow: What’s Best For You?
First, ask yourself — how complex are the features you’re building? If tackling major, multi-layered initiatives, having isolated branches with Gitflow may make more sense. It stops tangled code and helps big teams coordinate. But if you mostly release smaller enhancements, a trunk-based approach will remove the unnecessary overhead.
Also, think through your releases. For platforms with multiple active versions out in the wild, Gitflow keeps different change streams separated better. But on simpler systems with fewer long-term branches, trunk-based means improvements reach users faster without branch barricades.
Finally, consider your team and culture. Gitflow’s cautious flow assures quality but slows speed. Less disciplined squads find safety in restricting main branch changes.
While weighing all that — complexity, releases, culture — keep your goals for velocity, quality, and support in mind. Balance stability, speed, and maintenance when picking isolated or integrated development. And know that there’s no exact formula that works for everyone. Even expert organizations evolved their approaches over time.
Making Trunk-based Development Work
Shifting to trunk-based development initially feels uncomfortable, like trying on a new pair of shoes.
The first thing to change is working in small chunks instead of big, complex feature branches. It’s less risky that way.
Second, you should review each other’s code changes diligently — fresh eyes will catch things you can’t see in your work. But, reviewing small chunks of code and implementing small changes is much easier than reviewing massive pull requests accumulated over weeks. Especially if you use linting tools to enforce code style.
Third, your engineers need to get into the groove of merging work to the main branch daily rather than waiting to batch it up. This avoids the dreaded code reviews and merge hell down the road. This requires a whole new habit — making sure the new code doesn’t break what came before. Keeping things backward compatible is key.
Oh, and lean heavily on test automation if you don’t do that yet. Without extensive automated tests in place, it’s impossible to pick up the pace because it’s impossible to do manual regression tests daily on a decently complex system.
Our Path To Continous Integration
For years, continuous delivery and integration for me were about running the automated tests on GitHub Actions and deploying the update in an automated way. Now, I finally realize that it’s much more than that.
Trunk-based development, loosely-coupled architecture, automated testing, sub-teams, and, as a result, continuous delivery and integration are the key parts of the software development process for a mid-sized team.
These approaches make little sense when you have a small team during the startup phase, but you painfully need them as you grow.
Many engineering management books talk a lot about the end result, but the evolving processes are rarely covered.
Our team has a lot to learn. We are yet to try out the true trunk-based approach. It has two key prerequisites we don’t have yet — decent automated test coverage replacing manual testing and loosely coupled microservices architecture.
There’s a lot to figure out. For example, I want to know how splitting work into small batches works for features beyond adding a button. Maybe loosely coupled architecture is the answer here. When you have a monolith, you build features spanning many system parts. In contrast, when everything is split into separate branches, engineers can work on parts of the system in parallel as long as they maintain backward compatibility.
Originally published on Medium.com