Safeguard
Developer Tools•

Feature flag debt: why every flag needs an expiry date

Old feature flags hide dead code, untested paths and forgotten access rules. How to inventory, expire and remove flags before they become a risk.

Safeguard Security Team
4 min read

Every feature flag should have an owner, a purpose and an expiry date. Flags that outlive their purpose leave dead code paths, untested combinations and forgotten access rules in production. Treat flags as inventory with a carrying cost, and remove them on a schedule.

The cost

Martin Fowler's feature toggle article says toggles add abstractions, conditionals and testing work, and should be treated as inventory with a carrying cost. Release toggles, which hide unfinished work, are meant to last a week or two. If they stay for a year, you have two versions of that feature to maintain, and nobody remembers which one real users see.

Why this is a security issue

Untested paths. Code behind a long forgotten flag runs when someone flips it. If the flag has been off for a year, that code has missed a year of dependency updates, refactors and security fixes.

Hidden exposure. A flag meant for internal testing that is still on for a wide group is an access control decision nobody reviews.

Confusion in incidents. During an outage nobody can say what the system is actually doing, because behavior depends on the state of dozens of flags.

Dead code that still ships. Disabled code is still in the build, and still part of what scanners and attackers see.

Practices that work

Fowler's article describes several, which fit together well:

  • Add a removal task when you create the flag. Put it in the backlog on day one.
  • Set expiration dates. Some teams make a test fail or the application refuse to start once a flag is past its date.
  • Cap the number of flags. A new flag may require removing an existing one.
  • Manage by category. Release, experiment, ops and permissioning toggles have different lifespans and owners.

A flag inventory in one table

Keep a simple list with these columns:

  1. Name
  2. Category: release, experiment, ops or permissioning
  3. Owner, a person or team
  4. Created date
  5. Expiry date
  6. What off means and what on means
  7. Who can change it

A spreadsheet is enough to start. Review it monthly, and delete any row you cannot explain.

Removing a flag safely

  1. Confirm the flag has been fully on (or fully off) in production for a set time.
  2. Remove the losing branch of the code, not just the flag check.
  3. Remove tests for the dead path.
  4. Deploy and watch metrics.
  5. Delete the flag from the flag system.

Do the code removal in a small pull request on its own, so it is easy to review and revert.

Which flags may live forever

Some kill switches and permissioning flags are meant to persist. That is fine, as long as they are in the inventory, have an owner and are tested. Fowler's guidance is to test the on and off states of any toggle that could go live, and the same goes for these.

Safeguard's SBOM and dependency scanning covers code whether or not a flag enables it, and reachability analysis helps separate what is exercised from what is merely present. The free scan is at gold.safeguard.sh/scan.

Quick check

How long should a release flag live?

Fowler describes release toggles as lasting a week or two, sometimes longer when driven by product needs. Set an expiry when you create it.

Is deleting the flag enough?

No. Delete the unused code path and its tests too, or you keep the risk and lose the switch.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.