← All posts
Backend4 min read

Feature flags need an exit plan

Flags make releases safer, but temporary control paths become permanent complexity unless removal is part of the original work.

The release flag was created for a two-week rollout. Eighteen months later it controls three code paths, nobody knows which customers are in the treatment group and deleting it feels riskier than leaving it alone.

This is how a safety mechanism becomes permanent ambiguity.

Write the obituary when you create the flag

Every temporary flag should begin with enough metadata to remove it:

YAML
key: checkout.new-confirmation
type: release
owner: team-checkout
created: 2026-08-31
expires: 2026-09-21
success: 100% rollout with no conversion or error regression for 7 days
cleanup: ENG-4821
fallback: existing confirmation screen

The expiry date is not an automatic deletion date. It is the moment at which the owner must remove, extend or deliberately reclassify the flag. The cleanup ticket exists before launch because removing the old path is part of delivering the new one.

Different flags need different controls

Calling everything a “feature flag” hides important differences.

Release flag

Short-lived. Separates deployment from exposure and gradually moves traffic to a new implementation. It should disappear after the rollout is stable.

Operational flag

Changes system behaviour during degradation or an incident. It may live longer, but needs tested defaults, restricted access and a runbook. A kill switch that nobody has exercised is wishful thinking.

Experiment flag

Allocates subjects consistently and protects measurement. Changing eligibility mid-experiment or losing the targeting key can invalidate the result.

Entitlement

Controls a durable commercial or permission boundary. Model it as governed configuration, not temporary release scaffolding. It deserves audit, ownership and tests appropriate to access control.

OpenFeature calls the data used for targeting an evaluation context. The specification supports user, application, host and other ambient attributes, but that does not mean every attribute should be sent. Minimise personal data and keep targeting inputs predictable.

Decide failure before the provider fails

OpenFeature's evaluation API requires callers to supply a default value; its no-op provider returns that default when no provider is available. That is a useful design pressure.

For each flag, ask:

  • What happens before the SDK has initialised?
  • What happens when evaluation times out?
  • Can a cached value cross accounts or environments?
  • Does the default preserve safety, availability or compatibility?
  • Can an older app understand every returned variation?

There is no universal choice between open and closed. A promotional banner can safely disappear. A fraud-control path may need the more restrictive behaviour. The point is to decide before an outage decides for you.

A rollout example

Consider a replacement checkout confirmation screen:

  1. Deploy the inactive code and verify both paths in production.
  2. Enable it for the delivery team using a stable targeting key.
  3. Move to a small random cohort.
  4. Compare journey completion, client errors and support contacts by variation.
  5. Increase exposure in deliberate stages.
  6. Hold at full exposure long enough to cover normal traffic patterns.
  7. Delete the old path, flag, variation analytics and obsolete tests.

If the old screen remains reachable after step seven, the rollout is not complete.

Evaluation telemetry should record the flag key, variation, reason, application version and outcome correlation without logging the full evaluation context. This allows an incident responder to ask “did failures cluster in the new path?” without turning the flag system into another source of customer data.

Search for dead decisions

Run a regular inventory, but do not rely on inventory day to create lifecycle discipline. Useful signals include:

  • flags past their review date;
  • flags fixed at one value for an entire environment;
  • code references to deleted control-plane flags;
  • flags evaluated but never affecting an outcome;
  • owners who no longer exist;
  • experiment flags without an active analysis.

Our backend work uses flags to make change reversible, not to keep every historical implementation alive. The safest production path is usually the one with fewer unexplained branches.

Feature flags create options. Delivery includes closing the options the team no longer needs.

Sources

Filed under

  • Feature flags
  • Release strategy
  • Technical debt
  • Experimentation

Rather we just built it?

Start a project