Feature Flags and Progressive Delivery
Concept
Deploying code and releasing a feature are two different events, and conflating them is a common source of risk. A feature flag (feature toggle) is a runtime conditional — if (flags.newCheckout.enabled(user)) { ... } — that decouples the two: code ships to production dark, then gets turned on independently, for whichever subset of traffic you choose, without a redeploy. That decoupling is what makes progressive delivery possible: instead of a binary "deployed to everyone" moment, a feature rolls out along a controlled gradient — internal users, then 1% of traffic, then 10%, then 100% — with real production signal (error rates, latency, business metrics) gating each step.
Martin Fowler's taxonomy is a useful way to think about why a flag exists, because different flag types have very different lifecycles:
- Release toggles — hide incomplete work behind a flag so it can merge to trunk continuously without being user-visible yet. Short-lived by design: removed within days to weeks once the feature ships.
- Experiment toggles — route different users to different code paths for A/B testing. Lifespan matches the experiment.
- Ops toggles — operational kill switches (disable a non-critical dependency, throttle an expensive code path under load). Long-lived, intentionally — this is a resilience mechanism as much as a delivery one, closely related to the circuit breaker.
- Permissioning toggles — gate a feature by plan tier, org, or role. Long-lived by nature, effectively part of the product's authorization logic.
Progressive delivery techniques built on top of flags include canary releases (a small percentage of real traffic, watched against automated success metrics before widening — see Blue-Green and Canary Deployments), ring deployments (internal employees → beta users → general availability, each ring a wider blast radius), and targeted rollout by user attribute (region, account age, device) to de-risk a change for the specific segment most likely to surface problems.
Tradeoffs
| Approach | Blast radius on a bad release | Rollback speed | Coupling of deploy and release |
|---|---|---|---|
| No flags — deploy = release | Full user base immediately | Requires a redeploy or revert | Fully coupled |
| Feature flags, binary on/off | Still full user base once flipped on | Instant — flip the flag off | Decoupled |
| Progressive rollout (%-based, ring-based) | Bounded to the current cohort | Instant, and never reached full exposure if caught early | Fully decoupled, gradient-controlled |
The cost side is real: flags add combinatorial testing surface (every live flag roughly doubles the number of code paths in play), and a codebase with stale, never-cleaned-up flags becomes a maze of dead conditionals that nobody's sure are safe to remove. A flag management platform (LaunchDarkly, Flagsmith, or a homegrown service) adds an external dependency in the request path — its own availability and latency now matter, and a naive implementation that blocks on a flag-evaluation network call turns a config lookup into a new source of production risk.
When to use / when not to
- Use release toggles to enable trunk-based development — merge unfinished work continuously behind a flag instead of maintaining long-lived feature branches that rot and produce painful merges.
- Use percentage-based or ring-based rollout for any change with meaningful blast radius (new checkout flow, changed pricing logic, a rewritten core algorithm) so a regression is caught at 1% of traffic, not 100%.
- Use ops toggles as a deliberate resilience lever for expensive or non-critical dependencies you may need to shed load from under incident conditions — decide and build these before the incident, not during one.
- Don't leave a release toggle live past the rollout — the discipline of removing a flag once it's fully rolled out (or fully rolled back) is what prevents the "flag graveyard" failure mode; treat flag removal as part of the feature's definition of done.
- Don't gate correctness-critical logic behind a flag evaluated with weak default-safe behavior — a flag service outage or timeout should fail toward the safe state (usually "old behavior" or "off"), not an undefined one.
Common pitfall
Accumulating flags that never get cleaned up. Every release toggle that outlives its rollout becomes permanent conditional complexity: another branch every future contributor has to reason about, another combination that testing may or may not actually cover, and — eventually — nobody remembers whether it's safe to delete or what depends on which side of it. The fix isn't tooling, it's process: track flag age, alert on flags past an expected lifetime, and make flag removal a required step in the rollout's own checklist, not a someday cleanup task. The second common pitfall is treating a flag evaluation as free — a synchronous network call to a flag service on every request, with no local cache or safe-default fallback, turns a config check into a new single point of latency and failure.
Engineering Lens
Feature flags are the architectural embodiment of decoupling decision from deployment — one of the more transferable ideas in this whole knowledge base. Once you can flip behavior without a deploy, a whole category of release risk (would this regression have been caught before hitting everyone?) becomes a solved problem, and it opens the door to genuinely data-driven rollout decisions instead of "we deployed, let's hope." The Principal-level nuance to bring to a design review is the lifecycle discipline: flags are cheap to add and expensive to leave lying around, so a proposal that introduces flags without a stated removal plan is an incomplete proposal. This connects directly to canary deployment mechanics and to the broader operational-excellence theme of making changes reversible by default — the same instinct that shows up in database migrations, DNS cutovers, and infra rollouts: always leave yourself a fast way back.
Related
- Blue-Green and Canary Deployments
- Circuit Breaker Pattern
- SLIs, SLOs, and Error Budgets
- Design a Feature Flag / Progressive Delivery Platform