Stale flags accumulate because removing one is rarely anyone's explicit job. Give every flag an owner and an expiry at creation, review them on a cadence, and make removal a tracked work item rather than a cleanup chore – ideally in the same backlog as the story that created the flag, so it can't quietly outlive it.
How feature flags become stale
The pattern is familiar. A team ships a feature behind a flag, the rollout finishes, the flag gets left at 100%, and everyone moves on. The code path stays in the codebase and the flag stays in the platform, both waiting for someone to notice.
It's not laziness. "Remove the flag" was never anyone's assigned task in the first place. For example, a 2021 study of 12 open-source Python projects (Hoyos et al., Empirical Software Engineering) found that 75% of feature-toggle components were removed within 49 weeks. That still left a quarter in place after nearly a year, reinforcing the need for an explicit cleanup process. Some of that remaining quarter is by design. Kill switches and other permanent toggles aren't meant to come out. The rest is exactly the kind of debt this article is about.
Why this is getting worse
Teams are shipping more than ever. Faros AI's 2026 analysis of two years of telemetry across 22,000 developers and more than 4,000 teams found that as AI-assisted coding adoption rose, task throughput per developer increased 33.7% and epics completed per developer rose 66%. More shipping alone doesn't mean more debt, just more code. But the same analysis found that pull requests merged with no review at all, human or automated, were up 31.3%. Faros attributes that gap to reviewer capacity failing to keep pace with the new volume, not a change in review policy.
The cost of that gap shows up downstream. The same analysis found bugs per developer up 54% and the incidents-to-PR ratio up 242.7%, more than tripling relative to the low-adoption baseline. No study counts feature flags specifically, but they're part of that same code, shipping faster and drawing less scrutiny.
When review can't catch everything, feature flags become a more important safety net. A flag is a control that still works after code ships. If a rollout causes problems, the team can turn the flag off without waiting for another release. That reframes flag hygiene from a tidiness problem into a safety layer. A flag with no owner, no expiry, and no review cadence isn't just clutter anymore. It's a safety mechanism nobody's maintaining, at exactly the moment teams are relying more heavily on controls that work after merge.
The four practices that work
Give every flag an owner at creation. The owner doesn't necessarily have to remove the flag personally, but someone should be accountable for deciding when it's served its purpose and for making sure cleanup actually gets scheduled.
Give temporary flags an expiry or review date. Release flags and experiment flags are meant to be temporary; permission and ops flags aren't. Deciding which one you're creating, and writing down roughly when the temporary ones should come out, turns "we'll clean it up eventually" into something with a date attached, even if that date lives in a comment or a ticket instead of a dedicated field.
Review flags on a cadence. Someone has to actually look at what's stale, on a schedule, rather than discovering it during an incident. What matters here is less the tool and more that the review actually happens, whether that's weekly, per sprint, or whatever fits your team.
Track removal as work. This is the practice most teams skip. A flag getting flagged as stale doesn't remove itself. Someone has to turn "this looks unused" into a scheduled piece of work with an owner and a place in the backlog. Treating removal as its own untracked side-task is exactly how flags survive 49 weeks past the point everyone agreed they should go.
How the tools handle stale flags
Most feature flag platforms already do some version of stale-flag reporting, so a "stale" indicator alone isn't much of a differentiator anymore. What separates them is whether the tool proactively alerts someone, rather than waiting for a person to check a dashboard, and whether it opens a pull request (PR) removing the flag's code itself instead of leaving that to a person. The table below checks eleven platforms teams commonly use against those two questions, verified against each vendor's own current documentation:
Tool | Detects automatically | Generates cleanup PR | Owner/team alerting | Signal | Where you see it |
✓ | ✓ | None | 30+ days old, inactive or launched status for 7+ days | Dashboard and four automated pre-archive checks | |
Harness FME (formerly Split) | ✓ | ✓ | None | Based on selection criteria you write yourself (e.g. "100% rolled out," "no longer targeted"), not a fixed day count | Import a ready-made pipeline that scans automatically for cleanup candidates, or add a ready-made stage to clean up a flag you've already picked; either way, one PR per run |
✓ | ✗ | Configurable | Past a configured expected lifetime | Technical-debt dashboard, state filter | |
✓ | ✗ | None | No environment change in 30+ days | Orange "Stale" tag, filterable | |
✓ | ✗ | Configurable | No config change in a set number of days | "Zombie Flags" dashboard table | |
✓ | ✗ | Configurable | Unmodified 14–30 days, released at 100% for 14+ days, or zero evaluations for two weeks | Dashboard alert and in-feature banner | |
✓ | ✓ | Automatic (to owner) | Zero checks in 30+ days | Reminder on the flag's page | |
✓ | ✗ | None | Unchanged 2+ weeks, one-sided rollout | "Stale" badge with hover-reason text | |
✓ | ✗ | None | Not evaluated in 30+ days, or 100% rollout with no targeting for 30+ days | Filterable dashboard | |
✓ | ✗ | None | Ruleset not "Running," unmodified 30+ days | Automatic "Stale" status, filterable | |
✗ | ✗ | None | No automated signal | Manual "Search Code References" tool, (Premium/Ultimate only) | |
✓ | ✗ | None | Not evaluated in 30+ days | Flag Status field, on the story or the flag's configuration screen |
Where Atono is different
Atono's own row above shows that it doesn't currently generate cleanup PRs or proactively alert someone about stale flags. What Atono does differently isn't the detection.
Atono ties the flag to the story, not to a separate tool. It's where the flag lives. Atono is a product development platform built around a loop. A story moves from spec to build to test to release to what happened after, and the flag that controls a rollout stays attached to that story the whole way through, instead of living in a separate tool bolted onto the backlog.
Dedicated flag platforms can connect a flag to the work and analytics behind it too, usually through an integration or a metadata field. Optimizely, for instance, supports a custom link field that can hold a Jira ticket URL. In Atono, that connection is native rather than assembled. The story, its acceptance criteria, the rollout flag, the resulting engagement data, and its eventual removal are already part of the same work record, not fields wired together after the fact.
That connection addresses another part of the problem, understanding what a flag was for and acting on it safely. An incident caused by a flag nobody remembered is really a research problem. Someone has to reconstruct why the flag exists before deciding whether flipping or removing it is safe, and when the acceptance criteria, the rollout configuration, and the usage data are already sitting on the same story as the flag, much of that reconstruction is already done. We think that also makes removal more likely to happen. Cleanup competes against every other unplanned task, and it loses more often when working out what a flag was for means opening a second system. That's our reasoning rather than a measured result – nobody has published data on it.
Feature flag removal checklist
This works regardless of which platform you're on.
[ ] Confirm the rollout or experiment is complete. The flag should be serving its intended final value to the relevant audience, not just sitting at a value that happens to look stable.
[ ] Check evaluation data in every environment. A flag can look dead in production while still active in staging. If user behavior matters too, check feature usage data separately. Evaluation data measures whether the flag was checked. Usage data measures whether people used the feature it controlled, and it's easy to conflate the two.
[ ] Search the codebase for every reference. Include tests, config files, and documentation, not just the application code.
[ ] Confirm no other flag depends on this one. Some platforms let one flag list another as a prerequisite, and removing the wrong one first can break that dependency.
[ ] Decide which behavior survives. Remove the conditional and the now-dead branch from the code. Don't just delete the flag and leave the branching logic behind.
[ ] Deploy the change. Verify the surviving behavior in production before touching the flag's configuration.
[ ] Update the story or ticket that introduced the flag. Make sure the record reflects what actually shipped and what got removed.
[ ] Check for the flag in anyone's dashboard or saved report. Give them a heads-up before it disappears.
[ ] Archive or delete the flag's configuration in your platform. This keeps it from showing up in every future stale-flag report.
FAQ
What is feature flag debt?
The accumulation of feature flags that stay live in your code and your flag platform after the decision they supported is over, usually because removing a flag was never assigned to anyone as work.
How long should a feature flag live?
A temporary flag should live only as long as the rollout, experiment, or migration it's tied to requires. Set a review or expiry date when you create it, then remove it once the final behavior is established and you've confirmed the old code path isn't needed. Operational and permission flags are meant to remain long-term. There's no universal number of days that applies across tools or teams.
Who owns removing a feature flag?
Whoever was assigned ownership when the flag was created, per the first practice above. When that didn't happen, the team that owns the code path the flag gates is the fallback, but tracking that down after the fact is exactly the situation assigning ownership up front is meant to avoid.
How do I find unused feature flags?
Start with your flag platform's own reporting if it has one. Most current tools (LaunchDarkly, Split/Harness FME, Unleash, Flagsmith, ConfigCat, DevCycle, Statsig, GrowthBook, PostHog, and Optimizely) surface staleness automatically. Then confirm with a code-reference search across the whole repository, including tests and config, since evaluation data alone can miss a flag that's referenced but rarely hit.
Make your product work flow
Shared context from first decision to feature usage