You’ve scrolled past new-checkout-v2 for the third time this month, and you’re fairly sure the feature behind it shipped in spring. So you raise it in planning. Your manager hears “cleanup”, translates it into “a sprint with nothing to demo”, and the item slides to next quarter, where it slides again.
This post is for the engineer who’s convinced and now has to convince somebody else. You’ll get the three numbers worth gathering first and a pitch for each person across the table: a product manager, an engineering manager, your own team. Then three templates to copy, and an expiration policy in which every clause points at a setting in the product. We spent the most time on that policy. A policy on a wiki page gets read the week it’s approved. Each clause here is backed by a setting, so the product keeps reporting on it long after that week.
Key Takeaways
- Cleanup loses planning arguments because its cost is spread thin and nothing breaks on the day a flag goes stale. The fix is a small, recurring, priced ask.
- Gather three numbers before pitching anyone: how many flags are finished, how long one removal takes, and how many new flags you create a month.
- Lead with the trend. Show whether the backlog shrinks or grows at your current pace.
- Product managers care about predictable estimates and managers about the trend. Engineers mostly need an owner and a date on each flag.
- An expiration policy works when each clause maps to something the tool records and reports, such as an owner, an expiry date or a permanent marker.
1. Why cleanup keeps losing the planning argument
On any single day a finished flag costs almost nothing. You read one more conditional during a review, a test skips one more branch, and the flags list grows by one name nobody scrolls to. Pete Hodgson described feature toggles as “inventory which comes with a carrying cost” (martinfowler.com), and inventory never announces itself: no alert fires when a release flag outlives its rollout, and nobody files a bug about it.
Add them up, though. A codebase with 120 flags, a third of them finished, drags 40 dead branches through every refactor and every new hire’s first week. Once in a while the bill arrives all at once. In 2012 Knight Capital repurposed an old flag, a server still running stale code read it, and the firm lost $460 million in 45 minutes. Our feature flag cleanup playbook tells that story in full. Give it one sentence in your pitch. Your manager has probably heard it, and a trading firm’s bad morning in 2012 won’t move anything in your sprint.
Calendar time goes to asks that are small, recurring and priced in the listener’s own units. A “cleanup sprint” fails all three. Compare “two hours a week, here’s what that buys, and here’s what skipping it costs by June”.
If you’re on Featureflip, removing flags won’t lower the invoice. Paid plans include unlimited flags at a flat price, so deleting 50 of them saves nothing on the bill. Say so up front. It moves the whole argument to the code and your team’s hours, which is where a finished flag’s cost sits anyway.
2. Gather three numbers before you pitch anyone
You need three figures. All of them come from your own flag list and your own repository, and none needs an industry benchmark.
- How many flags are finished. Filter the flags list by Stale and Dead. Stale flag detection marks a flag Stale once a signal like “serves one variation to everyone” has held for 30 days, and Dead at 90. Add the flags past their expiry date. Then subtract the kill switches and ops toggles, which look finished and aren’t (section 5 deals with them).
- How long one removal takes. Time the last flag someone removed, from opening the branch to the merged pull request. Use a real removal, because a guessed figure tends to forget the review round and the second test run.
- How many flags you create a month. Count the last three months and divide by three.
Put those into the feature flag cleanup cost calculator with your loaded hourly cost and a guess at what one stale flag costs a month while it sits. It prints every formula under its result, so anyone you show it to can check the arithmetic.
Here’s a worked example using the calculator’s own starting values, which are placeholders for you to overwrite and not benchmarks: 120 flags, 35 of them finished, 60 minutes per removal, $100 an hour, 6 new flags a month, 2 removed a month, and 5 minutes of drag per stale flag per month.
- Clearing the backlog today costs 35 hours, or $3,500, once.
- Keeping up afterwards costs 6 hours a month.
- Carrying on at the current pace costs about $5,700 in drag over the next 12 months, and the backlog grows from 35 to 83.
Nobody panics over $5,700. Put the 83 in front of them instead. Doing exactly what the team does today, the pile of finished flags more than doubles in a year.
3. One problem, three audiences
You’ll probably write the pitch in an engineer’s vocabulary, and a product manager will read it. Keep the facts and change the first sentence for whoever’s across the table.
| Audience | What they’re measured on | Lead with | The ask |
|---|---|---|---|
| Product manager | Shipping what was promised, when it was promised | Estimates get worse in code full of dead branches | A fixed slice of capacity, every sprint |
| Engineering manager or executive | Risk, cost, delivery trend | The backlog trend and the hours behind it | Approve a standing policy and a small budget |
| Your own team | Time spent on work they find worthwhile | Fewer branches to read, review and test | Own your flags, with a date on each one |
For a product manager, it’s about predictability. A feature that touches checkout touches every flag still wrapped around checkout, and each one is a question somebody answers before estimating. Was that experiment ever concluded? Is the old path still reachable? Frame cleanup as keeping the next estimate honest, then ask for a small fixed slice of each sprint. A slice is easy to plan around, while a one-off cleanup sprint has to beat the roadmap in every planning meeting where someone proposes it.
An engineering manager will want the trend and the risk. Show the chart from section 2 with your own numbers, mention Knight Capital once, and point out that the policy below costs nothing to adopt beyond the hours you’re asking for. Once a manager approves a standing rule it stays approved, and nobody has to re-argue it at the next quarterly planning.
Engineers need the least persuading and the most structure. Most of them already dislike dead flags, but nobody has given them a reason to remove one this week. Ownership does: a flag with your name on it, an end date you picked, and an email the morning after that date passes.
4. Templates you can copy
Replace everything in square brackets. Each one uses the numbers from section 2.
An email to your manager:
Subject: Proposal: two hours a week on feature flag cleanup
Hi [name],
We have [120] feature flags in [service/repo]. [35] of them have finishedtheir job: they serve one value to every user, or nobody has evaluatedthem in 90 days. We create about [6] new flags a month and remove about [2].
At that pace we'll have [83] finished flags in the code a year from now.Each one is a branch we read, test and work around in every change tothat code.
What I'm proposing:1. Clear the current backlog over the next [two] sprints. My estimate is [35] hours, based on how long our last removal took ([60] minutes).2. After that, [2] hours a week from whoever owns the flags due that week.3. Adopt a short expiration policy (attached) so every new flag has an owner and an end date.
The arithmetic is in this calculator, with our inputs filled in:[link to the cleanup cost calculator]
Could we go over it for 15 minutes this week?
[your name]A message for the team channel:
Flag cleanup, starting [date]:
- Every temporary flag gets an owner and an expiry date when it's created. The PR template now asks for both.- Kill switches and ops toggles get marked permanent so they stop showing up in cleanup lists. If you know of one, mark it or reply here.- When your flag passes its date, you'll get an email. Remove it, or move the date and leave a one-line reason.- I'll post the finished-flag count on the first Monday of each month. It's [35] today. Let's see how low it goes.A sprint proposal or ticket:
Title: Clear finished feature flags in [service] ([35] flags)
Why: [35] of [120] flags are finished (Stale/Dead in the flags list, orpast their expiry date). The count grows by about [4] a month at ourcurrent removal rate.
Scope:- Remove each flag's code: make the winning path unconditional and delete the losing one, one PR per flag.- Archive each flag only after its removal is deployed.- Mark kill switches, ops toggles and entitlement gates permanent instead of removing them.
Out of scope: refactoring the surrounding code.
Estimate: [35] hours at [60] minutes per flag, measured on our last removal.Done when: the Stale/Dead filter shows only flags still under review.The remove-then-archive order in that ticket is deliberate. Archiving a flag while code still reads it makes the SDK fall back to whatever default the code passes in, which may not be the variation users had. Flag lifecycle management explains the code-first, archive-second, delete-last sequence.
5. An expiration policy the tool can hold you to
A flag policy gets written once, agreed in a meeting and forgotten by the next reorg, because nothing reminds anyone it exists. So write clauses a tool can check. “Every flag has an owner” means something only when a filter can show you the flags that don’t have one.
Here’s a policy you can paste into your engineering handbook. Adjust the numbers, then read the table after it, which maps each clause to the control that backs it in Featureflip.
# Feature flag expiration policy
1. Two kinds of flag. Every flag is either temporary (release flags, experiments, migrations) or permanent (kill switches, ops toggles, entitlement gates, long-lived configuration). Permanent flags are marked permanent when created.
2. One owner. Every temporary flag has one named person as its owner. A team name doesn't count. The owner removes the flag when its job is done.
3. An end date at creation. Every temporary flag gets an expiry date in the PR that introduces it. Release flags: [6 weeks] after the rollout starts. Experiments: [2 weeks] after the planned end of the test.
4. A default for the rest. Each project has a default flag lifetime of [90] days, so a flag created without a date still gets one.
5. Notice. When a flag passes its expiry date, or has made no real decision in any environment for 90 days, its owner is notified.
6. Response. Within [5] working days of a notice, the owner removes the flag or moves its date and records why. A flag whose date has moved [3] times is reviewed as a candidate to become permanent.
7. Removal order. Code first, one PR per flag. The flag is archived only after the removal is deployed, and deleted after that.
8. Departures. When someone leaves, their flags are reassigned to a named person as part of removing their account.
9. Permanent flags are reviewed [quarterly]. Each one still needs a reason to exist and someone who knows what it does.
10. Monthly check. On the [first Monday] of each month, [role] posts the count of finished flags and unowned flags. Exceptions to this policy are written down, with a date to revisit them.The table maps each clause to the control behind it and the plans that include that control.
| Clause | Featureflip control | Plans |
|---|---|---|
| 1. Two kinds of flag | The permanent marker, set at creation or later. Permanent flags drop out of stale badges, the Stale and Dead filters, cleanup notices and the cleanup Action. | Every plan |
| 2. One owner | Flag owners, defaulting to the flag’s creator, with an Unowned filter in the flags list | Pro and above. Free and Team store the creator as owner. |
| 3. An end date | The Expires on field when creating a flag, and Set expiry afterwards (expiry dates) | Every plan |
| 4. A default | The project’s Default flag lifetime, from 1 to 3,650 days | Every plan |
| 5. Notice | A daily cleanup notice when a flag passes its date or goes Dead in every environment | Team and above. Owners receive it on Pro and above. |
| 6. Response | Every expiry change lands in the audit log, so moved dates are visible | Every plan |
| 7. Removal order | The flag cleanup Action opens one pull request per flag, and its scheduled sweep archives the flag once the removal is deployed | Every plan |
| 8. Departures | Removing a member offers Reassign their flags to in the same dialog | Pro and above |
| 9. Permanent review | The lifecycle filter set to Permanent, combined with Stale or Dead | Every plan |
| 10. Monthly check | Stale and Dead filters, plus find_stale_flags over the MCP server for anyone who’d rather ask an agent | Every plan |
Tell your team plainly what “enforced” means here, so the policy promises only what the tool does. Featureflip records and reports. Nothing in this list blocks a deploy, and an expired flag keeps serving exactly what it served the day before. Clause 3 is also a review rule, since the Expires on field is optional, and clause 4 is the backstop for the PRs where nobody asked. Our code review checklist has the questions to put in the PR template.
A passed expiry date also never opens a removal pull request on its own. The Action waits until traffic agrees the flag is finished. Write your policy the same way, since a date records what someone intended, and a mistyped date shouldn’t make a live flag look safe to delete.
Sizing it for your team. A handful of engineers on one project can run clauses 1, 3, 4 and 7 and skip the rest. The Free plan has no notice emails, so the Expired marker in the flags list is your reminder, and the monthly check matters more. A single team can add clause 5. On Team, notices go to your organization’s Owners and Admins, so name one of them to triage. Several teams sharing projects need clauses 2 and 8, since Pro routes each notice to the flag’s owner where Team sends them all to the same few admins.
6. Rolling it out without flooding anyone
Switch things on in the wrong order and the first week buries everyone in notices about kill switches, and somebody turns the notices off before Friday.
Week 1: mark the permanent flags. Kill switches, ops toggles and entitlement gates serve one value for months by design, so traffic analysis reads them as finished. Set the lifecycle filter to Temporary, go through the Stale and Dead flags, and mark each long-lived one permanent, which takes it out of every list and email that follows.
Week 2: give every remaining flag an owner. Filter by Unowned. The audit log shows who last changed each flag, which makes that person the first one to ask.
Week 3: set the default lifetime, carefully. The lifetime counts from each flag’s creation date, with no grace period. A 30-day default on a project full of year-old flags marks every one of them expired at once. The Default flag lifetime card previews the effect while you type (“12 temporary flags will be marked past expiry within the hour”), so start long, read the count, and shorten it over a few months.
Week 4: start removing. Work through the backlog by hand, or run the cleanup Action on a schedule and spend the hours reviewing its pull requests instead of writing them. It handles twelve languages plus ERB and Blade templates. The automated removal post covers what it refuses to touch and why.
After that, post two numbers on the first Monday of every month: finished flags and unowned flags. Put them where the team already looks, in the channel or at the top of the planning doc.
7. When the answer is still no
Sometimes the timing’s wrong, and you’ll get a polite “not this quarter”. Two smaller asks can survive it.
Mark the permanent flags and set a long default lifetime. That costs no sprint time, takes an afternoon, and works on every plan, and from then on the Stale and Dead filters show the real backlog. When you raise cleanup again next quarter, you’ll bring three months of real data where you had an estimate.
Or ride along with feature work. When a ticket touches code wrapped in a finished flag, remove the flag in the same change, which adds an hour to a ticket already in the sprint and needs nobody’s approval. Keep a tally of those removals for the next time you pitch.
The shorter version
Cleanup loses planning arguments. Its cost is spread thin, and nothing breaks on the day a flag goes stale. Count three things first. Finished flags, minutes per removal and new flags a month make the case, and the trend carries it, because a backlog that doubles in a year is the number people remember after the meeting. Change the opening line for each listener, and for engineers make it ownership. Then adopt a policy whose clauses map to things your tool records, such as owners, expiry dates, a default lifetime and a permanent marker, so it keeps working after the meeting ends. Switch it on in order: permanent flags first, owners second, the default lifetime third, removals last.
Frequently asked questions
How do I convince my manager to prioritize feature flag cleanup?
Bring three numbers from your own codebase: how many flags are finished, how long one removal takes, and how many new flags you create a month. Show whether the backlog grows or shrinks at the current removal rate, then ask for a small recurring slice of time, such as two hours a week. Once approved, a standing rule needs no second conversation next quarter.
What should a feature flag expiration policy include?
A workable policy classes every flag as temporary or permanent. Every temporary flag has one named owner and an expiry date set at creation, a project-wide default lifetime covers flags created without a date, owners are notified when a date passes, and finished flags are removed code first and archived after the removal deploys. Posting the finished and unowned counts once a month shows whether anyone is following it.
How long should a feature flag live?
It depends on the kind of flag. A release flag should come out within a few weeks of reaching 100% of users, and an experiment flag shortly after the experiment ends. Kill switches, ops toggles and entitlement gates are meant to stay, so mark them permanent and review them on a schedule. A project default lifetime, such as 90 days, is a reasonable backstop for temporary flags nobody dated.
Does an expired feature flag stop working in Featureflip?
No. Passing an expiry date never changes what a flag serves. Featureflip reports it as stale with the reason PastExpiry, marks it Expired in the flags list, and on Team and above sends a cleanup notice. To switch a flag on or off at a set time, use a scheduled change.
Will removing flags lower our Featureflip bill?
No. Paid plans include unlimited flags at a flat price, so the number of flags never changes the invoice. The case for cleanup rests entirely on engineering time and risk, which is where the cost of a finished flag sits.
Featureflip is a flat-priced feature flag platform with expiry dates, a project default lifetime and the permanent marker on every plan, so the policy above works from day one. Start on the Free plan without a credit card.