Flag Debt
The accumulating complexity of feature flags that are never removed, producing untested code paths and combinatorial behaviour nobody understands.
Feature flags decouple deployment from release, which is genuinely valuable: incomplete work ships dark, rollout is progressive, rollback is instant without a deployment, and experiments are possible.
The cost is that each flag is a branch in production, and flags are added far more readily than they are removed.
The consequences compound: N flags mean up to 2^N combinations, of which the tests cover a handful; a code path behind a long-disabled flag is untested and will not work when someone enables it; and reasoning about behaviour requires knowing the flag state, which is not in the code.
What keeps it under control:
A defined lifecycle. Release flags are temporary and must be removed after full rollout — with a removal ticket created at the same time as the flag, since removal never happens on goodwill.
Distinguish flag types, because they have different lifetimes: release flags (temporary), experiment flags (temporary, until the experiment concludes), operational flags such as kill switches (permanent and legitimate), and permission flags (permanent, and arguably not flags at all).
Report flag age, and treat a release flag older than a threshold as a defect.
The architectural point: the flag system becomes a synchronous dependency of every service unless designed with local evaluation, cached rules and a fail-static default when it is unreachable.