Flag Expiry Discipline
also called Flag Lifecycle, Stale Flag Removal
Creating every flag with an owner and an expiry date and treating removal as part of the rollout's definition of done - because flags have a creation process and, without this, no removal process.
Feature flags are added for legitimate rollouts and removed by nobody. Each removal delivers nothing visible, so it loses every prioritisation comparison, every time — and the accumulation is not benign.
Each flag doubles the notional paths through the code. A hundred flags make the system's actual behaviour unknowable: the tests cover one combination and production runs another.
Why it matters
The cost is invisible per flag and severe in aggregate, which is the profile that guarantees under-attention. It also compounds with time: removing a flag a week after the rollout is trivial, and removing one after a year requires establishing what the code path does and whether anything depends on it.
Implementation patterns
- An owner and an expiry date, mandatory at creation. A flag past its date appears on a list somebody is accountable for.
- Classify by type, because lifetimes differ:
- Release flags — temporary, removed within weeks. These are the ones that accumulate.
- Experiment flags — removed when the experiment concludes.
- Operational flags — kill switches and circuit controls, legitimately permanent and labelled so cleanups do not sweep them up.
- Permission flags — not flags at all; entitlement belongs in the permission model.
- Removal in the rollout's definition of done, scheduled when the flag is created rather than negotiated afterwards.
- Automated stale reporting with age, owner and evaluation state. A flag returning the same value for every request for three months is dead.
- Test the actual production configuration, generated from the flag service, since the deployed combination has almost certainly never been tested otherwise.
- Cache flag evaluation locally with an indefinite fallback, because a flag read from a remote service on the hot path makes that service a hard dependency of every request.
Industry example
Developer-tooling and observability platforms such as Sentry and Vercel ship continuously with heavy flag use, which makes both the benefit and the accumulation acute. The distinguishing practice is not fewer flags but an expiry that somebody owns — teams with high flag creation and high flag removal are healthier than teams with low creation, because the flags were doing useful work.
Failure scenarios
- No expiry, producing indefinite accumulation.
- Operational flags removed in a cleanup, taking a kill switch with them.
- Permission logic implemented as flags, so entitlement is scattered and unauditable.
- Flags evaluated remotely on the request path, adding a hard dependency.
- The reason for a flag lost with its author, making removal unsafe.
Trade-offs
Mandatory expiry adds friction to flag creation, and friction on a safety mechanism is a bad trade if it discourages use — a team that stops using flags because the process is annoying has lost progressive delivery.
The resolution is that the friction should be at removal-time reporting rather than at creation: creation stays one line, and the expiry field is simply required. The accountability arrives later, through the stale list, rather than as a barrier to doing the right thing.
Interview question
"You inherit a codebase with three hundred flags. What do you do in week one, and how would you tell which ones are safe to remove?"