Prose Isn't Policy: Stop Writing Claude.md Rules

zernie2 pts1 comments

Prose Isn't Policy: Stop Writing CLAUDE.md Rules — Ernie

Let's talk

← Back to articlesEN/RU

The instinct everyone has first

An agent does something dumb in your codebase. It imports from the design system you migrated off of two quarters ago. It drops a console.log into a production path. It writes a 150-line function with a switch inside a try-catch inside a loop.

So you do the obvious thing. You open CLAUDE.md and you write it down. "Always use shadcn/ui. Never use console.log, use the logger. Keep functions small." You write it clearly. You write it in bold. Maybe you add a skill.

I did this for weeks. Everyone does. Even Vercel shipped a skill library: 40+ React performance rules, beautifully written, structured as SKILL.md files for agents to read. Genuinely good work.

And then the agent does the dumb thing again anyway.

Why prose loses at scale

A CLAUDE.md rule isn't really a rule. It's a suggestion you feed into a probabilistic system, hoping it draws the right card. Most of the time it does. You get beautiful, idiomatic code on the first try and you feel like a genius.

But "most of the time" is doing a lot of work in that sentence. When an agent is shipping more code in an afternoon than you'd review in a week, "most of the time" isn't a safety property. It's a countdown. Roll the dice enough and eventually the model drops an unindexed query into a hot path, and you find out when the database melts at 2 AM on a Friday.

Suggestion vs law<br>0 PRs shipped

Ship 10 PRsShip 1<br>CLAUDE.mda suggestion<br>0reached prod<br>Nothing slipped yet.

Lint rulea law<br>0reached prod<br>Nothing to block yet.

That's the thing about a CLAUDE.md. It's a set of guidelines, not actual rules. It explains the why, which genuinely helps the agent get it right on the first try. What it can't do is make getting it wrong impossible. And once you're producing code faster than any human can read it, the gap between "usually right" and "can't be wrong" is the whole ballgame.

We already solved this. Decades ago.

Unit tests, linters, CI: the whole history of software tooling is us admitting that good intentions don't survive contact with a real codebase, so we built machines that don't rely on them. Then LLMs showed up and we forgot it, and reached for a nicely worded document to govern the one actor in the building that is definitionally probabilistic.

CLAUDE.md explains the why and helps the agent get it right on the first try. A lint rule makes sure it can't get it wrong. Skills speed you up. Linters keep you honest. If you can only have one, take the linter.

The reframe that changed how I work: stop trying to teach the agent your conventions. Start making the wrong thing fail to merge. The agent doesn't have to remember something the CI will simply refuse.

So I counted

I kept telling people most of their conventions could be enforced, and at some point that started to sound like exactly the kind of claim I'd have wanted evidence for in someone else's post. So I went and got some. I pulled every CLAUDE.md and AGENTS.md I could find across 20 public repos, broke them into 252 individual rules, and sorted each one by how you'd actually enforce it.

How 252 real rules break down<br>252 rules · 20 repos

37%<br>27%<br>20%<br>16%

84% enforceable without prose · 16% must stay prose<br>37% (92) Hook<br>never push to main · never edit generated files · run tests first

27% (69) Custom lint rule<br>no raw fetch · this layer can't import that one

20% (51) Existing lint rule<br>no console.log · no any (just turn it on)

16% (40) Genuinely semantic<br>single responsibility · self-documenting

Preliminary: model-sorted, not yet hand-audited.

Two of those landed differently than I expected. I'd assumed most of what feels bespoke ships as a plugin, and for architectural boundaries that's true, but counted across everything people actually write, only a fifth was off-the-shelf. The easy win is real. It just isn't the bulk of the work.

The other one is the thing this article got wrong the first time. A third of those rules aren't lint at all.

A third of the rules aren't lint. They're hooks.

"Never push straight to main." "Never edit generated files." "Run the tests before you commit." "No Co-Authored-By: Claude in the commit message." Rules like that were a third of everything I found, and a linter can't enforce one of them. ESLint reads your source. It never sees the git command you're about to run, the file you're about to clobber, or the shell you're about to fire off. No lint config reaches any of that; it's the wrong kind of gate.

The right kind is a hook: a pre-commit hook, or a PreToolUse hook if your agent runs through Claude Code. Same law-instead-of-suggestion move at a different chokepoint. Instead of failing the merge, it refuses the action. "Block any git push aimed at main." "Block a write under generated/." You can't route around a door that was never yours to open.

This is also where a suggestion wearing a safety vest does the most damage,...

claude rules agent never lint first

Related Articles