DEV Community

Cover image for Feature Flags vs Feature Branches: When to Use Each
Domenico Giordano
Domenico Giordano

Posted on • Originally published at rollgate.io

Feature Flags vs Feature Branches: When to Use Each

I used to think feature branches were the only way to manage releases. Then I joined a team that used feature flags with trunk-based development, and I never looked back. Turns out, the "feature flags vs feature branches" debate is a false dichotomy — here's how to think about it.

The False Dichotomy

Developers often frame feature flags vs feature branches as an either/or choice. In reality, they solve different problems and work best together. Feature branches manage code integration. Feature flags manage feature release. Understanding this distinction is key to a healthy deployment workflow.

Whether you call them feature flags, feature toggles, or feature switches, the core idea is the same: decouple deployment from release. And whether you use Git Flow, GitHub Flow, or trunk-based development, branches are about managing code changes. Once you internalize that these tools operate at different layers, the "feature branch vs feature toggle" debate dissolves.

Feature Flags vs Feature Toggles: Are They the Same?

Before we go deeper, let's clear up a common source of confusion. You'll see "feature flags," "feature toggles," "feature switches," and "feature gates" used across blog posts, documentation, and conference talks. They all refer to the same concept: a conditional check in your code that controls whether a piece of functionality is active.

Martin Fowler popularized the term "feature toggle" in his 2010 article. The term "feature flag" gained traction in the DevOps community and is more common today, especially in SaaS tooling. Some teams use "toggle" for simple on/off switches and "flag" when targeting rules and percentage rollouts are involved, but this distinction isn't universal.

Throughout this article, we use "feature flag" and "feature toggle" interchangeably. If you've been searching for "feature branch vs feature toggle," you're in the right place.

For a complete introduction to the concept, see our guide on what feature flags are and how they work.

What Are Feature Branches?

Feature branches are a Git workflow where each new feature is developed on a separate branch. When the feature is ready, the branch is merged into the main branch (usually main or develop).

git checkout -b feature/new-payment-flow
# ... work on the feature ...
git push origin feature/new-payment-flow
# Create a pull request, get reviews, merge
Enter fullscreen mode Exit fullscreen mode

Advantages of Feature Branches

  • Isolation: Changes are isolated until the branch is merged
  • Code review: Pull requests enable structured review
  • CI checks: Automated tests run on each branch before merging
  • Familiar: Every developer knows Git branching

Problems with Feature Branches

  • Long-lived branches diverge: The longer a branch lives, the harder the merge. Conflicts accumulate, and integration becomes painful.
  • Delayed feedback: You don't know if your feature works in production until it's merged and deployed.
  • All-or-nothing releases: Merging a feature branch means releasing it. There's no way to deploy the code but hide the feature.
  • Merge conflicts: Large branches touching many files create merge hell, especially on active codebases.

The Hidden Cost of Long-Lived Feature Branches

The problems above aren't theoretical. Long-lived feature branches impose real, measurable costs on engineering teams.

Merge Conflict Escalation

Research from DORA (DevOps Research and Assessment) consistently shows that teams practicing trunk-based development with short-lived branches ship faster and with fewer failures. The reason is compounding: every day a branch stays open, the probability of a merge conflict increases non-linearly. A branch open for 2 days might have a 10% chance of conflict. At 2 weeks, that number can exceed 80%, depending on team size and codebase activity.

Context Switching Tax

When a developer opens a PR for a 3-week-old branch and receives 47 review comments, the original context is gone. The developer has moved on to other work. Rebuilding mental context to address feedback on stale code is one of the most expensive activities in software development. Studies suggest context switching can cost 20-25 minutes per interruption.

Integration Risk Compounds

Consider a team of 6 developers, each working on a separate feature branch for 2 weeks. When all six branches merge in the same sprint, the integration surface is enormous. Features that worked in isolation break when combined. This "merge day" anti-pattern is why many teams dread release weeks.

