CLAUDE.md as production infrastructure
ENGINEERING
SEPTEMBER 1, 2026

CLAUDE.md as production infrastructure

6 min read
BACK TO BLOG

What a coding agent's context file has to contain when the repos it touches are live, and the day I found the file preventing disasters had no backup.

I run coding agents across a fleet of repositories on a Mac Mini that stays on. Trading bots, a CRM with live customer data, a website, a home-services business that invoices real money. Most of those repos have a CLAUDE.md — a context file the agent reads before doing anything.

They started as notes. They are now the most load-bearing documentation I have, and the reason is unglamorous: an agent that lacks context does not stop and ask. It infers. Confidently, and at speed.

What actually goes wrong without one

Not what I expected. The failures were rarely bad code.

It rediscovers settled decisions. Without a record of why the CRM runs on tables named -dev that hold production data, an agent reads the name, concludes somebody made a mistake, and helpfully proposes a migration. Every time.

It optimises something load-bearing. I have a .gitignore that denies everything and then admits about twenty files by name. It looks like a mistake. It is the only thing standing between a public push and 14GB of secrets, venvs and twenty-eight nested repositories. Anyone tidying it — human or otherwise — is one edit from a very bad afternoon.

It cannot tell reversible from irreversible. Editing a file and sending an invoice look identical from inside a tool call. One of my automation modules creates real QuickBooks invoices and sends real email. Nothing in the code says "this is the one that touches money."

So what does the file need to contain?

Four things, in descending order of value.

A map. What lives where, and which of it is live. My fleet file opens by saying it is the map of the directory. For an agent landing cold, the difference between "there are 51 folders" and "these 51 folders, these six are in production" is most of the value.

The irreversible actions, named individually. Not "be careful" — a list. Which module sends email. Which one creates invoices. Which table has no backup. Which script needs --commit before it does anything, and which one doesn't and should. This is the section that has actually prevented damage.

The why, not the what. The diff shows what changed and can never show why. So the rule across my repos is that any non-trivial commit carries a line starting Why: — the reason, the alternative rejected, or the symptom being fixed. Why: mow billing double-charged when a work order was rescheduled is worth keeping forever. "fix bug" is worth nothing. That one line is the difference between history you can query and history you can only read.

Structural rules that read as arbitrary. Do not rename these folders, because cron and tmux reference absolute paths. Commit in the nested repo, not the parent. Every one of those looks like fussiness and every one of them is a scar.

Write it for a competent stranger

The mental model that made mine better: write for a competent contractor starting Monday who will not ask you anything.

That framing does the work. A competent stranger does not need the framework explained. They do need to know which of the four apparently-identical environments is the one customers touch, and they will absolutely not guess that -dev means production.

It also filters ruthlessly. Anything a good engineer could work out by reading the code does not belong in the file. What belongs is what the code cannot tell them: history, blast radius, and the decisions somebody already lost an afternoon to.

The thing I got embarrassingly wrong

I audited these files recently, and found that one of them — the harness for the CRM that runs a real business, the one full of this will send real email warnings — was in no repository at all.

The parent directory was not a repo. Two repos nested inside it, so everything that mattered looked version-controlled. And the fleet-level .gitignore denied everything by default, which is exactly the property I wanted, and which silently swallowed that file.

So the document whose entire purpose was preventing expensive mistakes existed on precisely one machine, with no history and no backup, while the code it described was pushed to a remote every day.

Worse: the fleet repository itself had no remote. The map, the automation scripts, the reboot-recovery — all of it local-only. I had been diligent about the projects and had never once thought about the harness that ran them.

What I changed

Every context file is now in a repository, admitted one file at a time rather than by loosening the deny-everything rule. Widening a publish allowlist by accident is the failure that rule exists to prevent.

The fleet has a private remote and an hourly job that commits and pushes it — silent when nothing changed, and it refuses rather than pushes when anything outside the allowlist appears, because the only way that happens is somebody weakened the .gitignore.

I treat these files as code. They get reviewed, they get the same Why: discipline, and when one turns out to be wrong that is a bug with a fix, not a note to tidy up eventually.

The part worth generalising

A coding agent is a very fast contractor with no memory and no fear. It will not hesitate before the irreversible thing, because from inside the tool call nothing marks it as irreversible.

The context file is where that marking lives. It is not documentation in the usual sense — nobody reads it for pleasure and it does not describe the system for a newcomer's benefit. It is a control surface. The bits that matter are the ones that say stop, and they only work if they are specific enough to match the moment the agent is actually in.

Which means the file deserves the same care as the code: version control, review, and a backup somewhere other than the machine it protects.


I am one engineer, and the whole fleet runs on this. Before taking my word for any of it, the free website check is a public unauthenticated endpoint that drives headless Chromium at an arbitrary URL from a stranger — an SSRF liability unless the boundary is real. It is a fair sample of how I build the parts nobody looks at.

If you are running agents against systems where a mistake costs something real and want another pair of eyes on the guardrails, tell me what you are running.

SHARE
6 min read
Read More

MORE ARTICLES

MSUFCU turned its own website off. That was the right call.
Engineering
Engineering

MSUFCU turned its own website off. That was the right call.

A PHP flaw became unauthenticated remote code execution with no workaround, so banks and governments went dark on purpose. What happened in Lansing, and why.

I gave an AI agent a wallet and a spending limit
Engineering
Engineering

I gave an AI agent a wallet and a spending limit

An autonomous client paid for its own API call in stablecoins. The payment was the easy half — the interesting half is the record of what it was refused.

The demo worked because you built it on your own data
Engineering
Engineering

The demo worked because you built it on your own data

The most expensive failure in applied AI is the one that looks like a success. Here is the test that catches it, and why it has to be able to fail.

How to read the arithmetic on a views guarantee
Engineering
Engineering

How to read the arithmetic on a views guarantee

A real offer promises 10 million views in 90 days or you do not pay. Here is the division that tells you what you are actually buying.