A CLAUDE.md that works
A full example CLAUDE.md for a small web app repo, with notes on what to leave out and how to keep the file accurate as the code changes.
Claude Code reads a file called CLAUDE.md at the root of your repository at the start of every session. Whatever is in it shapes every command Claude runs and every file it edits. A good one saves you from repeating "we use pnpm, not npm" in every session. A bad one is either empty, so Claude guesses, or so long and out of date that it confidently follows rules you dropped six months ago.
This piece gives you a complete example for a fictional app, then explains why each section is there, what to keep out, and how to stop the file going stale.
The example repo
Tallyroom is a fictional invoicing app for small agencies. It is a TypeScript monorepo with a React frontend, a Node API, a Postgres database and a background worker that sends reminder emails. Four engineers work on it. The file below is what they keep at the repo root.
The template
# CLAUDE.md
Tallyroom: invoicing for small agencies. TypeScript monorepo, pnpm workspaces.
Production runs on one API service, one worker and one Postgres database.
## Commands (run from repo root unless noted)
pnpm install # install all workspaces
pnpm dev # web on :5173, api on :4000, worker in watch mode
pnpm test # all unit tests (Vitest)
pnpm test -- path/to/file.test.ts # one file
pnpm test:integration # needs `docker compose up db` first
pnpm lint # ESLint, must pass before commit
pnpm typecheck # tsc --noEmit across all packages
pnpm db:migrate # apply pending migrations to the local database
pnpm db:new-migration <name> # create an empty migration file
Before saying a task is done: pnpm lint && pnpm typecheck && pnpm test.
## Architecture map
apps/web/ React + Vite frontend. Talks only to apps/api over /api/*.
apps/api/ Fastify HTTP API. Routes in src/routes/, one file per resource.
Business logic in src/services/. Routes stay thin.
apps/worker/ Reminder and dunning emails. Reads the jobs table, never the API.
packages/db/ Prisma schema and migrations. The only place SQL schema changes.
packages/money/ All currency math. Amounts are integer minor units (cents).
packages/ui/ Shared React components. No data fetching in here.
Request flow: web -> api route -> service -> packages/db.
The worker and the api share packages/db and packages/money, nothing else.
## Conventions
- Money is always an integer in minor units with a currency code. Never use
floats for money. Use the helpers in packages/money for every calculation.
- Dates are stored in UTC. Convert to the agency's time zone only in apps/web.
- Errors from services are thrown as AppError (packages/errors) with a code.
Routes map codes to HTTP status in one place: apps/api/src/errors.ts.
- New API routes need a Zod schema for the request body and a test that
sends an invalid body and expects a 400.
- Tests live next to the file they test: invoice.ts -> invoice.test.ts.
- Use the existing logger (packages/log). No console.log in committed code.
## Database
- Every schema change is a new migration. Never edit a migration that has
been merged to main.
- Migrations must work on a table with existing rows: add columns as
nullable or with a default, backfill in a separate step.
- Ask before writing any migration that drops or renames a column.
## Don'ts
- Don't add a new dependency without saying why in your summary. Check
whether packages/ already has a helper first.
- Don't touch .env files or anything in infra/. Ask instead.
- Don't run pnpm db:reset. It wipes the local database and its seed data.
- Don't change files under packages/db/migrations/ that are already on main.
- Don't commit or push. Leave changes in the working tree for review.
## Where to look
- Product rules for invoices and reminders: docs/billing-rules.md
- How to add a new email template: apps/worker/README.md
Why each section is there
One-line summary. Two sentences on what the product is and how it runs. This gives Claude enough context to make sensible guesses about intent without reading the whole codebase first.
Commands. This is the section that pays for itself fastest. Without it, Claude infers commands from package.json and sometimes picks the wrong one: a deprecated script, the wrong package manager, or a test command that needs a database it hasn't started. Include the command for running a single test file. Claude runs targeted tests far more often than the full suite, and the single-file syntax is the one people get wrong. The "before saying a task is done" line turns your definition of done into a habit.
Architecture map. A directory list with one line per folder about what lives there and what it may depend on. Keep it to the boundaries that matter: which package owns money math, which folder is the only place schema changes happen, what talks to what. Claude can read code to learn the details. What it can't learn from the code is which boundaries are deliberate rules and which are accidents.
Conventions. Only the ones that are specific to your repo and would be easy to break. "Money is integer cents" earns its line because a float slipping in causes a real bug. "Write clean, readable code" does not, because it changes nothing.
Database. Migrations get their own section because mistakes there are the hardest to undo. The "ask before" line is a soft guardrail. Back it with permission rules in settings (see the members checklist on permissions) for anything that must not happen.
Don'ts. Short, concrete, each one tied to a mistake that happened or would hurt. Say what to do instead where there is an alternative ("Ask instead", "check packages/ first").
Where to look. Pointers to longer documents rather than copies of them. Claude reads the linked file when the task calls for it, and the long text stays out of every session.
What to leave out
- Anything your tools already enforce. If ESLint and Prettier handle formatting, a paragraph about semicolons only adds noise. Tell Claude to run the linter instead.
- Generic advice. "Follow best practices", "write tests", "be careful". These cost space and change nothing.
- History. Why you moved from Express to Fastify in 2024 belongs in a decision record or the git log. Claude needs the current state.
- Secrets and environment values. No API keys, connection strings or internal hostnames. The file is committed and anyone with repo access can read it.
- Personal preferences. "I like terse commit messages" belongs in your user-level file (
~/.claude/CLAUDE.md), which applies to all your projects and isn't shared with the team. - Long copies of other docs. Link to them. Duplicated text drifts, and then the two copies disagree.
A useful length test: if a new engineer couldn't read the file in five minutes, it is too long for Claude to weigh well either. The Tallyroom file above is around 70 lines. Larger repos can add a short CLAUDE.md inside a subfolder (for example apps/worker/CLAUDE.md) for rules that apply only there. Claude Code picks those up when it works in that part of the tree, and the root file stays short.
How to keep it current
Start from the generated version. Running /init inside Claude Code produces a first draft from your repo. Treat it as a draft: cut the generic lines, check every command by running it, and add the conventions and don'ts that only your team knows.
Change it in the same pull request as the code. If a PR renames a script, adds a package or changes how migrations work, the CLAUDE.md edit goes in that PR. Add a line to your PR template: "Does CLAUDE.md need an update?"
Add a line when Claude repeats a mistake. The first time Claude uses the wrong test command, correct it in the session. The second time, add it to the file. This keeps the file grounded in real failures instead of imagined ones.
Prune once a quarter. Read the file top to bottom and run every command in it. Delete rules for code that no longer exists. Check that each "don't" still matches a real risk. Twenty minutes, four times a year.
Review it like code. It is committed, so changes go through review. A teammate reading the diff will catch a rule that contradicts how the team works.
Adapting the template
Replace the stack details, then go section by section:
- List the commands you ran this week. Those are your Commands section.
- Draw the folder map from memory, then check it against the tree. Where your memory and the tree disagree, write down the rule you thought was true.
- For Conventions, ask each engineer for the one mistake they most often fix in review.
- For Don'ts, list anything that has caused an incident, a lost afternoon or a wiped database.
- Run a real task with the new file and watch where Claude still guesses wrong. Fix those lines first.
This piece is free. 35 more like it, and each new one, come with AI Skills Academy membership.