Delayed Bug Discovery

A bug introduced on a feature branch stays hidden until merge. If your branch diverged from main 10 days ago, the bug has had 10 days to become entangled with other changes. Compare this to trunk-based development where the same bug would surface within hours, when it's cheap to fix and the context is fresh.

The alternative is clear: keep branches short (hours to days, not weeks) and use feature flags for long-running features that span multiple merges.

What Are Feature Flags?

Feature flags wrap new functionality in a conditional check. The code is deployed to production but only activated when the flag is enabled. This decouples deployment from release.

import { useFlag } from '@rollgate/sdk-react';

function PaymentPage({ user }) {
  const showNewPayment = useFlag('new-payment-flow');

  if (showNewPayment) {
    return <NewPaymentFlow />;
  }
  return <CurrentPaymentFlow />;
}
Enter fullscreen mode Exit fullscreen mode

On the backend, the same pattern works in any language. Here's Go:

package main

import (
    "net/http"
    rollgate "github.com/rollgate/sdk-go"
)

func paymentHandler(w http.ResponseWriter, r *http.Request) {
    user := getUserFromContext(r)
    ctx := rollgate.NewContext(rollgate.WithUserID(user.ID))

    if rollgate.IsEnabled("new-payment-flow", ctx) {
        handleNewPaymentFlow(w, r)
        return
    }
    handleCurrentPaymentFlow(w, r)
}
Enter fullscreen mode Exit fullscreen mode

Advantages of Feature Flags

  • Deploy anytime: Code goes to production even if the feature isn't ready for users
  • Gradual rollout: Enable for 1% of users, then 10%, then 100% — learn more in our gradual rollouts guide
  • Instant rollback: Disable a flag in seconds, no redeployment needed
  • Targeting: Show features to specific users, teams, or segments
  • Trunk-based development: Everyone commits to main, reducing merge conflicts
  • Scheduled releases: Combine flags with time-based rules for scheduled feature releases

Problems with Feature Flags

  • Code complexity: Two code paths mean more logic to maintain
  • Stale flags: Unused flags accumulate as technical debt
  • Testing surface: You need to test both flag states
  • Requires discipline: Flags need to be cleaned up after full rollout

How Companies Like Google and Netflix Use Trunk-Based Development

Trunk-based development with feature flags isn't a niche practice. It's how the world's most productive engineering organizations ship software.

Google

Google operates one of the largest monorepos in existence, with over 80,000 engineers committing to a single repository. Long-lived feature branches would be impossible at this scale. Instead, Google uses trunk-based development where all engineers commit to head. Incomplete features are guarded by flags (internally called "experiments" or "flags") that keep them invisible until ready. This approach allows Google to make over 60,000 commits per day without breaking the build.

Netflix

Netflix deploys hundreds of times per day across its microservices architecture. Their engineering blog describes how every feature is wrapped in a flag before reaching production. This allows them to test new recommendation algorithms, UI changes, and encoding pipelines with real traffic at controlled percentages. When a new video player feature caused buffering issues in 2019, they disabled it for all users in under 30 seconds — something a rollback deployment would have taken minutes or longer.

Meta (Facebook)

Meta ships code to production twice daily using a system called Gatekeeper, their internal feature flag platform. Every new feature — from News Feed ranking changes to Messenger updates — goes through a flag-controlled rollout. Engineers can target by geography, device type, employee status, or custom segments. This system handles billions of flag evaluations per day.

Spotify

Spotify uses feature flags extensively for their "controlled rollout" process. New features are first exposed to internal employees ("dogfooding"), then to a small percentage of users in a single market, then expanded globally. This layered approach catches issues at each stage before they reach the full 500+ million user base.

The pattern is consistent across these organizations: short-lived branches for code management, feature flags for release management.

When to Use Feature Branches

