Config Changes Are Deploys
Code goes through a gauntlet. It gets written on a branch, reviewed by someone else, run against a test suite, built, staged, rolled out gradually, and watched. Config, in most organizations, goes through a text field and a save button. And yet a config value can change what production does just as completely as code can — it can disable a feature, redirect traffic, change a timeout from three seconds to three hundred, or set a limit so low that the system starts rejecting legitimate work. The behavior of a running system is a function of both its code and its configuration, but only one of those two inputs is treated as dangerous.
The reason for the asymmetry is understandable. Config exists precisely so that things can be changed without a deploy — that’s the point of it, and being able to turn something off in ten seconds during an incident is genuinely valuable. But the value comes from speed, and speed was purchased by removing the checks. What often gets lost is that removing the checks was supposed to be a trade for a specific class of change: small, reversible, well-understood knobs. It quietly becomes the path for everything, including changes that would never have passed review if they’d been written as code, because they arrive through a channel where review isn’t part of the flow.
The failure mode this produces is specific and recognizable. Something breaks in production, the logs look normal, the recent-deploys list is empty because nobody deployed, and the investigation runs down every path except the one where a value changed in a system that keeps no history anyone thinks to check. Config changes are frequently invisible in exactly the places responders look first. A code change leaves a commit, an author, a diff, and a timestamp; a config change often leaves a new value and nothing else — no record of what it was before, who changed it, or why, which is the difference between a five-minute diagnosis and a two-hour one.
The fix isn’t to make config as slow as code. It’s to notice which properties of the code path were actually doing the work and to reclaim those cheaply. History and attribution are the highest-value ones: every change recorded, with the old value, the new value, whoever made it, and when — enough to answer “what changed” during an incident. Validation is next, because a large fraction of config-caused outages are values that were never valid at all: a malformed URL, a timeout in the wrong unit, a number outside the range the code can handle. That check belongs at write time, where the person making the change is still present to fix it, rather than at read time, when the service is already failing.
The underlying reframing is simple: a config change is a production change, and the question isn’t whether it deserves ceremony but which parts of the ceremony were load-bearing. Not every knob needs a review and a staged rollout. But every knob that can take production down deserves at least the ability to answer, afterward, what it was set to before — and ideally to refuse a value that was never going to work in the first place. Those two properties cost very little, and they’re what separates a config system from a set of unlogged, unvalidated global variables that anyone can edit at any time.