A flag is a runtime branch with operational consequences. It can save a launch—or create two applications nobody knows how to test. The difference is discipline, not the flag provider.
Every flag needs five things or it turns into debt: an owner, a precise audience, a default-safe state, telemetry for both branches, and an expiry date. Release flags get deleted after rollout. Entitlement and kill-switch flags are different species and live under different rules.
Name the job before naming the flag
| Flag type | Example | Lifetime |
|---|---|---|
| Release | new_checkout to 5% of users | Days or weeks |
| Kill switch | Disable a costly AI call | Potentially long-lived |
| Experiment | Compare two onboarding flows | Until decision is made |
| Entitlement | Enterprise-only export | Product lifetime |
Treating every flag as a permanent boolean is how if (flags.foo) spreads into thirty files with no one willing to delete it.
Roll out by evidence, not vibes
if (flags.enabled("new_checkout", { userId, plan })) {
return checkoutV2(request);
}
return checkoutV1(request);Start with internal accounts, then a small stable cohort, then a wider percentage. Track the user-visible metric and the failure metric for both paths. A flag with no metric answers only “did we turn it on?”, which is not enough.
A flag is not a substitute for a migration plan. If the new path writes data the old path cannot read, the flag merely lets you choose when to break compatibility.
The deletion checklist
- Set the winning behaviour as the code default.
- Remove the losing branch and tests that only supported it.
- Delete the flag definition and dashboard targeting.
- Verify no clients cache an old value that changes safety behaviour.
The best feature-flag dashboard has a shrinking list. If a team cannot state the removal date at creation time, it is probably configuration or an entitlement—model it that way instead.
Avoid flag evaluation drift
Evaluate a request-scoped flag once and pass the decision through the operation. Reading a remote flag again halfway through a checkout can route one request across two incompatible behaviours. For client flags, never expose rules or attributes that reveal another user's entitlement; send only the evaluated value when the provider supports it.
Make rollback boring
Predefine the safe value, owner, and metric threshold before rollout. A rollback that requires a code deploy is not a kill switch. Conversely, do not put irreversible schema writes behind a percentage flag until old and new readers can coexist.
Cover photo by Nik Oak on Pexels.
