← Back to The book shelf
#018 v11

Every knob is a promise

/3 min read

The HTTP retry policy in the weather API is three hardcoded constants: a ten-second timeout, three retries, and a 300-millisecond base delay that doubles into a 300–600–1200 backoff ladder. Twelve-factor instinct says those belong in the environment. The decision record says no, and I've come around to its reasoning: configuration is an interface, and every knob you expose is a promise that all of its positions work.

Three constants

HTTP_TIMEOUT_MS = 10_000, HTTP_RETRY_COUNT = 3, HTTP_RETRY_BASE_DELAY_MS = 300 — compile-time constants in one small file. The decision record prices the choice without flinching: changing the policy "requires a code change and redeploy rather than a config flip — an accepted trade-off until per-tier tuning is actually needed."

The phrase doing the work is actually needed. An env var for the retry count would cost nothing today and quietly start charging later, because every value nobody sets is a combination nobody tests. A service that has only ever run with three retries does not, in any sense that matters, support five — the knob just makes the claim. Unused configurability rots because testing tracks the configurations that exist, not the ones the schema permits. The gap that accumulates between what the knobs claim and what the deploys have proven is what I'd call knob debt.

The knob that earned it

The same service's upstream feed address, ARSO_BASE_URL, went the other way: it's an environment variable, required and validated at boot. The difference isn't importance — the retry policy matters just as much to the service's behavior. The difference is a demonstrated second consumer. The e2e suite has to point the service at a fake feed, http://arso.test/feed.xml, so two configurations of that variable genuinely exist and both genuinely run, every time the tests do.

Watching that variable earn its way into the env schema is what named the rule for me. The knob wasn't designed in; it was promoted — by a consumer that actually needed the second value, on the day it needed it. Before that day, a hardcoded URL would have been exactly as correct.

Constants first, promoted on demand

The Monday audit: for each variable in your env schema, name who sets it to something non-default. A tier? A test suite? An operator who has actually turned it? If the answer is no one, fold it back into a constant — you lose nothing but the claim, and the claim was rotting anyway. Promote it again the day a real second consumer shows up, and write down who that consumer is, because that name is the justification the next reader will want.

One bound on all of this: the weather API is a single-instance service run by the person who wrote it. A fleet with an operations team prices knobs differently — when the people turning the dials can't redeploy, a config flip is worth real money, and a half-tested promise may still be worth keeping. The rule I trust is narrower than the slogan: constants first, promotion on demonstrated demand.

What I keep getting wrong is the prediction. Values I was sure would need tuning never did; the base URL I'd have happily hardcoded was the one the tests demanded. The rule tells me when to promote a constant. It still doesn't tell me which ones I'll end up promoting.

Share: