← Academy index
Chapter 1 · 9 min read

What is a Feature Flag?

The anatomy of a feature flag, the four jobs flags actually do, and why decoupling deploy from release is the single biggest unlock in modern delivery.

fundamentalsconcepts

A feature flag, sometimes called a feature toggle, is a conditional in your code whose value can be changed without redeploying. That’s it. The technology is unglamorous. What it unlocks is not.

if (await flags.get("new-checkout", user)) {
  return renderNewCheckout(user);
}
return renderLegacyCheckout(user);

Strip that snippet down and a feature flag is three things stitched together: a boolean (or richer value) controlled remotely, a targeting rule that decides who sees what, and a fast path to read it in production. Everything else in the category, dashboards, audit logs, SDKs, edge caches, exists to make those three things safe at scale.

The four jobs a flag actually does

People often talk about feature flags as if there’s one canonical use case. There isn’t. There are four, and they have different lifecycles, different risk profiles, and different owners.

1. Release flags

Decouple deploy from release. Ship the code to production behind an off flag. Turn it on for one user, then ten, then ten thousand. If something explodes, flip it off and you’re rolled back without a redeploy.

Release flags are short-lived. They exist for the week, month, or quarter it takes to roll a feature out. After that they should die. (See Chapter 6 for how that actually goes.)

2. Ops flags (kill switches)

A kill switch is a flag wrapped around something that has nothing to do with shipping a new feature, usually a dependency on something that can fail. Third-party email provider. Recommendations service. AI call you’d rather not make on Black Friday.

Ops flags are permanent. They live as long as the system they protect, and they earn their keep on the worst day of the year, not the best.

3. Experiment flags

A flag that splits traffic into variants for the purpose of measurement. The flag is now coupled to a metric pipeline: assignment events, exposure logs, statistical significance.

Experiment flags often look identical to release flags in code. They are not the same thing operationally. (See Chapter 5.)

4. Permission / entitlement flags

if (org.plan === 'pro') modeled as a flag instead of hardcoded. Useful when entitlements change frequently, when you want plan changes to take effect instantly, or when sales needs to grant a one-off override for a specific customer.

Permission flags are also permanent. They tend to outlive most of the engineers who created them.

The crucial part: a single flag system can serve all four. The crucial trap: teams confuse them, leave release flags in forever, and end up with a config layer they’re scared to touch. Chapter 6 again.

The anatomy of a single evaluation

When your code asks “is this flag on for this user?”, a lot is happening, or it had better be, because you do not want to be the team that adds 80ms to every page load for the sake of a boolean.

A correct evaluation needs:

  • A flag definition, the rules, segments, defaults, and variants set in your dashboard.
  • A user context, at minimum a stable identifier; usually attributes too (plan, country, device, beta-opt-in, whatever).
  • A deterministic decision, same user + same rules should always return the same answer. No surprises across sessions.
  • A telemetry write, so you can later answer “who saw what variant, when?”

That last one matters more than it looks. Without exposure logging, your experiments are vibes and your incident reviews are guesses.

Boolean, multivariate, JSON: three shapes of flag

Most flags are booleans. on or off. Most are good with that.

Multivariate flags return one of several strings, useful for A/B/n tests and for swapping providers (stripe | adyen | braintree).

JSON flags return structured config, feature limits, copy strings, full UI configurations. Powerful, and the source of most flag-system foot-guns. The instinct to turn every config value into a flag is one of the patterns Chapter 6 calls out.

const checkout = await flags.get("checkout-config", user);
// { provider: "stripe", retries: 3, applePay: true }

If you find yourself with a JSON flag larger than a postcard, you almost certainly want a config service instead.

What a flag is not

A feature flag is not a config file. Config files are checked in, code-reviewed, and shipped with a build. A flag changes in seconds without a deploy. Those are different tools.

A feature flag is not an A/B test. A test is a flag plus a measurement plan. Skipping the measurement turns experiments into wishful thinking.

A feature flag is not a permissions system. Plans, roles, and SSO mappings need durable, auditable models. Modeling them as flags works for the first ten, and then you wish you hadn’t.

A feature flag is definitely not a place to put secrets. Anything you’d be unhappy seeing in a screenshot doesn’t belong here.

Why “ship silently”?

The point of all this is that the interesting moments, the launch, the rollout, the rollback, stop being build events. They become a checkbox in a dashboard at a time that suits you, not your CI.

The boring deploy is the whole game. Boring deploys are how you ship daily. Daily ships are how you out-iterate your competitors.

The rest of this Academy is about how to get there without painting yourself into a corner.


Next: Chapter 2, Architecture: where flags live