I've argued before that retiring feature flags is worth doing deliberately. The argument holds. The problem is that agreeing with it changes nothing, because the work stays a chore: find every reference, work out what the flag controls, figure out which paths go dead, clean them up without breaking anything. None of it is hard. It's fiddly enough to keep losing to whatever else is on that week.

So I've been building an AI-assisted flow to take the fiddly part out. Most of what's interesting about it isn't the automation. It's the two constraints around the automation: the rule that decides which flags it's allowed to propose removing, and the rule that decides who ends up responsible for the change.

What it actually does

The agent works through a small set of standardised skills rather than being handed a codebase and trusted to be sensible.

The first skill goes to the feature-flag service (LaunchDarkly, in our case) and establishes the flag's real state rather than its assumed one: whether it's enabled in a given environment, whether it has variations, and whether it's a candidate for removal at all. That last judgement is the one everything else depends on, and it's deliberately conservative.

If the flag clears that bar, the next step is a plan: what removing it would mean across every codebase that references it, not just the one you happen to have open. That includes the effect on tests: which cases exist only to exercise a branch that's about to stop existing, and what that does to coverage once they go. The output is a plan a developer can act on, not a set of changes already made.

The rule that makes it safe

The guardrail that does the most work is narrow and boring: a flag with variations is flagged as not safe for removal.

Variations mean the flag is still doing something. It might be mid-rollout, it might be splitting traffic for an experiment, it might be serving different values to different segments for a reason nobody wrote down. In every one of those cases the flag has a live job, and the honest answer to "can this go?" is no, regardless of how dead the code around it looks, and regardless of how confidently a model could argue otherwise from reading the source.

That rule is worth more than any amount of cleverness in the analysis, because it fails in the safe direction. The cost of wrongly excluding a removable flag is that somebody removes it by hand later, which is exactly the situation we were already in. The cost of wrongly removing a live one is a production behaviour change nobody asked for, arriving inside a cleanup pull request where no reviewer is looking for it. Those two errors are not remotely symmetric, and the tool should be built as though they aren't.

This is the part I'd tell anyone building something similar to spend their time on. Getting a model to trace references and spot dead branches is the easy half now. Deciding what it is not allowed to conclude is the half that determines whether you can leave it running.

Who owns the change

The second constraint is about people rather than code, and it turned out to matter more than I expected.

The developer is shown the implications of removing the flag and remains responsible for orchestrating the removal. Every pull request that comes out of the process is authored by the developer, not by the agent. That isn't ceremony. A pull request carries an implicit claim: I have read this and I believe it's correct. That claim has to belong to somebody who can actually make it. An agent-authored PR quietly moves that claim nowhere, and review gets thinner precisely when the change is mechanical enough to skim.

So the agent produces understanding and a plan. The person produces the change.

What actually changed

Here's the part I didn't anticipate when I started.

The bottleneck was never really the mechanics. It was that flag removal was distributed across code owners, and it only happened when each of them independently got round to it. A flag might be dead in three repos and live in a fourth, and the person who most wanted it gone (whoever introduced it, shipped the feature, and would happily never think about it again) had no way to finish the job themselves. They could ask. Then they could ask again.

With the analysis and the plan handled, that changes. Removal can be orchestrated end to end by the flag's owner, across all the codebases at once, which is the only way you get the guarantee that actually matters: that the flag is gone everywhere rather than mostly gone. What used to take weeks of waiting on other people's priorities now takes a day or two.

The speed is nice. The completeness is the real win. A flag removed from three of four codebases isn't 75% removed, it's still there, and the conditional you left behind is now the confusing kind: the one that disagrees with its own name in the other three repos.

What I can't tell you yet

Two honest gaps.

The first is that I can't give you a failure rate. It gets things wrong rarely, which is the kind of claim I'd be sceptical of if someone else made it, and the sceptical reading is fair: "rarely" is what you say before you've counted. The conservative guardrail means most of its errors should be refusals rather than bad removals, and refusals are cheap and invisible, which is comfortable and also exactly the condition under which you'd fail to notice a pattern. Counting properly is the obvious next thing.

The second is that everything above describes a tool being used by the person who built it, which is the easiest possible case. The packaging work (standardising the skills, tightening the prompts so results are consistent, making it runnable without knowing how the thing is wired underneath) was aimed at someone else picking it up. Whether that survives contact with a developer who has no investment in it, and who meets its refusals as obstacles rather than as design, is not a question I can answer from here.

What I'd defend is the shape. A tool that reads a system, forms a view, and hands a person a plan they have to sign their name to is a more comfortable thing to leave running than one that reads a system and starts making changes. Not because the model can't be trusted with the mechanics (it mostly can) but because the mechanics were never the part that was going wrong.