Engineering December 5, 2025 6 min read

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

1

Create the flag

Before writing code, create a feature flag in Flagit. Enable it only for your team or specific test accounts.

2

Commit early and often

Merge small changes to main daily. Each commit is behind the flag, invisible to users but integrated with the codebase.

3

Test in production

Enable the flag for internal users or beta testers. Get real feedback before the full rollout.

4

Progressive rollout

When ready, roll out to 1%, then 10%, then 100%. Monitor metrics at each step.

5

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:

  1. Create a feature flag before starting work
  2. Make small commits behind the flag
  3. Merge to main at least once per day
  4. Roll out gradually when ready
  5. 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.