Tags: web-dev experimentation concept
Feature Flags
Date: 2026-08-16
A runtime conditional that separates deploying code from releasing behaviour. It’s the practice that makes everything else here possible — gradual rollout, instant rollback, experimentation, trunk-based development — and the debt it creates is real and rarely paid.
What it is
A feature flag is a check evaluated at runtime that decides whether a code path is active, configurable without a deploy.
if (flags.isEnabled('new-checkout-flow', user)) {
return <NewCheckout />;
}
return <LegacyCheckout />;The consequence is the whole point: code can ship to production switched off. Deployment becomes a low-risk technical event; release becomes a separate business decision, made later, by someone else, reversibly.
The four kinds
They have different lifespans and mixing them up is the usual source of flag debt.
| Kind | Purpose | Lifespan |
|---|---|---|
| Release | Ship incomplete work off, enable when ready | Days to weeks — remove after |
| Experiment | Assign variants for a test | The test’s duration — Rollouts as Experiments |
| Operational | Kill switch for an expensive or fragile feature | Long-lived, deliberately |
| Permission | Entitlement by plan, role or region | Permanent — arguably not a flag at all |
Only the first two are temporary, and only the first two get cleaned up — in theory. Recording the kind at creation is what makes cleanup possible later.
What it unlocks
- Progressive Delivery — 1%, 5%, 25%, 100%, watching guardrails between steps
- Instant rollback. Turning a flag off is seconds; redeploying is minutes and carries its own risk — Rollback and Forward Fix
- trunk-based development — merge unfinished work behind a flag rather than maintaining a long-lived branch
- Experimentation. The same machinery assigns variants — though a flag rollout is not an experiment unless a comparison group is held back concurrently, see Rollouts as Experiments
- Decoupled release timing. Marketing launches when the campaign is ready, not when the deploy window is
The debt
Every flag is a branch in the code, and branches multiply.
3 flags → 8 possible states
10 flags → 1,024
20 flags → over a million combinations, none of which are tested
Consequences that show up within a year: untestable combinations, dead code nobody dares delete, if statements whose original purpose is unrecorded, and flags left permanently on that nobody realises are still evaluated.
The discipline that works:
- Record an owner and an expiry date at creation. A flag without both is technical debt on day one
- Remove the flag as part of finishing the feature, not as a follow-up ticket. Follow-up tickets don’t happen
- Alert on stale flags — anything past its expiry date, or fully rolled out for a month
- Cap the number of active flags and treat exceeding it as a blocker
- Default to off. A flag whose default is on isn’t protecting anything
Implementation
Decisions that matter more than the vendor choice:
- Where it’s evaluated. Server-side avoids the flicker and blocking problems of client-side entirely — Client-Side vs Server-Side Testing, Flicker and Flash of Original Content
- What happens on failure. If the flag service is unreachable, the answer must be a safe local default, not an exception and not “on”. A flag service that can take down the site has inverted its purpose
- Latency. Evaluation must be local and synchronous after an initial fetch. A network call per flag check is unusable
- Consistency. The same user must get the same answer across page loads and across services — deterministic hashing rather than stored state — Assignment and Bucketing
- Auditability. Who turned what on, when. This is the first question after an incident
Where it bites
- Flags in the data layer or analytics. Not recording which flags a user had makes any behavioural analysis unreadable — Experiment Assignment Tracking
- Flags controlling database schema. They don’t. Schema changes need their own approach — Database Migrations
- Flags as configuration. Anything that varies permanently by environment is config, not a flag
- Testing. CI should test the state you intend to ship, and at minimum both states of any flag currently mid-rollout