Automation
A Runbook Is Only Valid in the Environment It Was Written For
Inherited procedures carry invisible assumptions about the world they were written in — internet access, a reachable mirror, a particular OS — and those assumptions break silently the moment you run the runbook somewhere different.
- Automation
- Operations
- Documentation
- Runbooks
A runbook reads like a list of steps, but it’s really a list of steps plus a giant pile of unwritten assumptions about the environment those steps were proven in. Network access, a reachable package mirror, a specific OS version, a tool that’s already installed, a path that exists. None of that is in the document, because it was all true where the runbook was born. Hand that same runbook to a different environment and the assumptions break — silently, because nobody wrote them down to check. I’ve been bitten by this enough to treat every inherited procedure as valid only in its original habitat until proven otherwise.
The steps are explicit; the environment is assumed
When someone writes a runbook, they document what they did. They don’t document the conditions that made what they did work, because those conditions were just “how things are” at the time. “Run the installer” silently assumes the installer can be downloaded. “Update the packages” silently assumes a reachable repository. “Restart the service” assumes the service manager you have. The environment is the hidden half of every instruction, and it’s invisible precisely because it was ambient and reliable when the author wrote it.
A runbook documents the steps and assumes the world. Move it to a different world and the assumptions, not the steps, are what bite you.
The assumption I keep meeting: “the internet is reachable”
The sharpest version I hit: an inherited procedure assumed the host could reach a public package mirror to pull updates. Perfectly reasonable where it was written — and completely false on the locked-down, effectively air-gapped boxes it later got pointed at. The steps were “correct.” They just couldn’t execute, because the single most load-bearing assumption — outbound internet to a specific mirror — wasn’t true here and wasn’t written down anywhere. The runbook didn’t look wrong. It looked like a clean list of commands. The wrongness was in the environment it silently required.
Worse, that kind of failure is confusing, because the document gives you no hint that connectivity was ever a prerequisite. You debug the command when the real problem is a precondition nobody stated.
Make the assumptions explicit, then they’re testable
The fix isn’t to write longer runbooks; it’s to surface the preconditions so they can be checked instead of assumed. A procedure that names what it depends on lets you verify the environment before you run, and turns a silent failure into an upfront “this prerequisite isn’t met here.”
When I adapt an inherited runbook now, I hunt for the hidden dependencies:
- Network reach — what does this need to talk to, and is that reachable here? (Public mirrors, internal registries, license servers — the classic offenders.)
- OS and package manager — was this written for one distro and quietly assumes its tooling and paths?
- Pre-existing state — does a step assume something is already installed, configured, or present?
- Implicit credentials or access — does it assume a level of privilege or a reachable auth system that may not exist in the new environment?
Each of those, once named, becomes a precheck. Pull the dependency out of the prose and make it explicit — fetch from a reachable source, assert the precondition before proceeding — and the runbook stops being a trap for the next environment.
Carry your dependencies, don’t assume them
The deeper fix, for automation especially, is to stop assuming a dependency is reachable and instead arrange for it to be. If a procedure needs an artifact, have it pull from a source the target can actually reach rather than wherever the original author happened to get it. If it needs a tool, install it as part of the run rather than assuming it’s there. An “air-gapped” environment isn’t an exotic edge case; it’s just an environment whose assumptions differ from the author’s, and robust automation makes its requirements explicit and self-satisfied instead of ambient. That’s the same instinct behind making automation cross-distro rather than quietly assuming one OS.
Treat inherited procedures as habitat-specific
So the rule I keep: a runbook is a artifact of the environment that produced it, and “it worked there” says nothing about “it works here” until you’ve checked the assumptions it never bothered to state. Read inherited procedures as drafts to validate, not gospel to execute — the same way a job running isn’t a job working and writing the runbook is itself the test of whether you actually understand the steps. If you’ve inherited a procedure whose hidden assumption ruined an afternoon, I’d like to hear which one it was.