What Is a Feature Flag? An SRE's Answer, With Code

Summary

What is a feature flag? It is a conditional in your code whose value is read at runtime, so you can turn a code path on or off without deploying. That separates shipping code from releasing it. For SRE teams the payoff is a smaller blast radius: expose 1 percent, watch the error rate, step up or revert in seconds. The cost is flag debt, so every flag needs a type, an owner and an expiry date.

Operations desk at night with two monitors and a panel of physical toggle switches in the foreground

It is 03:12 and the checkout error rate has just crossed 4 percent. The change that caused it shipped six hours ago. Nobody wants to revert the whole release, because it also carries two unrelated fixes. So what is a feature flag, in this situation? It is a conditional in your code, read at runtime, that decides whether a code path runs. Flip it off and the new path stops executing, with no build and no deploy. That is the entire idea. Everything else is operations.

What a feature flag actually is (and what it is not)

A feature flag is a decision point. Somewhere in the code there is an if, and the value it reads comes from outside the binary: a config service, a database row, a vendor SDK, an environment variable you can change without a restart.

const useNewPricing = await flags.isEnabled("new-pricing-engine", {
  userId: user.id,
  plan: user.plan,
  region: request.region,
});

return useNewPricing
  ? newPricingEngine.quote(cart)
  : legacyPricing.quote(cart);

Three things follow from that snippet. The old path still has to exist and still has to work. The flag needs context (who is asking) if you want anything smarter than on or off. And the evaluation sits on the hot path of a request, so its latency and its failure mode are your problem now.

People use "feature toggle" and "feature flag" interchangeably. The word does not matter. The behavior does: you separate deploying code from releasing it. Code goes to production dark, and a person or a system decides later who sees it.

The four kinds of flag, and why mixing them hurts

Most flag pain comes from treating all flags as one thing. Martin Fowler's taxonomy of feature toggles splits them by how long they live and how dynamic the decision is. It holds up in practice.

Release toggles are transitionary. They hide unfinished work behind a flag so you can merge to trunk daily. Fowler's guidance is that they should rarely outlive a week or two. The decision is static: every request for a given release gets the same answer.

Experiment toggles route different users to different variants. They stay up long enough to reach statistical significance, hours to weeks, and no longer. The decision is per request.

Ops toggles control operational behavior: shed a non-critical feature under load, switch to a fallback dependency, disable an expensive query. A few of them become long-lived kill switches. These need to flip in seconds, which rules out anything that requires a release.

Permission toggles gate features by plan or entitlement. They can live for years, and the decision is per user on every request.

The mistake is giving all four the same lifecycle. A permission toggle needs an owner and an audit trail forever. A release toggle needs a deletion date the day it is created. If you cannot tell which kind a flag is by looking at its name, you will not be able to tell which ones are safe to remove.

Hand pulling a red kill switch lever on a dark control cabinet

Why SREs care: flags as a blast radius control

For a platform team the interesting property is not A/B testing. It is that a flag turns a release from an event into a dial.

Without flags, a deploy exposes 100 percent of traffic to the change at once. The only revert is another deploy, and that revert takes as long as your pipeline takes, plus the time it takes a human to be sure. With flags, you can expose 1 percent of users, watch the error rate and p99, then move to 5, 25, 50. The blast radius is whatever percentage you chose, and the revert is a config change that propagates in seconds.

That changes your mean time to recover more than most tooling investments do. The post-mortem question stops being "why did it take 40 minutes to roll back" and becomes "why did we not have this behind a flag".

A rollout dial has a catch. Someone has to watch the signals and turn it. At 03:12 that someone is tired. The useful version of a flag system ties the dial to a service level objective: if the error rate burns through a set share of the budget, the rollout halts and reverts without a human in the loop. Most teams build this by hand with a webhook from their alerting tool to the flag API. It is a few hundred lines and it pays for itself the first night it fires.

The bill comes due: flag debt and the Knight Capital lesson

Every flag you add doubles the number of paths through that code in theory. In practice you test a handful of combinations and hope. Fowler recommends treating flags as inventory with a carrying cost: add a removal task to the backlog when the flag is created, put expiration dates on them, and in the strictest teams make the build fail when an expired flag is still in the code.

The expensive version of this lesson is Knight Capital. On August 1, 2012, a new deployment reused an old flag that had once controlled a retired feature. One server missed the deployment and still ran the old code. In the first 45 minutes after the open, the firm's router sent more than 4 million orders into the market while trying to fill 212 customer orders, as the SEC's order against Knight Capital records. Fowler's article points to the loss, around $460 million, as a cautionary tale about flags you do not manage.

The flag was not the only failure there. Manual deployment, no kill switch, and slow detection all contributed. But the reuse of a dead flag name is the detail every platform team should keep on a wall. Never repurpose a flag. Delete the old one, create a new one, and make removal a first-class step.

Notebook with hand-drawn flow diagrams beside a keyboard and laptop on a wooden desk

