CLAUDE.md Best Practices (With Examples)

2026-07-11 · 6 min

If you've used Claude Code for more than an afternoon, you've hit the moment where it runs the wrong test command, reinvents a helper you already have, or "fixes" a file in a style your linter immediately rejects. The fix isn't a better prompt every time — it's a CLAUDE.md. This post covers what that file is, what earns a place in it, what to leave out, and how to keep it from rotting. There's a compact example you can copy at the end, plus per-stack notes.

What CLAUDE.md actually is

CLAUDE.md is a plain Markdown file Claude Code reads automatically at the start of a session. It's project memory: standing instructions that ride along with every request in that repo, so you don't have to re-explain your build setup or house rules each time.

The important consequence is that it occupies context for the entire session. It's not a wiki you fill out once and forget — every line sits in the model's working memory alongside your actual request, from the first turn to the last. That one fact drives most of the advice below: a short, accurate file beats a long, comprehensive one, because every line you add is a line the model has to weigh against your actual question.

A few placement rules worth knowing:

  • CLAUDE.md at the repo root is the shared, checked-in file. Commit it so your whole team — and Claude — works from the same rules.
  • ~/.claude/CLAUDE.md holds personal, cross-project preferences (your preferred commit style, "explain before large refactors"). It never touches the repo.
  • Nested CLAUDE.md files in subdirectories get pulled in when Claude works inside that subtree — handy in a monorepo where each package has its own commands.

You don't have to write it from a blank page. Running /init in a repo has Claude scan the codebase and draft a first CLAUDE.md for you. Treat that draft as a starting point, not the final word — it tends to over-describe structure and under-describe the gotchas only you know.

What to put in it

Think of CLAUDE.md as onboarding notes for a competent engineer who is fast but has never seen your repo. Four categories carry almost all the value.

1. Commands. The highest-leverage section, and the one to write first. If Claude knows the exact commands, it stops guessing. Include install, dev, build, typecheck, lint, and — critically — how to run a single test, because that's what it'll reach for most while iterating.

