Guide

A feature flag is a runtime switch that chooses a code path without shipping a new artifact. It separates two events. A deploy puts the code on servers. A release makes the change visible to users. Flags let you do the first without doing the second.

The four kinds

Pete Hodgson's taxonomy on martinfowler.com still describes how teams use flags.

KindLifetimeOwnerExample
ReleaseDays to weeksEngineeringHide a half-finished checkout rewrite
OpsMonths to yearsEngineering or SREKill switch for an expensive recommendation call
ExperimentDays to weeksProduct and data5% of users see new onboarding
PermissioningYearsProductEnterprise tier gets SSO

Release flags let you merge and deploy continuously while the feature stays dark. Ops flags are circuit breakers you can flip faster than a rollback. Experiment flags need consistent bucketing and analytics. Permissioning flags are really product data, so treat them as durable configuration, not temporary toggles.

Walking through a ramp

The code path is a single branch.

TypeScript
if (await flags.isEnabled("checkout-v2", { key: user.id, plan: user.plan })) {
  return renderCheckoutV2(req);
}
return renderCheckoutV1(req);

Here is a safe ramp for checkout-v2. The flag starts at 0%, so the deploy changes nothing for users. Turn it on for 1% of users in one region. Watch the golden signals for 30 minutes. Then go to 10%, 50% and 100%. If p99 latency doubles at 10%, turn the flag off in seconds, with no container rollback.

When the flag has been at 100% for a week, delete the old code path and the flag in the same change. That last step is mandatory.

OpenFeature is a vendor-neutral API. Application code calls it, and the provider behind it can be LaunchDarkly, Unleash, Flagsmith or your own Redis hash. The if (flag) call sites stay the same when the provider changes.

Flag debt

Every flag is a fork in the code. Five uncoordinated flags make a combinatorial space that nobody tests. Keep the debt small.

  • Name each flag with its purpose and expiry, such as checkout_v2_release_2026q2.
  • Track flags on the backlog, and alert when a release flag is older than an agreed number of days.
  • Use short-lived release flags, and never reuse an old flag for a new purpose.
  • Test both sides of critical flags, or at least the default off path.

Warning

Knight Capital Group lost roughly $440 million in 45 minutes in 2012. A deploy reused an old flag, which activated obsolete dead code on production servers. The takeaway is that flags are production control surfaces, and stale ones are landmines. Remove them when the release is done.

Common mistakes

  • Leaving release flags in place forever. Each one is an untested branch.
  • Defaulting a flag to on in production config. A new deploy then ships the feature to everyone at once.
  • Reusing a flag name. Old code may still be wired to it.

Check yourself

  1. A release flag has been at 100% for a week. What should you do? Answer: Delete the old code path and the flag together, in one change.
  2. Why is reusing an old flag dangerous? Answer: Old code can still respond to it, which is how Knight Capital activated dead code in 2012.

Key takeaways

  • A flag separates deploy from release, and chooses a code path at runtime.
  • Use the four kinds, and give each one an owner and an expiry.
  • Ramp in small steps, and watch the golden signals at each one.
  • Delete flags when the release is done. Stale flags are a risk.

Further learning

Pete Hodgson's article on feature toggles is the source of the taxonomy above.

Feature Toggles (Pete Hodgson, martinfowler.com)Feature Toggles (aka Feature Flags)martinfowler.com

A practical talk on using flags to raise deploy frequency and separate release from rollout.

Deliver Cloud-Scale Developer Productivity with Feature Flags | BRKFP03Microsoft Developer

OpenFeature and LaunchDarkly: What are feature flags? cover the API standard and the product side.

Next, see the risk of changing the flags and settings that are read live: dynamic config and blast radius.