A line earns its place in a CLAUDE.md by being checkable. Sounding wise doesn’t count. “Write clean code” fails that test: nothing afterward proves the assistant did it or skipped it. “Never edit supabase/migrations/* without a test that logs in as a second account and tries to read the first account’s row” passes: either that test exists and ran green, or it doesn’t and the change shouldn’t have shipped.

That’s the whole standard, and it fits in one line:

Never touch src/lib/payments/webhook.ts without pnpm test webhook passing.

One sentence, one file, one command that either runs green or doesn’t. Compare that to the line most starter templates open with, “keep the code clean and well-documented,” and the gap between those two sentences is what the rest of this post is about.

Most of what currently ranks for CLAUDE.md best practices covers the same ground, and covers it well: keep the file short, list the build and test commands, describe the folder structure so the assistant stops guessing where things live, then hand you the /init-generated starting point to trim by hand. None of that is wrong. A CLAUDE.md that only tells the assistant how to run the project is worth having. It’s also table stakes, and table stakes is where every guide currently ranking for this keyword stops. What’s missing is the part that matters once the app has a login page: the sections that name what the assistant isn’t allowed to break, and the difference between a rule that reads like a guardrail and one that actually is.

The guardrail sections nobody writes

A security-minded CLAUDE.md earns its keep on three sections most starter templates skip entirely: a fragile-areas list naming the files where a wrong change costs money or data, a matching test command for each of those files, and a secrets rule stating what never gets echoed into a chat, log, or commit.

  • Fragile areas: the two or three files or directories where a plausible-looking change costs money or leaks data (auth middleware, RLS policies, the payment webhook), named specifically enough that “fragile” means a file path, not a feeling.
  • Never-touch-without-a-test: for each fragile area, the exact test command that has to pass before that file changes, so the rule can be checked instead of trusted.
  • Secrets rules: what never gets echoed back into a chat response, a log line, or a commit, stated as specifically as the fragile-areas list.

All three matter for the same reason: a wrong-but-confident answer in one of these spots does damage that nothing in the demo will show you. Why AI coding tools ship security holes by default covers the same ground from the model’s side: auth, payment verification, and tenant isolation are exactly the classes that pass a demo and fail a second account.

Line up a vague version of the same three rules against a checkable one and the gap is immediate:

Sounds like a guardrail
Is one
Auth code is sensitive, be careful with it
src/lib/auth/*: don’t change without pnpm test auth passing first
Never leak secrets
Never echo an API key, signing secret, or service-role token into a chat response, log line, or commit
Payments need extra care
src/lib/payments/webhook.ts: don’t restructure without pnpm test webhook passing against a replayed, unsigned payload
Sounds like a guardrail
Auth code is sensitive, be careful with it
Never leak secrets
Payments need extra care
Is one
Auth code is sensitive, be careful with it
src/lib/auth/*: don’t change without pnpm test auth passing first
Never leak secrets
Never echo an API key, signing secret, or service-role token into a chat response, log line, or commit
Payments need extra care
src/lib/payments/webhook.ts: don’t restructure without pnpm test webhook passing against a replayed, unsigned payload

A rules file only works if something enforces it

A CLAUDE.md rule enforces nothing by itself. Anthropic’s documentation says Claude treats the file as context, not enforced configuration, and a written rule only becomes a guardrail once something outside the conversation, a test that fails the build, checks whether it held.

I’ve seen what an unenforced rules file looks like when it’s dressed up as the safety layer of an entire product. In the AxonBuild audit corpus, Repo 04 was an AI orchestrator for a health-data platform, marketed on the promise that a human approves every clinical action before it happens. Its written guardrail was a 462-line policy file spelling out exactly what the AI could and couldn’t do without sign-off. I checked whether the running server actually loaded that file anywhere. It didn’t, and that’s the finding that’s stuck with me: the approval logic that existed lived elsewhere in the code, hand-rolled, and had already drifted from what the policy document claimed. The same audit found a second, separate way the same product’s approval step could be walked around: the orchestrator handed the model its own step-up token and stamped every action as human-confirmed, so a steered model could sign off on itself. Two different failures, one root cause: nothing was checking whether the written rule held.

That’s an extreme version of a mistake a CLAUDE.md makes on a smaller scale. Anthropic’s own documentation is direct about the mechanism: “Claude treats them as context, not enforced configuration. To block an action regardless of what Claude decides, use a PreToolUse hook instead.” A CLAUDE.md is a briefing, read at the start of every session and taken seriously. A briefing is not a lock, and the locks live elsewhere: what Claude Code can actually touch is decided by the permission tiers and hooks, not by anything you wrote in this file. Reach works the same way: what connecting an MCP server to Claude Code actually grants is settled in a config file, and a line in this one asking the assistant to be careful with a server changes none of it. The fragile-areas list above only becomes a guardrail once something outside the conversation, a test that fails the build, checks whether the rule held. Write the rule and stop there, and you’ve built Repo 04’s policy document at a smaller scale: something a founder could point to, resting on nothing that runs.

A CLAUDE.md the assistant reads and a rule the build enforces are two different things, and only one of them survives an agent that’s confident and wrong.

Diagram showing CLAUDE.md governs every session, every agent, and every edit, while AGENTS.md carries the tool-specific rules.

A real, security-first CLAUDE.md example, annotated

A security-first CLAUDE.md example needs three sections, rarely more than 30 lines between them: fragile areas, secrets, and what to do before touching a fragile area. The version below is annotated with HTML comments; Claude strips block-level <!-- --> comments before the file enters its context, so a note for the next human costs zero tokens.

# Fragile areas

<!-- Every line below names a file and a command. If a line doesn't have
     both, it's a preference, not a guardrail: move it out of this section. -->

- `src/lib/auth/*`, `middleware/session.ts`: don't change without `pnpm test auth` passing first.
- `supabase/migrations/*.sql` (RLS policies): any edit needs a passing test that logs in as a second account and tries to read the first account's row.
- `src/lib/payments/webhook.ts`: signature verification runs on the raw request body. Don't restructure this file without `pnpm test webhook` passing against a replayed, unsigned payload.

# Secrets

<!-- This section exists because assistants debug by printing things. -->

- Never echo an API key, a signing secret, or a service-role token into a chat response, a log line, or a commit message, even while debugging. Mask it first.

# Before you touch a fragile area

- If the matching test doesn't exist yet, write it before you change the file it protects, not after.
- Don't disable a type check, lint rule, or test to make a build pass. Ask first.

Notice what’s missing: a folder map, a list of npm scripts, style preferences, the baseline content the community guides already cover and your own /init pass already generates for free. Also missing is any statement of what to build, because that belongs one step upstream in a spec written before the agent starts; this file only names what not to break once it has. Every remaining line is a file path plus a command, because that’s the only shape of instruction a build can actually check.

The pruning test, and keeping the file honest

A CLAUDE.md earns its length back the same way it earned its first line: something checkable justifies keeping it. Once a quarter, or whenever a new fragile-areas entry gets added, read the file from the top and ask, for every line, whether it’s ever caught anything. A rule nobody remembers writing, backed by no test, and never once fired, is the file turning back into the style guide it was trying not to be. Delete it, or write the test that would let it earn its place back. How long that prune takes depends entirely on how many fragile areas got named honestly in the first place, and if the count is zero, the file was never actually guarding anything to begin with.

The file stays useful only as long as it stays true, which makes it read work as much as write work. The maintainability case for a rules file covers the other half of this, the churn and the god files a rules file also helps contain. This post is the narrower slice: the sections that exist because something real, a login, a payment, another customer’s row, is now on the other side of the change. Whether that’s actually the state your app is in yet deserves a straight answer before you write the file at all, and whether an AI-built app is ready to launch is where that answer starts.

More on working with Claude Code

A rules file is one part of a working setup. The rest have their own pages: AGENTS.md, the cross-tool rules file, writing the spec before the agent starts building, whether Claude Code is safe to run on a real codebase, which MCP servers Claude Code can connect to, and what they reach, the technical debt AI-generated code leaves behind once a few months of it accumulate, what Claude Code costs per month and what the free plan leaves out, and the Claude Code alternatives worth comparing before you switch.

Common questions about CLAUDE.md files

How long should a CLAUDE.md be?

Anthropic’s own guidance is a hard number: target under 200 lines, because longer files consume more context and reduce how consistently Claude follows them. In practice, the guardrail sections above rarely need more than 20 to 30 lines even in a real app, since a fragile-areas list is only as long as the number of places a mistake actually costs money or data, usually three to six. If a file is approaching 200 lines, move anything that only matters for one part of the codebase into a path-scoped rule under .claude/rules/ instead of trimming prose in the top-level file.

Can Claude Code read Cursor rules?

Claude Code doesn’t read .cursorrules on its own, and never re-reads it session to session. What it reads at the start of every conversation is CLAUDE.md, full stop. The one exception is a one-time import: running /init in a repo that already has a .cursorrules, .devin/rules/, or .windsurfrules file reads it and folds the relevant parts into the CLAUDE.md it generates. After that single pass, a .cursorrules file sitting next to your CLAUDE.md does nothing on its own; Claude Code isn’t re-reading it every session the way it re-reads CLAUDE.md. If you run more than one tool and want one file all of them read, AGENTS.md is the cross-tool rules file standard, and Claude Code picks it up through a symlink or a one-line import rather than natively.

Does a CLAUDE.md file replace tests?

No, and the two aren’t interchangeable. A CLAUDE.md tells the assistant what not to break. A test is what turns “don’t break this” from a request into a fact the build can verify. Write the fragile-areas list without the matching test commands, and the result is a document an assistant reads and a founder can point to, not a control anything actually enforces.