Pakkit.net
← Back to blog

Field Notes

Field Note: Automation Needs a Dry-Run Mode

A field note on the cheapest safety feature in automation — a preview that shows exactly what would happen before anything does. Build the dry run first, and the blast radius stays understandable.

  • Field Notes
  • Automation
  • Operations
  • Reliability
Illustration of a terminal showing a dry-run diff of would-create, would-update, and would-delete lines inside a preview-only safety rail, with a small blast-radius gauge.

Field notes are the short version: one habit, one reason it earns its place, no grand theory. This one is about the first feature I build into anything that acts on its own — a dry-run mode — and why I write it before the automation it’s meant to preview.

I like building slightly weird things: scripts that fan out across a fleet, jobs that rewrite config, bots that poke at services on a schedule. The fun part is the leverage. The responsible part is making sure that leverage has a preview button. A dry run is how I keep “build weird things” compatible with “the blast radius is something I can actually picture.”

Build the preview before the engine

The order matters. If I write the doer first and bolt on a --dry-run flag later, the flag ends up being a half-truth — it skips some of the side effects and quietly performs others, which is worse than no preview at all. So the preview path comes first, and the real run is implemented as “the dry run, except this time we commit.” Same plan, same code path, one boolean apart.

That constraint also keeps the design honest. If an action can’t describe itself without performing itself, that’s a smell: it usually means intent and execution are tangled together, and untangling them is exactly the work that makes the whole thing safer.

What a good dry run actually shows

A dry run that just prints Would update 14 things is theater. The version I trust shows the specifics, in the operator’s own terms:

  • Every target it would touch, named — not a count, the actual list.
  • The before-and-after for anything it would change.
  • The commands or calls it would make, in order.
  • A loud line for anything destructive or irreversible.

Then it stops. The goal is that a tired human can read the output at a glance and go “yes, that’s what I meant” — or catch the one wrong entry before it becomes a real entry.

Trust is earned in dry runs and spent in real ones.

I don’t point an automation at anything that matters until its dry-run output has been boring and correct several times in a row. Boring is the whole goal. A dry run that surprises me has just done its job.

The dry run is the start, not the whole story

A preview tells you what would happen. It doesn’t help once something already has. That’s the line where this field note ends and the longer argument begins: real automation also needs logs of what it decided, a rollback path for when it’s wrong, scoped permissions so a bug can’t reach further than its job, and a manual override a human can actually find under stress. I wrote all of that up in Automation Needs a Panic Button — the dry run is just the first and cheapest of the set, which is why it gets its own note.

It matters even more when the thing acting isn’t a script but an agent, because an agent can be confidently wrong in ways you didn’t think to guard against. A proposal-first, preview-then-confirm loop is the backbone of how I run the AI automation lab: let it move fast inside a fence, but never let it act before showing its work.

None of this is glamorous, and that’s the point. The dry run isn’t friction slowing the automation down — it’s the thing that makes it reasonable to let the automation run at all. Build the preview first. If you want to compare notes on doing this without summoning a haunted house of cron jobs, I’m easy to reach.