CLAUDE.md File: What It Is and How to Write One
A CLAUDE.md file is the instructions Claude Code reads at the start of every session. Where to put one, what belongs in it, what to leave out, and a full example you can copy.
A CLAUDE.md file is a plain markdown file that Claude Code reads at the start of every session. It holds the things you would otherwise re-explain every time: the build command, the test command, where the code lives, and the rules that apply to this project.
Claude Code starts each session with an empty context window. A CLAUDE.md is how knowledge survives from one session to the next. It is not documentation for humans, though humans can read it. It is context for the agent.
Where a CLAUDE.md file goes
There are four locations, and each has a different scope. They load from broadest to most specific, so a project rule lands in context after a personal one.
| Scope | Path | Who it applies to |
|---|---|---|
| Managed policy | macOS /Library/Application Support/ClaudeCode/CLAUDE.md, Linux and WSL /etc/claude-code/CLAUDE.md, Windows C:\Program Files\ClaudeCode\CLAUDE.md | Everyone on the machine, deployed by IT |
| User | ~/.claude/CLAUDE.md | You, in every project |
| Project | ./CLAUDE.md or ./.claude/CLAUDE.md | Everyone on the team, through version control |
| Local | ./CLAUDE.local.md | You, in this project only |
Most people need two of these. A project CLAUDE.md committed to the repository, so the whole team and every agent gets the same instructions. And a global ~/.claude/CLAUDE.md for preferences that follow you everywhere, like how you want commit messages written.
CLAUDE.local.md is for things that should not be committed: your sandbox URLs, your local database name, your own test data. Add it to .gitignore.
One catch with CLAUDE.local.md: if you work in git worktrees, a gitignored file only exists in the worktree where you made it. To carry personal instructions across all of them, keep the file in your home directory and import it instead:
# Individual Preferences
- @~/.claude/my-project-instructions.md
How Claude Code loads them
Claude Code walks up the directory tree from wherever you started it, collecting every CLAUDE.md and CLAUDE.local.md it passes.
The files are concatenated, not overridden. Nothing gets replaced by a file closer to your working directory. Content is ordered from the filesystem root downward, so the instructions nearest to where you launched Claude are read last. Within a single directory, CLAUDE.local.md is appended after CLAUDE.md.
Files in subdirectories below your working directory behave differently. They are not loaded at launch. They load on demand, when Claude reads a file in that directory. That makes per-package instructions in a monorepo cheap: they only cost context when they are relevant.
To see what actually loaded in a session, run /context and look under Memory files. If a file is not in that list, Claude cannot see it.
What to put in a CLAUDE.md
The test is simple: would you have to explain this to a new teammate on their first day, and would they be unable to work it out from the code?
Good candidates:
- Commands that are not obvious.
npm run deployruns a build, a deploy, and a Slack post. Nobody guesses that. - Conventions that differ from the default. Everyone assumes the common case, so write down where you diverge.
- Project layout, when the structure is not self-evident from the directory names.
- Rules with a reason attached. “Never edit
src/generated/, it is rewritten by the build” is followed more reliably than “do not edit generated files.” - The things you keep correcting. If you have typed the same fix into chat twice, it belongs in the file.
Write instructions specific enough to check. “Use 2-space indentation” beats “format code properly”. “Run npm test before committing” beats “test your changes”.
For the rest of what separates a file agents follow from one they drift away from, see CLAUDE.md best practices.
What to leave out
Length has a cost. Every CLAUDE.md loads into the context window at the start of every session, so anything in it competes with the work. Anthropic’s guidance is to keep each file under 200 lines, and adherence gets worse as files grow.
So leave out:
- Anything Claude can read from the code. Directory trees, dependency lists, and API surfaces are already in the repository.
- Long procedures. A multi-step workflow used occasionally belongs in a skill, which loads only when it is needed.
- Rules that only apply to one corner of the codebase. Those belong in a path-scoped rule.
- Contradictions. If two instructions disagree, Claude may pick either one. Re-read the file when you add to it.
Global and project files hold different things
The two files people actually keep answer two different questions.
Your global ~/.claude/CLAUDE.md is about you. How you want an agent to work with you, in every project you open. It travels with you and nobody else sees it.
# How I work
- Ask before installing a new dependency.
- Show me the plan before doing anything that touches more than three files.
- Commit messages: one line, present tense, no trailing period.
- If I am wrong about something, say so plainly. Do not soften it.
A project CLAUDE.md is about the project. It gets committed, so it is written for whoever opens the repository next, human or agent. It should say nothing about your personal preferences.
What a project CLAUDE.md looks like
For a codebase, the useful lines are the ones the code cannot tell you:
# Storefront
Next.js, Postgres via Prisma, deployed to Vercel.
## Commands
- `npm run dev` needs a local Postgres running first
- `npm run deploy` builds, migrates, and posts to Slack. Never run it from a branch.
## Rules
- Money is always integer cents. Never a float.
- Do not edit `src/generated/`, it is rewritten by `npm run codegen`.
- Migrations are forward-only. To undo one, write a new migration.
Note what is missing: no explanation of what Next.js is, no dependency list, no directory tree. Every line is something the repository does not already say.
It does not have to be a codebase
Nothing about CLAUDE.md is specific to code. It is a file of standing instructions for a directory, and the directory can hold anything.
A folder of website copy, with rules an agent would otherwise get wrong every time:
# Site content
Drafts live in `drafts/`, published pages in `pages/`. Never edit `pages/` directly.
## Voice
- Second person. Short sentences. No exclamation marks.
- Never use "delve", "seamless", "unlock", or "in today's landscape".
- Say what the thing does before saying why it matters.
## Rules
- Every page needs a meta description under 155 characters.
- Link to at least two other pages in `pages/`.
- Do not invent statistics. If a number has no source, cut the sentence.
Or a household folder, where the standing facts are the ones you are tired of repeating:
# Meals and shopping
Recipes are in `recipes/`. The running list is `groceries.md`.
- Two of us. No pork, no shellfish.
- Group the list by aisle: produce, dairy, dry goods, frozen.
- Check `pantry.md` before adding anything. We already own more rice than we think.
- Keep the weekly plan to five dinners. We eat out twice.
The test is the same in all three cases. Would someone new need telling, and can they not work it out from what is already in the folder?
Importing other files
A CLAUDE.md can pull in other files with @path/to/file:
See @README for the project overview.
- git workflow @docs/git-instructions.md
Paths can be relative or absolute, and relative paths resolve against the file doing the importing rather than your working directory. Imports can chain up to four hops deep.
Imports are for organization, not for saving context. Imported files load at launch just like the file that references them, so splitting a 400-line CLAUDE.md into four imports does not reduce what it costs.
If you want to mention a path without importing it, wrap it in backticks. Import parsing skips code spans and fenced blocks.
CLAUDE.md and AGENTS.md
Claude Code reads CLAUDE.md. It does not read AGENTS.md.
If your repository already has an AGENTS.md for other agents, do not maintain two copies. Make CLAUDE.md import it, then add anything Claude-specific underneath:
@AGENTS.md
## Claude Code
Use plan mode for changes under `src/billing/`.
A symlink works too, if you have nothing Claude-specific to add. On Windows that needs Administrator rights or Developer Mode, so the import is the safer default.
For what belongs in each file, see how to configure Claude Code with CLAUDE.md, AGENTS.md, and Skills.
When a rule or a skill fits better
Not everything belongs in CLAUDE.md.
.claude/rules/ holds instructions split into topic files. Give a rule a paths value in its frontmatter and it only loads when Claude touches matching files:
---
paths:
- "src/api/**/*.ts"
---
- Every endpoint validates its input
- Use the standard error envelope
That is how you keep API rules out of context while working on the frontend.
Skills are for repeatable procedures rather than standing facts. They load when invoked or when Claude judges them relevant, so a long release checklist costs nothing on the sessions where you are not releasing.
Rough rule: standing facts go in CLAUDE.md, file-specific rules go in .claude/rules/, and procedures go in skills.
Keeping them consistent across projects
One good CLAUDE.md is easy. Fifteen repositories with fifteen drifting CLAUDE.md files is the actual problem, and it is the one crystl is built around.
Starter files are templates crystl writes into a project when you create a gem, so every new repository begins with the same CLAUDE.md, the same AGENTS.md, and the same .mcp.json instead of a copy-paste from whichever repo you had open last.
The agent file editor puts every one of those files in a single panel, so you can read and edit the CLAUDE.md for any project without opening it in an editor first.
The project optimizer scans a project for what is missing, including an absent CLAUDE.md or one that has gone thin, and fixes each finding in a click.
You can also just ask. Run /init in a project and Claude Code writes a starting CLAUDE.md from what it finds in the codebase. If one already exists, /init suggests improvements rather than overwriting it. Refine from there with the things it could not have known.