Feature branches are the right choice when:

  • Short-lived work (1-3 days): Bug fixes, small features, refactors that can be reviewed and merged quickly
  • Code review is essential: You want a structured PR workflow with approvals
  • No production risk: The change doesn't need gradual rollout or targeting
  • Open-source contributions: External contributors need isolated branches for PRs

When to Use Feature Flags

Feature flags are the right choice when:

  • Gradual rollout needed: You want to release to a percentage of users first
  • Long-running features: Work that spans multiple sprints or weeks
  • Production testing: You need to verify behavior in production before full release
  • Kill switch required: The feature could impact system stability
  • User targeting: Different users should see different experiences
  • A/B testing: You're running experiments between variants

The Best Approach: Use Both

The most effective teams use short-lived feature branches combined with feature flags:

  1. Create a short-lived branch for a small piece of work (1-2 days)
  2. Wrap new UI or behavior behind a feature flag
  3. Merge to main quickly — the flag is off, so nothing changes for users
  4. Deploy to production — the code is there but hidden
  5. Enable the flag gradually when the full feature is ready
  6. Clean up the flag once the feature is at 100%

This gives you the best of both worlds:

  • Short branches avoid merge conflicts and integration pain
  • Feature flags give you release control without deployment pressure
  • Trunk-based development keeps the main branch always deployable
  • Continuous delivery becomes natural, not scary

Trunk-Based Development with Feature Flags

Trunk-based development (TBD) is the practice of keeping branches short (hours to days) and merging to main frequently. Feature flags are what make TBD safe for large features:

Day 1: Merge backend API behind flag -> Deploy -> Flag off
Day 2: Merge UI component behind flag -> Deploy -> Flag off
Day 3: Merge integration tests -> Deploy -> Flag off
Day 4: Enable flag for internal team -> Test in production
Day 5: Rollout to 10% -> Monitor metrics
Day 7: Rollout to 100% -> Remove flag
Enter fullscreen mode Exit fullscreen mode

Each merge is small, easy to review, and safe to deploy. The feature grows incrementally in production without affecting users until you decide it's ready.

Here's a concrete Node.js example of a flag-guarded API endpoint that you can merge to main safely on Day 1:

import Rollgate from '@rollgate/sdk-node';

const rollgate = new Rollgate({
  apiKey: process.env.ROLLGATE_SERVER_KEY,
});

app.post('/api/checkout', async (req, res) => {
  const user = req.user;
  const useNewCheckout = await rollgate.isEnabled('new-checkout-flow', {
    userId: user.id,
    attributes: { plan: user.plan, country: user.country },
  });

  if (useNewCheckout) {
    return handleNewCheckout(req, res);
  }
  return handleLegacyCheckout(req, res);
});
Enter fullscreen mode Exit fullscreen mode

Migration Guide: From Feature Branches to Feature Flags

If your team currently relies on long-lived feature branches, migrating to a flag-based workflow doesn't have to happen overnight. Here's a practical step-by-step approach.

Step 1: Pick One Feature as a Pilot

Don't try to convert your entire workflow at once. Choose a medium-complexity feature that's about to start development. This gives you a real scenario to learn from without risking critical work.

Step 2: Set Up Your Flag Service

You need a way to create and manage flags. You can start with environment variables or a config file, but a dedicated service like Rollgate gives you a dashboard, targeting rules, and audit logs from day one.

Step 3: Adopt the Branch + Flag Pattern

Instead of a single long-lived feature/new-dashboard branch, break the work into small PRs (each 1-2 days):

# PR 1: Add the flag and the empty shell
git checkout -b add-dashboard-flag
# Create the flag in Rollgate, add the conditional check
# Merge to main -- flag is OFF, no user impact

# PR 2: Build the data layer
git checkout -b dashboard-data-layer
# Add API endpoints behind the flag
# Merge to main -- still invisible to users

# PR 3: Build the UI
git checkout -b dashboard-ui
# Add components behind the flag
# Merge to main -- still invisible

