In a lot of offices, the thermostat on the wall isn't wired to anything. Facilities mounts it because people complain about the temperature, and a dial they can turn quiets the complaints. The building runs on a schedule set somewhere nobody in the room can see. People nudge it up two degrees, feel a little better, and the air handler never hears about it.
We shipped one of those. Ours was a per-tenant flag to turn a feature off — set it, redeploy, the feature goes dark for that tenant. The toggle sat in the settings table, the admin screen showed it, and three weeks later a tenant asked why the feature was still running after they had turned it off.
Nothing read the flag. The settings row updated, the admin screen said "off," and the code path that was supposed to check it never did. The feature ran for everyone the whole time. Every test passed, because no test asserted the flag changed anything. It was a thermostat wired to nothing, and we had installed it ourselves.
The write side was done. The read side didn't exist.
The flag went in as one PR — a column, a migration, an admin toggle, an endpoint to set it. The read was meant to land in a follow-up and never did. So we had the entire surface of a feature flag with no consumer behind it.
From the outside it looked finished. You could set it, it persisted, the screen reflected it. The one thing it didn't do was the only thing it was for.
That is worse than having no flag. No flag is honest — the feature just runs, and everyone knows it. A dead flag lies. It tells the operator they have a control they don't have, and they make decisions on that lie until something forces the truth out.
A knob only exists if something reads it
A config value is not a feature. The read that consumes it is the feature. Storage, UI, and an API to set it are the packaging.
// what we had — write side complete
await db.tenantSettings.update({
where: { tenantId },
data: { featureX: false },
});
// what was missing — a read, anywhere
// if (!settings.featureX) return; <-- never written
We had spent the effort on the visible 80% — the column, the toggle, the save — and skipped the invisible 20% that was the whole point. The visible part is what gets demoed and reviewed. The read is a single if buried in a service, easy to leave for later and easy to forget you left it.
The test that would have caught it on day one
There was a test for the flag. It set the value and read it back from the settings table and asserted it was false. It tested the storage, not the behavior. It passed the entire three weeks.
The test that mattered flips the flag and asserts the feature actually changes:
test("featureX=false disables the feature", async () => {
await setSetting(tenantId, "featureX", false);
const result = await runFeatureX(tenantId);
expect(result.ran).toBe(false); // fails loudly if nothing reads the flag
});
This test can't pass unless a real code path reads featureX and branches on it. Round-tripping the value through the database proves the database works. Flipping it and checking the outcome proves the feature works. Only the second one is worth writing.
How we find the dead ones now
We stopped trusting that a setting was wired just because it existed. For every key in the settings schema, grep the codebase for a read:
# every settings key that has a write but no read is dead
rg -o "settings\.\w+" --no-filename | sort -u > /tmp/keys.txt
# then, per key, confirm at least one read outside the settings module
We ran it across 11 flags. Three had no reader — one dead like the tenant flag, two that used to have readers deleted in a refactor that left the toggles behind. Two of the three admin screens still showed those toggles, so an operator could set them and watch nothing happen.
The refactor case is the nastier one. A live flag becomes dead when its reader is deleted, and nothing complains. The toggle still saves. The screen still renders. The feature quietly reverts to its hardcoded default, and the operator keeps flipping a switch that came unwired weeks ago.
The rule
Every config knob ships with the read that consumes it and a test that flips it and asserts the behavior changes — in the same PR, not a follow-up. If you can't write that test, there is no reader, and you don't have a flag. You have a row in a table and a lie on a screen.
A knob is a contract with a reader. Ship the write side alone and you've signed one side of it. The other side is where the feature actually lives.