Writing an Effective CLAUDE.md File: A Complete Guide
Why Most CLAUDE.md Files Don't Work
Open ten different repositories that use Claude Code and you'll find ten different CLAUDE.md files — and most of them are doing almost nothing. They read like a project's About page: a paragraph on what the app does, a bullet list of dependencies, maybe a sentence about "clean code" and "best practices." Then the developer wonders why Claude keeps making the same mistakes every session.
Here's the thing nobody tells you upfront: CLAUDE.md is not documentation. It's an instruction file that gets loaded into context at the start of every conversation, and Claude treats it the way you'd treat a note from a strict manager — closely, but not infinitely closely. Vague, generic guidance gets skimmed and forgotten by the time you're ten tool calls deep into a task. Specific, concrete, testable instructions get followed.
The difference between a CLAUDE.md that saves you hours every week and one that's dead weight comes down to a handful of principles: specificity over generality, examples over adjectives, structure over prose, and ruthless editing over accumulation. This guide walks through what actually goes into an effective file, with real examples you can adapt today, plus the mistakes that quietly sabotage most attempts.
What CLAUDE.md Actually Is (and Isn't)
CLAUDE.md is a markdown file that Claude Code automatically reads at the start of a session. It can live in several places, and the location matters:
- Project root (
./CLAUDE.md) — checked into git, shared with your whole team, describes the project itself. - Home directory (
~/.claude/CLAUDE.md) — your personal global preferences, applied across every project you touch. - Subdirectories — you can drop a CLAUDE.md inside a specific package or module in a monorepo, and Claude will pick it up when working in that area.
- Local override (
./CLAUDE.local.md) — for machine-specific or personal notes you don't want in version control.
It is not a README. A README explains your project to humans who are unfamiliar with it — onboarding, installation, architecture overviews. CLAUDE.md explains your project to an agent that is about to take actions in it — commands to run, conventions to follow, traps to avoid, and behaviors to override.
The mental model that works best: imagine handing this file to a very capable contractor who has read the entire codebase in the last ten seconds but has zero institutional memory of your team's quirks, your last three postmortems, or the reason you banned a particular library last year. What do they need to know before they touch anything?
The Core Sections Every Good CLAUDE.md Has
Not every project needs every section, but the strongest CLAUDE.md files tend to converge on a similar skeleton. Here's a structure that scales from a small side project to a large monorepo:
- Project overview — one or two sentences, not a paragraph. What is this, in plain language.
- Commands — the exact shell commands for building, testing, linting, and running the dev server. Not "run the test suite" but the literal command.
- Code style and conventions — naming patterns, import ordering, preferred patterns (and anti-patterns) specific to this codebase.
- Architecture notes — where things live and why, especially anything non-obvious (a module that looks unused but is dynamically imported, a config file that's generated rather than hand-written).
- Git and PR workflow — commit message format, branch naming, whether to squash, what NOT to do (force-push, skip hooks, etc.).
- Testing expectations — what "done" means before code is considered mergeable.
- Known gotchas — the stuff a senior engineer would mention in the first five minutes of onboarding a new hire.
A minimal but genuinely effective example, for a small Node.js API project, looks like this:
# CLAUDE.md
## Project
Express + PostgreSQL API for an internal inventory tool. TypeScript throughout.
## Commands
- Install: `npm install`
- Dev server: `npm run dev` (runs on port 4000)
- Run tests: `npm test`
- Run a single test file: `npm test -- src/routes/orders.test.ts`
- Lint: `npm run lint`
- Type check: `npm run typecheck`
## Code style
- Use named exports only, no default exports.
- All database queries go through `src/db/query.ts` — never call `pg` directly in route handlers.
- Route handlers live in `src/routes/`, one file per resource.
- Prefer `async/await` over `.then()` chains.
## Git workflow
- Commit messages: imperative mood, no period at the end, e.g. "add order refund endpoint".
- Never commit directly to `main`. Always work on a feature branch.
- Run `npm run lint` and `npm test` before every commit.
## Gotchas
- The `orders` table has a legacy `status` column that uses integers (1=pending, 2=shipped, 3=cancelled), not strings. Do not "fix" this without a migration plan — three other services read this column directly.
- `.env.example` is out of date. Ask before assuming an env var doesn't exist.Notice what's absent: no vague "write clean code," no "follow best practices." Every line is either a literal command, a hard rule, or a specific fact about this codebase that would otherwise take a new contributor a week to learn the hard way.
Be Specific, Not Aspirational
This is the single biggest lever for improving a CLAUDE.md file. Compare these two instructions:
## Bad
- Write good tests.
- Handle errors properly.
- Keep functions small.## Good
- Every new API route needs at least one test covering the success path and one covering a 4xx error case, in the matching `*.test.ts` file.
- Wrap external API calls in `src/lib/http.ts`'s `safeFetch()` helper, which already handles retries and timeout — do not write your own `fetch` wrapper.
- If a function exceeds ~40 lines, look for an extractable helper before adding more logic inline. This is a guideline, not a hard limit — readability wins if there's tension.The bad version could apply to literally any project on Earth, which means it carries almost no information. Claude has no way to act differently because of it — "write good tests" doesn't tell it what a good test looks like *in your project*. The good version is falsifiable: you can look at a diff and know whether the instruction was followed.
A useful test for any line you're about to add: could this sentence be pasted, unchanged, into a completely different project's CLAUDE.md? If yes, it's probably too generic to be useful. Delete it or make it concrete.
Structuring for an Agent, Not a Human Reader
Claude Code parses CLAUDE.md as part of its context window, and how you format it affects how reliably instructions get followed during a long session. A few structural habits make a real difference:
- Use headings and bullet lists, not walls of prose. An agent scanning for "what's the test command" finds it faster under a
## Commandsheading with a bullet than buried in paragraph three. - Put the most important rules first. Context gets long during a session; instructions near the top of the file are re-anchored on more reliably than ones buried at the bottom of a 400-line file.
- Use strong, unambiguous language for hard rules. "Never force-push to main" reads very differently to a model than "try to avoid force-pushing when possible." If something is a hard constraint, say NEVER or ALWAYS and mean it.
- Group related instructions under one heading rather than scattering them. If git rules are split across three different sections, some will get missed.
- Keep code examples short and directly runnable. A three-line example of the exact pattern you want beats five sentences describing the pattern in the abstract.
Here's an example of the same instruction written poorly versus well, from a real category of mistake:
## Poorly structured
We try to use TypeScript strictly around here and people should
generally avoid any type assertions unless there's a really good
reason for it, and also we like to use interfaces over types most
of the time although there are exceptions especially for unions,
and testing is important too so please write tests when you add
new features to the codebase since we got burned before by a
change that didn't have any test coverage and broke production.## Well structured
## TypeScript conventions
- No `any`. Use `unknown` and narrow, or ask if genuinely stuck.
- Prefer `interface` for object shapes, `type` for unions/intersections.
- Avoid type assertions (`as X`) except when narrowing from `unknown`.
## Testing
- New features require at least one test.
- Run `npm test` before considering a task complete.Same information, roughly the same length, but the second version is scannable, and each rule stands alone instead of being buried in a sentence with three other rules.
Real-World CLAUDE.md Examples by Project Type
Different kinds of projects need different emphasis. A few patterns worth adapting:
For a frontend React/Next.js app, the emphasis usually goes on component conventions and state management:
## Frontend conventions
- Components: functional components with hooks only, no class components.
- Styling: Tailwind utility classes; avoid inline `style={}` except for computed values.
- State: local component state via `useState`; cross-page state goes through the
`useAppStore` Zustand store in `src/store/`, not prop drilling or new Context providers.
- File naming: components are PascalCase (`OrderCard.tsx`), hooks are camelCase
prefixed with `use` (`useOrderStatus.ts`).
- Never introduce a new state management library without asking first.For a data/ML pipeline project, gotchas and environment details matter more than style:
## Environment
- Python 3.11, managed via `uv`. Run `uv sync` after pulling, not `pip install`.
- GPU jobs run through `scripts/submit_job.sh`, never invoke the training script directly —
it skips required env var setup for checkpoint paths.
## Data gotchas
- `raw_events.parquet` has a known duplicate-row issue for 2024-03 dates; the
dedup step in `pipeline/clean.py` is not optional even though it looks skippable.
- Never commit anything under `data/` — it's gitignored for a reason (multi-GB files).For a monorepo, a top-level CLAUDE.md plus scoped ones per package works well:
## Monorepo layout
- `apps/web` — Next.js frontend. Has its own CLAUDE.md with frontend-specific rules.
- `apps/api` — Express backend. Has its own CLAUDE.md.
- `packages/shared` — types and utilities shared by both. Changes here require
checking usages in both apps before committing.
- Run commands from the repo root using the workspace flag, e.g.
`npm run test --workspace=apps/api`, not `cd` into subfolders.The nested CLAUDE.md files are additive — Claude Code reads the root file plus the closest scoped file for whatever directory it's working in, so you don't need to repeat shared context in every subfolder file.
For an open-source library, the emphasis shifts toward public API stability and release discipline:
## Library conventions
- Anything exported from `src/index.ts` is public API. Changing a function
signature there requires a major version bump — flag this explicitly if
a change would break it.
- Internal helpers live in `src/internal/` and are never exported.
- Every public function needs a JSDoc comment with at least one `@example`.
## Releases
- Version bumps and changelog entries are handled by `changesets`, not manual
edits to `package.json`. Run `npx changeset` after a feature-level change.
- Do not publish to npm directly; releases go through the `release.yml` GitHub Action.Across all four examples, the pattern repeats: pick the two or three things that would actually cause a real problem if Claude got them wrong, and write those down precisely. Everything else can be left for Claude to figure out by reading the code, which it's generally very good at doing on its own.
Common Mistakes That Quietly Sabotage Your CLAUDE.md
A few patterns show up repeatedly in files that don't earn their keep:
- Letting it become a changelog. Every time something goes wrong, a line gets appended: "also, don't do X," "remember to Y." Six months later the file is 300 lines of accumulated scar tissue with no structure, and Claude has to weigh a rule about a payment webhook against a rule about CSS class naming with equal priority. Periodically prune and reorganize — merge related rules, delete ones that no longer apply, promote important ones to the top.
- Documenting things Claude can just discover. You don't need to explain your entire folder structure line by line — Claude can read a directory listing faster than it can read your prose description of one. Reserve CLAUDE.md for things that are *not* obvious from reading the code: historical context, hard constraints, non-negotiable conventions, and traps.
- Writing instructions with no verification path. "Make sure code is well-tested" gives no way to check compliance. "Run
npm testand confirm it exits 0 before finishing" is checkable. - Contradicting your own linter or CI config. If your CLAUDE.md says "use single quotes" but your Prettier config enforces double quotes, you've created a coin flip. Keep the file consistent with whatever tooling already enforces standards, or better, just point to the tooling ("formatting is enforced by Prettier, don't hand-fix formatting issues, run
npm run format"). - Treating it as a wishlist instead of a rulebook. "It would be nice if error messages were more descriptive" is aspirational and gets deprioritized under pressure. If it matters, phrase it as a rule with a concrete example of compliant vs non-compliant code.
- Forgetting the global file exists. Anything true across *all* your projects — commit message conventions you personally always want, a blanket rule like never adding AI attribution trailers to commits — belongs in
~/.claude/CLAUDE.md, not copy-pasted into every project's file. - Burying the one rule that matters most in the middle of a long list. If there's a single instruction that, if violated, would cause real damage — "never run migrations against the production database directly," for instance — don't let it sit as bullet fourteen under a generic "Notes" heading. Give it its own short section near the top, or bold it, so it can't be skimmed past.
- Writing instructions as questions or suggestions. "Maybe consider using the shared logger?" reads as optional. "Use the shared logger in
src/lib/logger.tsfor all server-side logging; do not useconsole.login committed code" reads as a rule. Small wording choices like this measurably change how consistently an instruction gets followed.
Splitting Large Files with Imports
As a codebase grows, a single CLAUDE.md can start to sprawl — commands, conventions, architecture notes, and gotchas for five different subsystems all crammed into one file. Rather than letting that one file balloon past the point anyone wants to read it, you can reference other markdown files from within CLAUDE.md so the root file stays a short index and the details live closer to what they describe.
A practical pattern for a mid-size project:
# CLAUDE.md
## Project
Multi-tenant SaaS billing dashboard. React frontend, Django backend.
See @docs/backend-conventions.md for Django/DRF-specific rules.
See @docs/frontend-conventions.md for React/component rules.
See @docs/deploy.md for release and rollback steps.
## Quick commands
- Backend tests: `python manage.py test`
- Frontend tests: `npm test`
- Full local stack: `docker compose up`This keeps the root file readable at a glance while still giving Claude a path to the deeper detail when it's actually working in that part of the codebase. It also mirrors how a real engineering team documents itself — a short top-level index, with specialized detail living in the area it governs — rather than one monolithic wiki page that nobody fully reads end to end.
Iterating on Your CLAUDE.md Over Time
An effective CLAUDE.md is never really "done." Treat it the way you'd treat an onboarding doc for a growing team: it should evolve as you notice repeated corrections.
A practical workflow that works well in practice: whenever you catch yourself correcting Claude on the same thing twice, that's the signal to add a line to CLAUDE.md rather than repeating the correction a third time in a future session. If you find yourself typing "remember, we use pnpm not npm" more than once, that's a two-line addition that pays for itself immediately.
It also helps to periodically ask Claude itself to review the file for staleness — point it at the current codebase and ask whether any instructions in CLAUDE.md no longer match reality (a renamed script, a deprecated pattern, a library that's been swapped out). Stale instructions are worse than no instructions, because they actively mislead rather than simply being silent.
Finally, resist the urge to front-load a huge, comprehensive file before you've actually worked with the agent on the project. The best CLAUDE.md files are usually written incrementally, shaped by real friction points, rather than drafted all at once as a theoretical exercise. Start small — commands, a few hard rules, one or two gotchas — and let it grow only as specific needs surface.
Putting It All Together
A CLAUDE.md file is leverage: a small, well-maintained file changes the behavior of every single session you run against that codebase, for as long as the project exists. The projects that get the most out of Claude Code aren't the ones with the longest CLAUDE.md files — they're the ones with the most specific, most current, most ruthlessly edited ones.
If you're building the muscle of writing agent instructions well — not just for Claude Code but for AI-assisted engineering workflows generally — this is a skill worth deliberately practicing, the same way you'd practice writing a good pull request description or a clear ticket. It compounds: a good CLAUDE.md written once saves correction time on every future session.
If you want a structured, hands-on way to build this skill alongside the rest of the Claude Code workflow — from setup through advanced agent patterns — our Claude Code Tutorial for Beginners course on teachyou.ai walks through exactly this, with real project examples you can adapt directly into your own CLAUDE.md files.
AI CodingShip full-stack AI apps at conversation speed — specs, agents, deploys, all from the terminal.
Claude CodeGo from zero to confident with Claude Code, the terminal agent that reads, edits, runs, and verifies real code.
Related reading