## Commands
- Install: `pnpm install`
- Dev server: `pnpm dev` (→ http://localhost:3000)
- Typecheck: `pnpm typecheck`
- Lint (autofix): `pnpm lint --fix`
- All tests: `pnpm test`
- Single test: `pnpm test -- path/to/file.test.ts`

Before calling a change done: `pnpm typecheck && pnpm lint && pnpm test`

2. Conventions. The decisions that are already made, so Claude follows them instead of picking its own. Keep these concrete and enforceable.

## Conventions
- Validate all external input with Zod at the boundary (route handlers, form actions).
- Data access lives in `lib/db/`. Components never import the DB client directly.
- Use the `cn()` helper for conditional classes — no string concatenation.

3. Gotchas. The non-obvious traps that cost a human an afternoon the first time. This is where a CLAUDE.md earns its keep, because these are exactly the things a fresh model can't infer from the code.

## Gotchas
- Every by-id query must be scoped to the caller's account_id. Unscoped
  lookups leak data across tenants — this is our #1 bug class.
- `NEXT_PUBLIC_*` env vars are inlined into the browser bundle. Never
  put a secret behind that prefix.

4. Do / Don't. A short list of the mistakes you keep correcting. If you've typed the same feedback three times, it belongs here.

## Don't
- Don't add a dependency for something the framework already does.
- Don't disable a lint rule or `@ts-ignore` to make a build pass —
  fix the type or leave a `// TODO(name): reason`.

What to leave out

Just as important as what goes in. Every one of these dilutes the signal:

  • Anything the tools already enforce. Formatting, import order, quote style — Prettier and ESLint own these. Restating them wastes context and, worse, invites Claude to hand-format instead of running the formatter.
  • Full architecture essays. A one-paragraph orientation and a pointer beat a five-page design doc. Claude can read the code; it can't read your mind about the conventions, so spend the budget there.
  • Generic best practices. "Write clean code," "handle errors," "add tests." The model already knows these. They read as filler and train you to skim your own file.
  • Secrets and credentials. Obvious, but worth stating: this file is committed. No tokens, no internal hostnames, no connection strings.
  • Volatile trivia. Current sprint status, a teammate's name, a ticket number. It's stale in a week and adds nothing.

The test for any line: would this change what Claude does? If not, cut it.

Keep it short and high-signal

A stale CLAUDE.md is worse than none — it makes Claude confidently wrong, running a command you renamed months ago. Two habits keep it honest:

  • Update it in the same PR that breaks it. Renamed the test script? Fix the file in that diff. Treat it like code, because it is.
  • Prune on sight. If you notice a rule that no longer applies while reading it, delete it then. Aim for something you can skim in under a minute — most healthy files land between 30 and 80 lines.

Two Claude Code niceties help here. Prefixing a chat message with # lets you append a note to memory on the fly ("# we use vitest, not jest"), so you capture rules the moment you notice them. And you can factor shared content into another file and pull it in with an @path/to/file import, keeping the root file lean.

A compact example

Here's a complete, realistic CLAUDE.md for a TypeScript API service. Notice how little it is — it's high-signal, not comprehensive.

# CLAUDE.md — Orders API

Node 20 + TypeScript + Fastify + Postgres (Drizzle). Package manager: pnpm.

## Commands
- Install: `pnpm install`
- Dev: `pnpm dev` (→ http://localhost:4000)
- Typecheck: `pnpm typecheck`
- Lint (autofix): `pnpm lint --fix`
- All tests: `pnpm test` · Single: `pnpm test -- src/x.test.ts`
- Migrations: `pnpm db:migrate` (never edit generated SQL by hand)

Definition of done: `pnpm typecheck && pnpm lint && pnpm test` all pass.

## Layout
- `src/routes/` — HTTP handlers, thin. No business logic here.
- `src/services/` — business logic. Handlers call these.
- `src/db/` — Drizzle schema + queries. Only place that touches Postgres.

## Conventions
- Validate request bodies with Zod schemas in `src/schemas/` at the route.
- Money is stored and computed in integer cents — never floats.
- Return errors via `throw new AppError(code, message)`, not ad-hoc JSON.

## Gotchas
- Every query is scoped by `account_id` from the auth middleware. An
  unscoped by-id lookup is a cross-tenant leak — the top bug we ship.
- `db:migrate` must run before tests locally, or the schema is stale.

## Don't
- Don't put logic in route handlers.
- Don't add an ORM feature we can do in plain SQL — keep queries readable.
- Don't `@ts-ignore` to pass a build; fix the type.

Per-stack tips

The structure is the same everywhere; the gotchas differ.

  • Next.js / TypeScript. Be explicit about Server vs Client Component rules and, given how much it changed between versions, your fetch caching expectations. Note that next build catches Server/Client boundary and Suspense errors that dev and tsc don't. If you want a fleshed-out starting point, there's a free Next.js/TS CLAUDE.md template in the claude-code-starter repo.
  • Python. State the environment manager and the exact activation/run commands (uv, Poetry, plain venv) — this is the most common source of "wrong command" errors. Pin your type-checker (mypy vs pyright) and whether it's part of the done-check.
  • Go. Point at go test ./..., your linter (golangci-lint), and any codegen step (go generate) that must run before a build. Note error-wrapping conventions if you have them.
  • Monorepos. Put shared rules in the root file and package-specific commands in a nested CLAUDE.md per package. Say how to run one package's tests without building the world — that's the command Claude will need constantly.

The takeaway

A good CLAUDE.md isn't documentation for humans that Claude happens to read — it's a small, load-bearing config file. Commands so it stops guessing, conventions so it matches your codebase, gotchas so it avoids the traps you already know, and nothing else competing for attention. Write the first version in ten minutes, then improve it one line at a time: every piece of feedback you find yourself repeating is a line the file was missing.

Related guides

Set up Claude Code the right way

Start free with the open-source starter — a ship skill, an adversarial-reviewer subagent, a bug-hunt workflow, and a CLAUDE.md template. Want the full kit? The Pro Pack adds 6 skills, 3 subagents, 3 workflows, 4 templates and a handbook.

Free starter on GitHub Get the Pro Pack — $39