# Ready? Enable the flag for your team first, then gradually roll out.
Enter fullscreen mode Exit fullscreen mode

Step 4: Establish Flag Hygiene Rules

Agree on team conventions before flags accumulate:

  • Naming: Use a consistent pattern like feature.dashboard-v2 or release.new-checkout
  • Ownership: Every flag has an owner responsible for cleanup
  • TTL: Set an expected removal date when creating the flag
  • Cleanup sprint: Dedicate time each sprint to removing fully-rolled-out flags

Step 5: Expand to the Full Team

Once the pilot is complete, share lessons learned and repeat the pattern. Most teams report that within 2-3 sprints, the flag-based workflow feels natural and the benefits are obvious: fewer merge conflicts, faster releases, and fewer incidents.

Real-World Comparison

Aspect Feature Branches Only Feature Branches + Flags
Branch lifetime Days to weeks Hours to days
Merge conflicts Frequent Rare
Release control None (merge = release) Full (gradual, targeted)
Rollback speed Minutes (redeploy) Seconds (toggle flag)
Production testing Not possible Easy
A/B testing Not possible Built-in

FAQ

Can I use feature flags without a feature flag service?

Yes. The simplest feature flag is an if statement checking an environment variable:

if (process.env.ENABLE_NEW_DASHBOARD === 'true') {
  showNewDashboard();
}
Enter fullscreen mode Exit fullscreen mode

This works for basic use cases, but it has limitations. Changing a flag requires redeployment, there's no gradual rollout, no user targeting, and no audit trail. As your team grows and you manage more flags, a dedicated service pays for itself in reduced deployment risk and developer time. Most teams start with env vars and upgrade to a flag service once they hit 5-10 flags.

Do feature flags slow down my application?

A well-implemented feature flag adds negligible overhead. The flag evaluation itself is a simple conditional check — microseconds at most. Modern SDKs cache flag values locally and sync in the background, so evaluations don't make network requests on the hot path.

In Go, a cached flag evaluation looks like this:

// This reads from local cache -- no network call
enabled := rollgate.IsEnabled("new-feature", ctx)
// Typical evaluation time: <1 microsecond
Enter fullscreen mode Exit fullscreen mode

The performance concern is valid only if your implementation makes a remote API call on every evaluation — which no production-grade SDK does.

How do I handle database migrations with feature flags?

Database migrations are one of the trickier aspects of flag-based development. The key principle is: your database schema must support both code paths simultaneously.

Here's a practical approach:

  1. Additive migrations only: Add new columns or tables, never remove or rename existing ones while the flag is active
  2. Dual-write pattern: When the flag is on, write data to both the old and new schema. When off, write only to the old schema
  3. Backfill after full rollout: Once the flag is at 100% and removed, run a migration to clean up the old schema
async function saveOrder(order: Order) {
  // Always write to the existing table
  await db.orders.insert(order);

  // When the flag is on, also write to the new normalized tables
  if (await rollgate.isEnabled('normalized-orders')) {
    await db.orderHeaders.insert(order.header);
    await db.orderLines.insertMany(order.lines);
  }
}
Enter fullscreen mode Exit fullscreen mode

This approach avoids the scenario where you roll back a flag and lose access to data written in a new format. Both code paths can read from the same source during the transition.

Conclusion

Feature branches and feature flags aren't competing approaches — they're complementary tools. Use feature branches for code management and review. Use feature flags for release management and safety. Together, they enable fast, safe, continuous delivery.

If you're still doing long-lived feature branches with big-bang releases, consider adding feature flags to your workflow. Start with one flag on your next feature and experience the difference. The teams at Google, Netflix, and Spotify didn't adopt this pattern because it was trendy — they adopted it because it works.

Get started with Rollgate — free tier, no credit card required.


I'm building Rollgate, a feature flag platform for developers. If you have questions about feature flags, drop them in the comments — happy to help!

Top comments (0)