
ENGINEERING
SEPTEMBER 1, 2026
CLAUDE.md as production infrastructure
6 min read
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](/website-audit)** 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](/contact).
Read More
MORE ARTICLES

Engineering
Engineering
Two ECS failures that produce no useful error
A readiness probe blocked by the task's own IAM role, and readonlyRootFilesystem silently killing ECS Exec. Both look like broken infrastructure.

Engineering
Engineering
How do you know when your AI feature is wrong?
Most teams ship an AI feature and have no answer past spot-checking. What an evaluation harness actually contains, and the failure it usually misses.

Engineering
Engineering
Don't put a model where you need a reproducible answer
Three systems where I deliberately chose regex and a lookup table over an LLM, and the two questions that decide which one a problem needs.

Engineering
Engineering
A fact register: stopping an LLM inventing your statistics
Four invented statistics shipped to my own live website. The fix was not a better prompt — it was an allowlist of every number the copy may contain.