Retrofitting feature flags with an expiry date onto a student records platform
Retrofitting feature flags with an expiry date onto a student records platform that wasn't allowed to stop.
Greenfield advice is easy to write and not much use. This is the same idea applied to a system already carrying production load that isn't allowed to stop, with the compromises left in view.
A flag is a temporary fork in your system that becomes permanent by default. Two flags mean four possible systems, and the combinations nobody tests are the ones that produce the incident.
THE COMBINATORIAL PART
Each flag doubles the number of configurations that exist in production. Ten flags is a thousand and twenty-four, and your test suite covers two of them: everything on and everything off. The configuration that actually breaks is some middle state a customer is in and nobody has ever run.
That's the argument for aggressive removal, and it's worth making with the number rather than the principle. Ten flags sounds tidy. A thousand configurations doesn't.
TWO KINDS OF FLAG, DIFFERENT RULES
Release flags exist to decouple deploy from launch. They're temporary by nature and should be deleted within weeks, because their whole purpose expires the moment the feature is fully on.
Operational switches are different: a kill switch for an expensive integration is legitimately permanent, and pretending otherwise means it gets removed in a cleanup and isn't there the day it's needed. Name the two categories differently so nobody has to guess which they're looking at.
The code wasn't the hard part. Convincing registrars that a second reader wouldn't cause a withdrawal was backdated after the census had been filed was, because the last project that promised that did exactly that.
MAKE REMOVAL AUTOMATIC
Relying on someone to remember doesn't work. What works is an expiry date recorded with the flag and a build that starts failing when it passes. Annoying by design, and much less annoying than the alternative.
The old code path needs deleting too, not just the conditional. A dead branch left behind is a trap for the next person, who'll reasonably assume it still runs and maintain it.
We ran both paths against live traffic for three weeks and compared every enrolment record. The mismatch rate started at four percent, all of it the old system's undocumented rounding.
WHERE IT GOES WRONG
- A dead code branch left behind after the flag is removed, maintained by mistake for years.
- Removal depending on someone remembering, which is to say not happening.
- Ten flags, a thousand possible configurations, and two of them tested.
- Release flags and permanent kill switches sharing one list, so cleanup removes the wrong ones.
Every release flag gets a death date, enforced by the build.
WHAT THE MIGRATION COST
Eleven weeks, one reverted step, nothing customer-visible. The reverted step was the one where we changed two things at once. We keep relearning that and we keep writing it down.