DEV Community

Sergey Shinder
Sergey Shinder

Posted on

The feature flag we forgot to delete

We shipped a big feature behind a flag, rolled it out gradually, watched the metrics, and declared victory. The feature was a success. What we didn't do was delete the flag. Eighteen months later that forgotten flag caused a production incident, and cleaning up the graveyard of dead flags it lived in took us most of a quarter.

Feature flags are one of the best tools in release engineering. They separate deploy from release, let you dark-launch, roll out by percentage, and kill a bad feature without a rollback. I'll defend them all day. But every flag is a branch in your code, and a branch that never gets removed is a branch that gets forgotten while still being live. Ours had quietly become permanent forks in the logic that nobody remembered the meaning of.

The incident was almost comic. Someone doing an unrelated config cleanup toggled an old flag whose name gave no hint it was still wired to anything. It was. A code path nobody had thought about in a year and a half woke up in production. The person who wrote it had left the company. The flag's name meant nothing to anyone alive. We spent hours reconstructing what a boolean was supposed to do, from git archaeology.

That's the hidden cost nobody budgets for. Every permanent flag doubles the number of states your system can be in. Ten forgotten flags is a thousand possible combinations, almost none of them ever tested together. You've quietly turned your codebase into a combinatorial minefield, and each new flag doubles it again.

So we made flag deletion part of releasing, not an optional cleanup that never happens. Every flag gets an owner and an expiry date when it's created. We run a job that flags the flags — anything past its expiry shows up in a report someone has to act on. A flag that's been fully rolled out for two weeks is a bug ticket to remove it and delete the dead branch. The flag isn't done when the feature ships. It's done when it's gone.

A feature flag is a loan against your codebase's complexity. Take it — they're genuinely useful — but pay it back. An unpaid flag doesn't just sit there quietly. It compounds, and eventually it comes due at the worst possible time.

– Sergey Shinder

Top comments (0)