Recipe · Basic · 15 min
How to write a CLAUDE.md that Claude Code actually follows
What to put in CLAUDE.md and what to leave out, where each file goes, @path imports, /init and /memory, plus a tested template for a web project.
Tested with Claude Code 2.1.316 · reviewed
The problem
You write a huge CLAUDE.md with everything you know about the project and Claude Code skips half of it. Or the opposite: you have none, and every session you explain again how to run the tests. Both have the same fix: a short file holding only what Claude can't work out from the code, in the right place, with the rules that allow no exceptions moved out of it.
The solution
1. Generate a draft with /init and start deleting
Open Claude Code inside your project and type:
/init
Claude analyzes the code and creates a CLAUDE.md with the build commands, test instructions and conventions it discovers. If you already have one, it suggests improvements instead of overwriting it. From there the work is mostly removing lines: the official docs recommend staying under 200 lines per file, because longer files use more context and are followed less reliably.
2. Keep only what Claude can't see in the code
Include:
- How to check the work: the exact test, typecheck and lint commands, and how to run a single test file.
- Where the code is misleading: an old pattern that's everywhere but must not be copied, or a folder that looks dead but isn't.
- Decisions that aren't in the repo: which error helper to use, how deploys work, what needs review.
- Anything you've already corrected twice.
Leave out:
- The folder tree, the dependency list or an architecture summary: Claude reads those from the code (
/doctorproposes cutting them). - Vague rules like "write good code" or "format properly". Write checkable rules: "2-space indentation", "run
npm testbefore committing". - Long multi-step procedures (they go in a skill, step 5).
- Prohibitions that allow no exceptions (they go in hooks or permissions, step 6).
- Your personal preferences (they go in
CLAUDE.local.md, step 3).
3. Put each thing at its level
Claude Code concatenates (doesn't override) all of these files, from broadest to most specific:
| Level | Path | For | Shared with |
|---|---|---|---|
| Organization | Linux/WSL: /etc/claude-code/CLAUDE.md · macOS: /Library/Application Support/ClaudeCode/CLAUDE.md | Company standards | The whole organization |
| User | ~/.claude/CLAUDE.md | Your preferences across all projects | Only you |
| Project | ./CLAUDE.md or ./.claude/CLAUDE.md | Team commands and conventions | The team, via git |
| Local | ./CLAUDE.local.md | Your sandbox URLs, sample data | Only you (add it to .gitignore) |
| Subfolder | src/payments/CLAUDE.md | Rules for that part of the code | The team |
Files in the directory where you launch Claude and the ones above it load at startup; the closest one is read last. Subfolder CLAUDE.md files load on demand, when Claude reads or edits a file in that folder. To keep CLAUDE.local.md out of the repo:
echo "CLAUDE.local.md" >> .gitignore
If a rule only matters for certain files, a better option than a subfolder is a rule with paths in .claude/rules/, which only enters the context when Claude touches matching files:
---
paths:
- "src/api/**/*.ts"
---
- Validate request bodies with the schemas in src/api/schemas/
4. Split into files with @path imports
Write @path/to/file anywhere in CLAUDE.md and that file loads along with it. Relative paths resolve from the importing file (not from the folder where you launch Claude), and nested imports go up to four hops deep. Importing organizes, but it doesn't save context: the imported file loads in full next to the CLAUDE.md. To mention a path without importing it, wrap it in backticks. The first time a project CLAUDE.md imports something from outside the project (for example @~/.claude/my-notes.md), Claude Code asks you to approve it.
5. Move procedures to skills
A multi-step workflow (cutting a release, preparing a migration) weighs on every session if it lives in CLAUDE.md. In a skill (.claude/skills/<name>/SKILL.md) Claude only sees the name and description until the task needs it. If the workflow has side effects (deploying, publishing), add disable-model-invocation: true to its frontmatter so it only runs when you type /<name>.
6. Move mandatory rules to hooks and permissions
CLAUDE.md is context, not enforced configuration: Claude follows it almost every time, but it may decide an instruction doesn't apply. If something must always or never happen, have Claude Code enforce it. For example, "never read .env" as a permission rule in .claude/settings.json:
{
"permissions": {
"deny": ["Read(./.env)", "Read(./.env.local)", "Read(./secrets/**)"]
}
}
deny rules hold in every permission mode and also block cat .env. For "run the formatter after every edit" or "don't touch migrations/" use a PostToolUse or PreToolUse hook (see the official hooks guide). If you catch yourself adding "IMPORTANT" to a line Claude keeps ignoring, that line is a hook.
7. Copy this template for a web project
Save it as CLAUDE.md at the root and swap the commands for yours:
# Project: web-shop (Vite + React + TypeScript)
## Commands
- Install: `npm ci`
- Dev server: `npm run dev` (http://localhost:5173)
- One test: `npx vitest run src/path/file.test.ts`
- All tests: `npm test`
- Types: `npx tsc --noEmit`
- Lint: `npm run lint`
## When something counts as done
- `npx tsc --noEmit` and the tests for the file you touched pass.
- UI changes: check them in the browser, not only with tests.
## Conventions (what the code doesn't show)
- Style: @docs/style.md
- API calls only from `src/api/client.ts`; never a bare `fetch` in components.
- `src/legacy/` is still in production, but don't copy its patterns into new code.
## Known gotchas
- Integration tests need `VITE_API_URL`; copy `.env.example` to `.env.local`.
- `npm run build` fails if there are circular imports in `src/store/`.
## Git
- Branches `feat/…` or `fix/…`; commit messages in the imperative.
And the imported file, docs/style.md:
# Code style
- 2-space indentation, single quotes, no semicolons.
- Components in PascalCase, one component per file, named exports (no `export default`).
- User-facing text in plain, neutral English.
Check it
Inside a session, /context shows the Memory files list with the CLAUDE.md files that loaded, and /memory lists them all (including ones that don't exist yet) so you can open and edit them. From the terminal you can ask directly:
claude -p "What style rules does this project have? Answer in one line"
Real output with the template above and Claude Code 2.1.316 (our ~/.claude/CLAUDE.md asks for replies in Spanish, so it answered in Spanish; translated here, which also shows the user and project levels being combined):
According to `docs/style.md`: 2-space indentation, single quotes, no semicolons, components in PascalCase with one component per file and named exports (no `export default`), and user-facing text in plain, neutral English.
The style rules live only in docs/style.md, and --output-format stream-json --verbose shows Claude used no tool to read it: it arrived through the @docs/style.md import.
Why it works
Claude Code reads the CLAUDE.md files at the start of every session and delivers them as a message after the system prompt, so they compete for attention with everything else: the shorter and more concrete, the better they're followed. Imports are expanded before reaching the model, paths rules and subfolder CLAUDE.md files only enter when needed, and hooks and permissions are run by Claude Code, not the model, so they don't depend on it "remembering". Sources: Claude Code memory and best practices.
Common mistakes
- Claude ignores rules near the end of the file → the CLAUDE.md is over 200 lines → remove what can be derived from the code or move it to
.claude/rules/withpaths. - Claude follows a rule some times and the opposite other times → two instructions contradict each other (for example, the user one and the project one) → review every level;
/doctor prompt-auditlooks for contradictions. - The import doesn't load → the path has spaces or is wrapped in quotes or backticks → escape spaces with
\and don't quote it. - A subfolder
CLAUDE.mddoesn't show up in/context→ that's expected: it loads when Claude reads a file in that folder. - A rule disappears after
/compact→ you only gave it in the conversation → move it to the root CLAUDE.md, which is re-read after compaction. - Your personal notes end up in the repo → you put them in
CLAUDE.md→ move them toCLAUDE.local.mdand add it to.gitignore.
FAQ
Where does the CLAUDE.md file go?
At the project root (./CLAUDE.md or ./.claude/CLAUDE.md) for the team, and in ~/.claude/CLAUDE.md for your preferences across all projects. Both load together.
How many lines should a CLAUDE.md have?
The official docs recommend under 200 per file. Splitting it with imports doesn't reduce the cost, because imported files load anyway.
What's the difference between CLAUDE.md and CLAUDE.local.md?
CLAUDE.md goes into git and is shared by the team; CLAUDE.local.md is yours only, is read after the CLAUDE.md in the same folder, and belongs in .gitignore.
Does my AGENTS.md work?
Yes: Claude Code reads AGENTS.md when there's no CLAUDE.md or CLAUDE.local.md in your folder or above it. If you have both, put @AGENTS.md inside your CLAUDE.md.
Does writing "IMPORTANT" or "ALWAYS" help?
Not much. If a line keeps being ignored, either the file is too long or the rule allows no exceptions and should be a hook or a permission rule.