Skip to main content
Jul 2, 2026 · 4 min

Letting an agent write to production

The moment an agent writes to production instead of just reading it is where things break. Six rules that make it safe — and why default-deny is the whole game.

Khoa

Reading is free. An agent that reads your database, your store, your analytics and summarises them can't hurt you — the worst case is a wrong answer you can ignore. The danger starts the moment it writes: edits a record, updates a field, changes a live thing that other systems depend on. That's the line most people won't cross, and they're right to be careful. But you can cross it safely, and the safety isn't a feature you bolt on afterwards. It's the design.

I built an agent that fills product pages on a live store carrying thousands of interdependent fields, apps, and pricing logic. Here's the pattern that lets it write without breaking anything — none of it specific to Shopify.

What actually goes wrong?

Almost every production accident an agent can cause fits into five shapes:

  • Acting on stale state. It wrote based on what it remembered, not what's true right now.
  • Silent bulk edits. It changed a hundred things when you meant one, and you find out later.
  • Touching what it didn't understand. It edited a field that looked cosmetic but was wired to price, or to another system.
  • Irreversibility. Something's wrong and there's no clean way back to how it was.
  • One bad field poisoning the batch. A single rejected value takes down the whole write, including the innocent parts.

Every rule below exists to close one of those.

The pattern: six rules

  • Read before write. Always fetch the live record immediately before composing a change. Never act on memory or a snapshot from ten minutes ago.
  • Snapshot before write. Save the full current state first — every field, not just the ones you're touching. No snapshot, no write. This is your undo.
  • Diff and approve. Produce a field-by-field old → new and write only what's approved. No silent extras, ever.
  • Allowlist, not denylist. The agent may write to a short, explicit set of fields. Everything else is refused by default — see below, because this is the one that matters most.
  • One at a time. No silent bulk mutation. A batch is a canary of one, verified live, then the rest in small chunks that stop on the first error.
  • Reversible and logged. Every change is recorded and can be rolled back from its snapshot. If you can't undo it, you're not ready to do it.

Why an allowlist, not a denylist?

Because you cannot enumerate everything that could break. A real production record is wired to things you don't know about — apps, downstream jobs, cached derivations, other teams' assumptions. A denylist ("don't touch these dangerous fields") is a bet that you thought of all of them. You didn't.

An allowlist flips the burden. You enumerate the tiny set of fields that are safe to author and refuse everything else by construction. When you meet a new field you don't understand, the default is already correct: hands off. The system is safe not because the agent behaves well, but because the code refuses. That distinction is the whole game — good behaviour fails under pressure; a refusal doesn't.

Most of the engineering in a system like this goes into the refusing, not the writing. That feels backwards until the first time a naive edit would have quietly changed a shared record wired to dozens of live things — and the allowlist simply didn't let it.

Where does the human stay?

On the diff. The agent does the mechanical 90% — fetch, compose, validate, show the change — and a human owns the single decision that carries risk: yes, write that. This isn't a training-wheels phase you graduate from. For anything that touches money, availability, or published state, the human yes is the design, permanently. What you're removing isn't the human; it's the 90% of tedious, error-prone work that made the human avoid the job in the first place.

The nice property of a diff-and-approve loop is that it degrades safely. If the model is having a bad day, the worst it produces is a diff you reject. Nothing reaches production that a person didn't read.

Why write the rules down?

Because the rails are only half of it. The other half is the accumulated knowledge of the specific system: which fields are safe, which look shared but aren't, every trap that once caused a real error. Write that down — as a playbook the agent reads before it acts — and two things happen. A cheaper, smaller model can run the recurring work reliably, because the judgement is encoded instead of improvised. And the system gets more trustworthy over time, not less, as every near-miss becomes a written rule.

This is the same discipline as shipping evidence instead of features: the point isn't how much the agent can do, it's how much it can do that you can verify and undo. Give an agent read-before-write, a snapshot, a diff, an allowlist, one-at-a-time, and a rollback — and "let AI edit production" stops being reckless and starts being just another controlled deploy.