Write a CLAUDE.md the agent actually uses
Put in what code can't say — commands, done criteria, and never-do rules. Leave out anything the agent can read for itself.
Most CLAUDE.md files fail the same way: they re-describe the codebase. A file that lists your directory structure and dependency versions is dead weight, because the agent can read package.json and run find itself. A CLAUDE.md earns its keep by holding the handful of things no amount of code-reading will reveal.
What actually belongs in the file
Anthropic's own guidance for Claude Code frames this as "the place you write down what you'd otherwise re-explain," and lists concrete triggers for adding a line: "Claude makes the same mistake a second time," "a code review catches something Claude should have known about this codebase," "you type the same correction... that you typed last session" (Claude Code memory docs). That's a good filter: if you've said it more than once, it goes in the file.
In practice that means:
- Exact commands. Not "run the tests" but
pytest tests/ -xormake test. The same docs recommend concrete phrasing: "Runnpm testbefore committing" over "Test your changes." - Definition of done. What "finished" means for this repo — lint clean, types clean, no skipped tests.
- Never-do rules.
git push --forceto main, editing generated files, touching/prodconfig. Things a human hire would be told on day one and that a linter can't always catch. - The "why" behind odd code. A workaround for a vendor bug, a deliberately duplicated function — context that isn't in the diff.
What to leave out
Anything a Read or Grep call would surface: directory layout, which framework you use, what a function's signature is. The same source is direct about this: keep CLAUDE.md "to facts Claude should hold in every session," and move anything that's a multi-step procedure or only relevant to part of the codebase into a skill or a path-scoped rule instead. Padding the file with restated code doesn't just waste space — it's loaded into context on every single session, so bloat has a recurring cost, not a one-time one.
A real-looking example
## Commands
- Test: `make test` (fast subset: `pytest -m "not slow"`)
- Lint: `make lint` — must pass before any commit
## Definition of done
- Tests green, lint clean, no new mypy errors
- Root cause fixed, not the failing test deleted or skipped
## Never do
- Never edit files under `/migrations/applied/`
- Never commit `.env` or credentials
## Conventions the code doesn't show
- We use soft deletes everywhere; there is no hard DELETE in this repo
- Retry logic lives in `retry.py`, not inline — copy that pattern
In the figure, the left column is what the agent derives from reading; the right column is what only a written file supplies, and CLAUDE.md exists to feed that column specifically.
Try it today
- Open your last three corrections to an agent's code — comments you left in review, or things you fixed by hand afterward.
- For each one, ask: could the agent have read this from the code? If yes, skip it.
- For the ones it couldn't have known, write one line each into
CLAUDE.mdin imperative form ("Never...", "Always...", "Run..."). - Keep the file under roughly 200 lines; the same docs note that longer files "consume more context and reduce adherence."
- Re-run the task that caused the original mistake and confirm the agent now avoids it.
Where this breaks
- CLAUDE.md is loaded as context, not enforced configuration — the docs are explicit that "there's no guarantee of strict compliance." A rule that must always hold (no force-push, ever) belongs in a hook or permission deny-list, not just prose.
- A file that grows by accretion and is never pruned becomes noise; contradictory instructions get picked "arbitrarily," per the same source.
- It won't fix a vague task description — that's a separate problem, covered next.
Next: hooks — verification as a loop, not a step.