Guide · Coding agents
How to write a good CLAUDE.md, with a complete example
A good CLAUDE.md is a short Markdown file of facts Claude Code can’t work out from your code: the exact build and test commands, conventions that differ from the defaults, where key things live and the gotchas that trip people up. Claude Code loads it at the start of every session, so keep it under 200 lines and cut anything the code already says.
By Tahir NazirUpdated 12 min read
On this page
- What is CLAUDE.md?
- Where Claude Code looks for CLAUDE.md, and in what order
- What to put in CLAUDE.md
- What to leave out
- How long should CLAUDE.md be?
- A complete example CLAUDE.md
- Before and after: fixing a weak CLAUDE.md
- CLAUDE.md and AGENTS.md: one file for every tool
- Keep it working: /init, /memory and regular trims
- Questions people ask
What is CLAUDE.md?
CLAUDE.md is a Markdown file of instructions that Claude Code reads at the start of every session. Each session begins with no memory of your project, so the file is where you write down what you’d otherwise explain again and again: how to build and test, how the team writes code, and what not to touch.
Two things are worth knowing before you write one. First, it’s context, not configuration. Claude Code delivers it as a message after the system prompt, and Claude tries to follow it, but nothing enforces it. Anything that must always happen, such as blocking edits to a folder or running a formatter after every change, belongs in a hook or a permission rule instead. Second, it’s yours to write. Claude Code also keeps an auto memory of notes Claude saves itself, but that’s separate and lives in your home folder.
The quickest start is /init: Claude reads the codebase and writes a first CLAUDE.md with the build commands, test instructions and conventions it finds. If a CLAUDE.md already exists, it suggests improvements instead of overwriting it. Treat the result as a draft and edit it down.
Where Claude Code looks for CLAUDE.md, and in what order
Claude Code loads several files and joins them together; none of them overrides another. At launch it reads them from the broadest scope to the most specific:
1Managed policy
Everyone on the machine- /etc/claude-code/CLAUDE.md
- C:\Program Files\ClaudeCode\CLAUDE.md
Deployed by IT. Can’t be excluded.
2User
You, in every project- ~/.claude/CLAUDE.md
- ~/.claude/rules/
3Project
Your team, through git- CLAUDE.md or .claude/CLAUDE.md
- .claude/rules/
Parent folders first, then the folder you launched in.
Imports such as @AGENTS.md expand where they’re written, up to four hops deep
4Local
You, in this project (gitignore it)- CLAUDE.local.md
Read right after the CLAUDE.md in the same folder.
Later, on demand
packages/api/CLAUDE.md and rules with paths: load when Claude reads or edits a matching file.
Nothing overrides anything: every file is added to the context. If two files disagree, Claude may follow either, so keep them consistent.
- Project file. Put it at
./CLAUDE.mdor./.claude/CLAUDE.mdand commit it. - Personal file.
CLAUDE.local.mdsits next to it for your own notes, such as a sandbox URL. Add it to.gitignore. - Parents and subfolders. Claude Code loads every CLAUDE.md from the folder you launch in up to the root. Ones in subfolders load only when Claude reads or edits a file there, which suits monorepos.
- Imports.
@path/to/fileanywhere in the text pulls that file in at launch, relative to the file that contains it, up to four hops deep. Text inside backticks isn’t imported, so put a path in a code span when you only mean to mention it. - Rules. Markdown files in
.claude/rules/load like the project file. Give one apaths:list in its frontmatter and it loads only when Claude works on matching files.
Run /context to see which files actually loaded, under Memory files. If you edit the root CLAUDE.md mid-session, the change applies only after /clear, /compact or a restart, because the file is read once at session start.
What to put in CLAUDE.md
Write down facts Claude would otherwise get wrong. Anthropic’s own test for each line is “would removing this cause Claude to make mistakes?” Things that pass it:
- Commands Claude can’t guess. The exact install, dev, test, single-test, lint and migration commands, with any order that matters (“start the database first”).
- Rules that differ from the defaults. “Money is integer cents”, “pnpm only”, “no SQL in route handlers”. Skip anything a competent developer would do anyway.
- Architecture pointers. One or two lines on what the project is, and where to find the deeper write-up, not the write-up itself.
- Gotchas. Generated folders never to edit, a test command that wipes a database, an API that needs the raw request body.
- Workflow. What “done” means (“run
pnpm checkandpnpm test”), commit style, when to ask first. - Environment quirks. Required environment variables by name, never by value.
Make every rule concrete enough to check. “Use 2-space indentation” works; “format code properly” doesn’t. “Run npm test before committing” works; “test your changes” doesn’t. A good moment to add a line is when Claude makes the same mistake twice, or when a code review catches something it should have known.
What to leave out
- Anything the code already says. File-by-file tours, dependency lists and folder descriptions are in the repository, and Claude reads it.
/doctorproposes cuts for exactly this kind of content in a checked-in CLAUDE.md. - Generic advice. “Write clean, maintainable code” and “follow best practices” change nothing. Neither does telling Claude it’s an expert.
- Long explanations and tutorials. Link to the docs instead. Detailed workflows that matter only sometimes, such as how to cut a release, belong in a skill, which loads only when needed.
- Things that change often. Version numbers, sprint goals and today’s bug go stale fast and then mislead.
- Secrets. Never put a password, token or connection string with credentials in CLAUDE.md. The project file is committed to git, and every file is sent to the model as part of each request.
If a rule must hold even when Claude disagrees, CLAUDE.md is the wrong tool. Use a hook to run something at a fixed point, or a permissions.deny rule to block an action outright.
How long should CLAUDE.md be?
Under 200 lines per file is Anthropic’s guidance, and shorter is usually better. Longer files take more context and, in Anthropic’s words, reduce adherence: the important rules get lost among the rest. Claude Code warns at startup when a file is over the recommended length, and skips a file over 4 MiB entirely.
Token cost is real but small. The complete example below is 34 lines and about 434 tokens, and at that density a 200-line file would be roughly 2,600 tokens. Claude Code sends the whole context with every request, so the file is part of every turn, but prompt caching bills those repeats at the cached rate. For Claude Sonnet 5.5 that’s $0.10 per million tokens, so re-reading the example file on 100 turns costs about $0.00434, and it fills 0.04% of the model’s context window (prices as of 2026-10-09).
So the reason to stay short is attention more than money. Two things help when a file grows. Move rules that apply to one part of the codebase into .claude/rules/ with a paths: list, and move occasional procedures into skills. Splitting a file with @ imports keeps it tidy but saves nothing, because imported files load at launch too. Paste your file into the token counter to see its size.
A complete example CLAUDE.md
This is the CLAUDE.md for Tally, an invoicing app we made up for this guide but kept realistic: Next.js, Postgres through Drizzle ORM, Stripe webhooks, Vitest and Playwright.
# Tally
Invoicing app for small agencies: Next.js (App Router), TypeScript, Postgres with Drizzle ORM.
How billing, invoices and the webhook worker fit together: @docs/architecture.md
## Commands
- Install: `pnpm install` (pnpm only; npm rewrites the lockfile)
- Dev server: `pnpm dev` (start the database first: `docker compose up -d db`)
- Unit tests: `pnpm test`; one file: `pnpm test src/lib/tax.test.ts`
- End-to-end tests: `pnpm e2e` (starts its own server on port 3100)
- Type check and lint: `pnpm check`
- Database migration: edit `src/db/schema.ts`, then `pnpm db:generate` and `pnpm db:migrate`
## Conventions
- Money is integer cents (`amountCents`). Never use floats for money.
- Store dates in UTC. Format them with `formatInTz()` from `src/lib/dates.ts`.
- Server Components by default; add `"use client"` only for state, effects or browser APIs.
- Validate every request body with the Zod schemas in `src/schemas/`.
- Database access goes through `src/db/queries/`; no SQL in route handlers.
## Gotchas
- Never edit files in `drizzle/` by hand; they are generated from `src/db/schema.ts`.
- The Stripe webhook in `src/app/api/stripe/route.ts` needs the raw request body. Don't call `req.json()` before `constructEvent()`.
- `pnpm e2e` truncates the `tally_test` database. Never point it at another database.
- Invoice numbers must stay sequential per workspace: use `nextInvoiceNumber()`, which locks the row.
## Workflow
- Run `pnpm check` and `pnpm test` before you say a task is done.
- Conventional Commits (`feat:`, `fix:`, `chore:`), one logical change per commit.
- Ask before adding a dependency.Why each part earns its place:
- Two lines of context and one import. Claude learns what the project is and loads
docs/architecture.mdat launch. Because imports cost context too, import only what’s needed in every session; otherwise just name the file so Claude can open it when relevant. - Commands, with their traps. “pnpm only” and “start the database first” are the details Claude can’t infer and would otherwise trip over. The single-file test command saves running the whole suite.
- Conventions that differ from defaults. Integer cents, UTC and a named date helper, and where validation and queries live. Each one is checkable in a review.
- Gotchas. Each line prevents a specific, expensive mistake: hand-edited migrations, a broken webhook signature check, a wiped database, duplicate invoice numbers.
- What “done” means. Claude runs the checks before it reports back, and asks before adding a dependency.
There’s no folder tour, no dependency list and no style guide that a linter already enforces. Claude can see all of that in the repository.
Before and after: fixing a weak CLAUDE.md
Here is the kind of file that often comes before the one above. It looks thorough:
# Project Guidelines
You are an expert senior full-stack developer with 20 years of experience. You always write clean, maintainable, well-documented code and follow industry best practices at all times.
## About this project
Tally is a web application built with Next.js. Next.js is a React framework that supports server-side rendering, static generation and API routes. We chose it because it is popular, well supported and has a large ecosystem. The app lets agencies create invoices, send them to clients and get paid online through Stripe.
## Folder structure
- src/app/ - the Next.js app directory with all the routes
- src/app/(dashboard)/invoices/page.tsx - the invoices list page
- src/app/(dashboard)/clients/page.tsx - the clients list page
- src/app/api/ - API routes
- src/components/ - React components
- src/components/Button.tsx - the button component
- src/lib/ - utility functions
- src/db/ - database code
## Dependencies
- next, react, react-dom
- drizzle-orm, postgres
- stripe, zod, date-fns
- vitest, playwright
## Rules
- Write good, clean code
- Use TypeScript
- Make sure to test your changes properly
- Be careful with the database
- Follow best practices for security
## Environment
DATABASE_URL=postgres://tally:hunter2@prod-db.internal:5432/tally
STRIPE_SECRET_KEY=sk_live_(redacted)| Before | After |
|---|---|
| “You are an expert senior developer…” | Deleted. It changes nothing Claude does. |
| A paragraph explaining what Next.js is | One line naming the stack. |
| Folder structure and dependency lists | Deleted. Claude reads the code and package.json. |
| “Write good, clean code”, “test properly”, “be careful with the database” | Concrete rules: integer cents, pnpm test before done, never point pnpm e2e at another database. |
| No commands at all | Every command, with the order and traps. |
| A database password and a Stripe key | Removed. Secrets live in environment variables, never in an instruction file. |
The surprise is the size. The before file is 38 lines and about 310 tokens; the after file is 34 lines and about 434 tokens. Fixing it didn’t make it shorter. It replaced lines that change nothing with lines that each prevent a mistake. Short is the limit; useful is the goal.
CLAUDE.md and AGENTS.md: one file for every tool
AGENTS.md is an open format for the same job, read by agents such as OpenAI Codex, Cursor and Gemini CLI. Since v2.1.277, Claude Code reads it too, but by default only when there’s no CLAUDE.md or CLAUDE.local.md in your folder or above it:
| Your repository has | Claude Code reads |
|---|---|
| AGENTS.md, and no CLAUDE.md or CLAUDE.local.md | AGENTS.md |
| AGENTS.md and a CLAUDE.md (or CLAUDE.local.md) | The CLAUDE.md files only |
| A CLAUDE.md that imports AGENTS.md | CLAUDE.md, with AGENTS.md included through the import |
Change the default with the Project instructions setting in /config. From Claude Code’s memory documentation, 2026-10-08.
To share one set of rules across tools, Anthropic’s documented pattern is to keep them in AGENTS.md and make CLAUDE.md a short file that imports it, with anything Claude-specific below:
@AGENTS.md
## Claude Code
Use plan mode for changes under `src/billing/`.This works on every version and in sessions that can’t read AGENTS.md directly, and Claude Code never reads the file twice. A symlink from CLAUDE.md to AGENTS.md also works, but avoid it if anyone uses Windows: a committed symlink can check out as a one-line text file there. Note that adding a personal CLAUDE.local.md to a project that relies on AGENTS.md alone stops Claude reading AGENTS.md, unless you import it or change the setting.
Free toolCLAUDE.md and AGENTS.md generatorStart from a Next.js, Python, Go, Rust or monorepo template and get an AGENTS.md plus a CLAUDE.md that imports it, checked for secrets, stray @ imports and length.Keep it working: /init, /memory and regular trims
- `/init` writes a first draft, or suggests improvements to an existing file. With
CLAUDE_CODE_NEW_INIT=1set it runs an interactive flow that can also set up skills and hooks, and shows a proposal before writing any files. It picks up Cursor and Copilot rule files too. - `/memory` lists every memory file Claude Code knows about, opens one in your editor, and turns auto memory on or off.
- Ask in plain words. The old
#shortcut for adding a memory was removed in v2.0.70. Now say “add this to CLAUDE.md”. If you say “remember…”, Claude saves it to auto memory instead. - `/doctor prompt-audit` (v2.1.283 or later) checks your instruction files for outdated, contradictory or broken references and proposes edits without changing anything.
- Comments for humans. Block-level
<!-- … -->HTML comments are stripped before the file reaches Claude, so maintainer notes cost no context.
Treat the file like code: review it when Claude gets something wrong, prune it when rules go stale, and check in your next session that the change worked. MCP servers your project uses are worth a line too, saying when to use them; the MCP setup guide covers connecting them, and What is MCP explains what they are.
FAQ
Questions people ask
Should I commit CLAUDE.md to git?
Yes, commit the project CLAUDE.md (at the repository root or in .claude/) so the whole team and every session get the same instructions, and review changes to it like code. Keep personal preferences in CLAUDE.local.md, add that file to .gitignore, and put preferences for all your projects in ~/.claude/CLAUDE.md.
Where should CLAUDE.md go: the root or the .claude folder?
Either. Claude Code reads a project file from ./CLAUDE.md or ./.claude/CLAUDE.md. The root is more visible to people browsing the repository; .claude/ keeps it next to your rules and settings. In a monorepo you can also add a CLAUDE.md inside each package, which loads when Claude works on files there.
Does Claude Code read AGENTS.md?
Yes, from v2.1.277, but by default only when there’s no CLAUDE.md or CLAUDE.local.md in your working folder or above it. If you have both, Claude reads CLAUDE.md only. The reliable way to use one shared file is a CLAUDE.md whose first line is @AGENTS.md, with Claude-specific notes below it.
Why is Claude ignoring my CLAUDE.md?
Run /context and check the file is listed under Memory files; if not, it’s in the wrong place. Then look for vague rules, contradictions between files, or a file so long that rules get lost. Edits made mid-session apply only after /clear, /compact or a restart. For rules that must never be broken, use hooks or permission rules.
Does a long CLAUDE.md cost more tokens?
Yes. It loads at the start of every session and is part of every request after that, so every line takes up context. Prompt caching makes the repeats cheap in money, but long files are followed less reliably. Anthropic recommends under 200 lines per file; move part-specific rules to .claude/rules/ and occasional procedures to skills.
What is the difference between CLAUDE.md, rules and skills?
CLAUDE.md holds facts for every session. Files in .claude/rules/ do the same job split by topic, and a rule with a paths: list loads only when Claude touches matching files. Skills are packaged procedures, such as a release checklist, that load in full only when invoked or relevant, so the rest of the time each costs at most its one-line description.
Try it
Tools from this guide
Keep reading