Build it yourself or buy a flag platform?

A homegrown flag system starts as a config file and a boolean. That is fine for the first dozen release toggles on one service. It stops being fine when you need percentage rollouts that stay sticky per user, targeting rules by segment, an audit log of who changed what, and a flag change that reaches every instance in seconds without a thundering herd on your config store.

At that point you are comparing platforms on measurable criteria: SDK evaluation latency (local evaluation versus a network call), streaming versus polling for updates, targeting rule granularity, multi-environment support, and how the pricing meter scales with your traffic.

The meters differ more than the feature lists do.

LaunchDarkly is the reference implementation of the category: mature targeting, guarded releases, and a deep integration surface. Its usage-based plans bill per service connection and per thousand client-side monthly active users, so a consumer app with a large anonymous audience can see the bill move in ways a backend-only team will not. The positive case is real: if you need governance and audit at enterprise scale, it has had the longest time to harden it.

Statsig bundles flags with experimentation and product analytics, and bills on events. That suits teams that treat flags mostly as an experiment surface. If your use case is mostly ops toggles and kill switches, you are paying for analytics you may not read. Note also that the vendor has changed owners twice since September 2025, which is worth a line in your vendor risk review.

Whichever you pick, test the failure mode before you sign. Block the SDK's network access in staging and check what the application does. A good SDK serves the last known flag values from cache and logs loudly. A bad one blocks the request path.

Observability is half of the flag system

A flag without telemetry is a guess. The minimum useful setup attaches the flag state to every trace and log line for the request, so that when an error spike appears you can group by flag variant and see whether the new path is the cause.

Datadog and similar all-in-one platforms make the per-variant breakdown easy once your SDK tags spans, with the caveat that per-host and per-signal billing grows quickly when you add high-cardinality tags such as flag key plus variant plus user segment. Be deliberate about which dimensions you tag.

Grafana is the cheaper path if you already run Prometheus and OpenTelemetry. You will build the flag-aware dashboards yourself, which costs engineering time but gives you full control of the labels you emit. For a team of 50 to 500 engineers with a platform group, that trade is often right.

Engineer walking down a server rack aisle lit in blue and amber

A short runbook for your first flag in production

If you are introducing flags to a team that has none, keep the first rollout boring.

  1. Pick one change with a clear success metric, such as checkout error rate or p99 latency of one endpoint.

  2. Name the flag with its type and owner, for example release.checkout.new-tax-calc.owner-payments.

  3. Write the removal ticket now and attach an expiry date no more than two weeks out.

  4. Roll out at 1 percent, hold for one full traffic cycle, then step up. Define in advance the threshold at which you revert.

  5. Make the revert a single command or button, and rehearse it once in staging before you need it at night.

  6. After 100 percent has held for a few days, delete the flag and the old code path in the same change.

The unglamorous part is step 6. Teams that skip it end up with a few hundred flags, no one who knows what half of them do, and a Knight-shaped risk sitting in the repository.

What would your last incident have looked like behind a flag?

Take the last production incident your team wrote up. Ask three questions. Could the change have gone out dark? Could one person have disabled it in under a minute without a deploy? Would an alert have told you which variant was failing?

If the answers are no, no, and no, you have your first flag candidate and your first honest estimate of what a revert costs today. The post-mortem will say what it says. The better question is how much of it you could have made smaller by the time the pager went off.

Frequently asked questions

What is a feature flag in simple terms?
A feature flag is a runtime condition in your code that decides whether a code path runs. The value comes from outside the binary, such as a config service or vendor SDK, so you can change behavior without a new deploy.
What is the difference between a feature flag and a feature toggle?
In practice, nothing. Both names describe the same mechanism. Some vendors use toggle for the practice and flag for the code-level implementation, but teams use the terms interchangeably.
What are the main types of feature flags?
Four are commonly distinguished: release toggles for hiding unfinished work, experiment toggles for A/B tests, ops toggles for runtime control such as kill switches, and permission toggles for plan or entitlement gating. Each has a different expected lifespan.
How long should a feature flag live?
It depends on the type. Release toggles should usually be removed within a week or two. Experiment toggles live as long as it takes to reach significance. Ops kill switches and permission toggles can live for years, with a clear owner.
What is a kill switch in software?
A kill switch is a long-lived ops flag that lets an operator disable a feature or dependency instantly in production, without a deploy. It is used to degrade gracefully under load or to stop a failing change.
Should we build our own feature flag system or buy one?
A config file and a boolean work for a few release toggles on one service. Once you need sticky percentage rollouts, segment targeting, audit logs and fast propagation across instances, compare platforms on SDK latency, update mechanism, targeting granularity and how the pricing meter scales.
Can feature flags cause outages?
Yes. Reusing an old flag name, leaving dead code paths in place and untested flag combinations are common causes. Knight Capital's 2012 incident involved a repurposed flag. Treat flags as inventory with expiry dates and never repurpose one.