Trunk-Based Development with Feature Flags
Long-lived feature branches are a productivity killer. They lead to merge conflicts, delayed integration, and the dreaded "big bang" merge. Feature flags offer a better way.
The Problem with Feature Branches
Traditional feature branch workflows seem sensible: work on a feature in isolation, then merge when it is ready. But in practice, this creates problems:
Merge Hell
The longer a branch lives, the more it diverges from main. A two-week-old branch can take days to merge and may introduce subtle bugs.
Delayed Integration
You do not discover integration issues until merge time. By then, the developer has moved on to other work and context-switching is expensive.
Blocked Deployments
If multiple features are in flight, you cannot deploy one without the others. Everything waits for the slowest feature.
Trunk-Based Development
Trunk-based development (TBD) is simple: everyone commits to main (trunk) frequently. Branches are short-lived, typically less than a day. The main branch is always deployable.
But wait - how do you work on features that take weeks to complete? How do you hide unfinished work from users?
Feature flags.
Feature Flags Enable Trunk-Based Development
Instead of isolating code in a branch, you isolate it behind a flag:
// Day 1: Start the feature, merged to main
if (isEnabled('new-dashboard-v2')) {
return <DashboardV2 />; // Empty shell
}
return <Dashboard />;
// Day 3: Add chart component, merged to main
if (isEnabled('new-dashboard-v2')) {
return <DashboardV2 />; // Now has charts
}
return <Dashboard />;
// Day 7: Feature complete, still behind flag
if (isEnabled('new-dashboard-v2')) {
return <DashboardV2 />; // Full feature
}
return <Dashboard />;
// Day 8: Enable for 10% of users
// Day 10: Enable for everyone
// Day 14: Remove the flag and old code The Workflow
Create the flag
Before writing code, create a feature flag in Flagit. Enable it only for your team or specific test accounts.
Commit early and often
Merge small changes to main daily. Each commit is behind the flag, invisible to users but integrated with the codebase.
Test in production
Enable the flag for internal users or beta testers. Get real feedback before the full rollout.
Progressive rollout
When ready, roll out to 1%, then 10%, then 100%. Monitor metrics at each step.
Clean up
Once fully rolled out, remove the flag and the old code path. Schedule this cleanup when creating the flag.
Benefits
No Merge Conflicts
Small, frequent merges rarely conflict. When they do, they are easy to resolve.
Continuous Integration
Every commit is tested against the real codebase. Issues are caught immediately.
Ship Anytime
Main is always deployable. Ship hotfixes without waiting for features to be ready.
Real Feedback
Test with real users before full rollout. Validate assumptions early.
Common Concerns
"Won't this add technical debt?"
Yes, if you do not clean up. Every flag should have an owner and an expiration date. Flagit tracks flag age and helps you identify stale flags. The key is to remove flags promptly after full rollout.
"What about code review?"
You still use pull requests, just smaller ones. Instead of one massive PR after two weeks, you have multiple small PRs throughout development. Smaller PRs are easier to review and less likely to have bugs.
"What if someone accidentally enables a flag?"
Use proper access controls. Flagit supports RBAC so you can restrict who can modify flags in production. Audit logs track every change.
Getting Started
Start small. Pick one feature and try the trunk-based approach:
- Create a feature flag before starting work
- Make small commits behind the flag
- Merge to main at least once per day
- Roll out gradually when ready
- Remove the flag within a week of full rollout
Once you experience the benefits, you will never want to maintain a two-week feature branch again.