Stop Writing Claude.md Rules. Write Linting Rules Instead

speckx1 pts0 comments

Stop Writing CLAUDE.md Rules. Write Linting Rules Instead. — 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

Same PRs, same convention, two ways to enforce it. In the prose lane the rule lives in CLAUDE.md — the agent honors it most of the time. In the lint lane it's a CI check. Ship a few dozen and watch where the ~12% that slip actually land.<br>Ship 10 PRsShip 1<br>CLAUDE.mda suggestion<br>~12% chance to honor it — probabilistically<br>0reached prod<br>No PRs shipped yet.

Nothing slipped — yet. Keep shipping.<br>shipped cleanviolation in prod

Lint rulea law<br>0% chance to violate — it cannot merge<br>0reached prod<br>No PRs shipped yet.

0 reached prod. Nothing to block yet.<br>shipped cleanblocked at CI

“Most of the time” is not a safety property — it’s a countdown. The law never counts down.

That's the thing about a CLAUDE.md. It's firmly in pirate-code territory, more what you'd call guidelines than 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.

This one gets me. We know this. We've known it for years.

Unit tests exist because humans forget edge cases. Linters exist because humans can't agree on where to put a curly brace and shouldn't have to. CI exists because "works on my machine" stopped being funny after the third production outage. The entire history of software tooling is us admitting, over and over, that good intentions don't survive contact with a real codebase. So we built machines that don't rely on them.

And then LLMs showed up and we collectively forgot all of it. "Just write a really good CLAUDE.md." "Just add more skills." "Just prompt it better." We took the one actor in the building that is definitionally probabilistic and decided the way to control it was… a nicely worded document.

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. Move every rule you can from prose into an executable check. The agent doesn't have to remember something the CI will simply refuse.

The single highest-leverage rule: complexity caps

If you do one thing after reading this, do this one.

I don't mean style rules or import ordering. I mean hard caps on how big and how tangled a piece of code is allowed to get. It turns out ESLint has shipped these for years and almost nobody turns them on:

complexity — caps cyclomatic complexity

max-depth — limits how deeply you can nest

max-lines-per-function — forces decomposition

max-params — keeps interfaces narrow

max-statements — stops a function from doing twelve things at once

SonarJS cognitive-complexity — smarter about nested conditionals

I learned why these matter the fun way. Without them, an agent will cheerfully hand you a single 150-line function with six levels of nesting, three early returns, and a switch buried inside a try-catch inside a loop. It compiles. It passes tests. It even works. Then the next agent touches it, makes it worse, and now you have two of them.

Turn them on:

"rules": {<br>"complexity": ["error", 10],<br>"max-depth": ["error", 3],<br>"max-lines-per-function": ["error", 40],<br>"max-params": ["error", 4],<br>"max-statements":...

agent claude rules shipped write rule

Related Articles