← All Claude Code tips
01

Your CLAUDE.md Is a Contract, Not Documentation

Most CLAUDE.md files fail because they describe a project instead of constraining behaviour. Here is the one I actually use, rule by rule, and why each line earns its place.

Foundations7 min read· Updated 8 Aug 2026

Nearly every CLAUDE.md I see is a README wearing a different hat. Project description, folder structure, a list of npm scripts. All accurate. All useless.

It is useless because Claude can already see your folder structure. It can read your package.json. Telling it things it can derive in one tool call spends context to buy nothing.

A CLAUDE.md earns its place when it contains things that are not derivable from the codebase: your preferences, your prohibitions, and the decisions you are tired of repeating.

Put another way — it is a contract, not documentation.

The test for whether a line belongs

Before writing a line, ask: could Claude work this out by reading the repo?

If yes, delete it. "This project uses Tailwind" is visible in package.json and every component file. "This project uses Nuxt 3 with file-based routing" is visible from the pages/ directory existing.

If no, keep it. "Use pnpm, never npm" is not derivable — a pnpm-lock.yaml hints at it, but the hint loses to habit under pressure. "Do not run the dev server unless I ask" is not derivable at all. It is a preference that exists only in your head until you write it down.

That single question removes about 80% of what people put in these files.

What mine actually says

Here are the rules I run globally, across every project. Not a template — the real thing, with the reasoning I would normally leave implicit.

Response rules

- Be concise. Minimize tokens.
- Do NOT explain unless explicitly asked.
- Do NOT add background, theory, or justification.
- DO NOT run npm run dev unless explicitly asked
- DO NOT run npm build scripts unless explicitly asked

The first three are about signal. I am a developer; I do not need an assistant explaining what a Vue composable is before it writes one. Every paragraph of unrequested background is a paragraph I have to skim past to find the code.

The last two are about control, and they were written after being burned. A dev server started inside an agent session is a process I did not start and might not notice — it holds a port, it rebuilds on every file write, and if the agent is editing files rapidly you get a cascade of rebuilds fighting each other. Build scripts are worse: they are slow, they write to dist/, and they turn a two-second edit into a ninety-second wait.

I decide when to run those. Not the tool.

Code requests

- If the user asks for code, output ONLY the code.
- No explanations, comments, or extra files unless requested.
- Assume the user knows what they are doing.

"Assume the user knows what they are doing" is the highest-leverage line in the file.

Without it, you get defensive output — try/catch around things that cannot throw, comments restating the line above them, a paragraph on why you might prefer a different approach. That is a model optimising for a beginner reader. Telling it who it is talking to changes the output more than any amount of prompt engineering.

"No extra files" matters more than it sounds. Ask for a utility function and you can get the function, a test file, a barrel export, and a README. Four files where you wanted one, three of which you now have to read and delete.

Modifications and fixes

- When asked to add, change, or fix something, touch ONLY what is required.
- Do not refactor, reformat, or improve unrelated code unless explicitly asked

This is the rule I would keep if I could only keep one.

The failure it prevents is the diff that does the job and renames three variables, reorders imports, converts a function to an arrow, and reformats a file that a different tool formats. All individually defensible. Collectively, a code review where the actual change is buried in ninety lines of noise, and a git blame that now points at the wrong commit.

Scope discipline is not a style preference. It is what makes a change reviewable.

Defaults

- Prefer the simplest correct solution.
- If an answer can be one line, make it one line.
- if something is not very clear, ask the user questions if your understanding is correct with options if needed
- use pnpm to install or update npm packages

The third one is the interesting one, and it is doing something subtle.

Left alone, an agent facing ambiguity will pick an interpretation and build. If it picked wrong, you find out after the work exists — and sunk cost makes you more likely to accept something you did not want. Asking with options turns a thirty-minute misunderstanding into a ten-second choice.

The specific phrasing matters too. "Ask if you are unsure" gets ignored, because models are rarely uncertain in a way they can detect. "Ask if your understanding is correct, with options" describes an action, not a feeling.

Read restrictions

Do not read or search files under these directories unless I explicitly ask:
dist, .nuxt, node_modules, .git .env

Three different reasons in one line.

node_modules, dist and .nuxt are noise — generated or vendored code that will match almost any search and swamp the results. A grep for a function name that returns forty hits from a minified bundle has told you nothing.

.env is not noise. It is credentials. Anything read into context can end up quoted back in a response, and responses get pasted into tickets and Slack. Keep secrets out of the window entirely.

Global versus project

Two levels, and they hold different things.

~/.claude/CLAUDE.md holds everything above — how I want to be worked with. It is stack-agnostic and it barely changes.

A project's CLAUDE.md, checked into the repo, holds what is true about that codebase and not visible at a glance. For this site:

## Important Notes
- WordPress headless CMS for content (using GraphQL API)
- Nuxt 3 SSR application with dark/light theme support

That first line is worth its space. Nothing in the Nuxt repo tells you the content comes from WordPress over GraphQL — you would have to read the server routes to find out. That is exactly the kind of fact that saves an agent five tool calls and a wrong assumption.

The rest of that project file lists commands and folder purposes. Being honest: most of it fails my own test. It is on the list to trim.

Which is the real lesson — these files rot. Rules accumulate after bad sessions and stay long after they stop mattering. Read yours occasionally and delete what no longer earns its place.

Rules are not magic

Two honest caveats.

A CLAUDE.md shifts probabilities, it does not enforce. A long session with a lot of context can drift from instructions given at the top of it. If a rule genuinely must hold every single time, that is a job for a hook or a permission setting, not prose.

And rules conflict. "Be concise" versus "ask clarifying questions" pull opposite ways; so do "simplest correct solution" and "assume the user knows what they are doing" — one argues for the obvious approach, the other for the sophisticated one. When you notice the same disappointing behaviour repeatedly, the cause is often two of your own rules fighting, not the model ignoring you.

Where to start

Do not write one from a template. Start empty, and add a line the next time something annoys you.

That is genuinely how mine was built. Every rule is a scar. "Do not run npm run dev" exists because of a runaway dev server. "Touch only what is required" exists because of a diff I had to unpick by hand.

Rules earned that way are short, specific, and actually followed — because they describe a real failure rather than an imagined one.


Next in this series: giving Claude a memory that survives past the end of a session — and why I keep it in an Obsidian vault rather than a config file.

// ready?

Need a developer who ships fastwithout shipping mess?

I build and maintain WordPress, Laravel and Nuxt applications for businesses that care about performance and maintainability.