TLDR; CLAUDE.md is the instruction file Claude Code loads at the start of every session: project identity, house rules, output format, and hard boundaries a competent collaborator needs, not a full reference manual. Keep it under 200 lines, Anthropic’s own figure, and move subsystem detail into skills or path-scoped rules. Add a rule only when Claude makes a mistake; delete anything Claude could learn by reading the code.
Over two weeks working on a client’s Rails project, I built out its CLAUDE.md alongside the codebase. Coding conventions, deployment procedures, domain model explanations, API patterns, test expectations, common gotchas. It reached 280 lines. Thorough. I was proud of it.
Then I ran an experiment. I trimmed it to 85 lines, only the instructions that prevented mistakes Claude had made, and ran the same set of tasks. Output quality didn’t drop. On one task it was noticeably better. Fewer instructions competing for attention meant the ones that remained got followed more reliably.
This tracks with what the community has found. Lakshmi Narasimhan argues that frontier models reliably follow roughly 150–200 instructions before compliance decays. Claude Code’s system prompt alone consumes about 50 of those slots. Your CLAUDE.md gets maybe 100–150 effective instructions. After that, every line you add isn’t wasting tokens; it’s actively diluting the instructions that matter.
Most CLAUDE.md files I encounter are too long, too vague, and full of information Claude could discover by reading the code. This guide covers how to write one that improves output, and how to recognise when it’s making things worse.
What CLAUDE.md is
CLAUDE.md is an instruction file that loads automatically at the start of Claude Code sessions and resumed conversations. Custom subagents normally inherit applicable project instructions, while built-in Explore and Plan agents use deliberately narrower context. It’s the persistent context you author yourself; its machine-written counterpart, auto-memory, works alongside it and is covered in the memory hierarchy guide.
Anthropic’s own internal teams, including teams that support non-engineering staff, treat their Claude.md files as the codebase tour they hand to new hires. That’s the right mental model. Not a full reference manual. Not a personality configuration. The minimum context a competent collaborator needs to start working without making avoidable mistakes.
Four files make up the system, loaded in order and merged:
| File | Scope | Committed to git? |
|---|---|---|
Managed policy (/etc/claude-code/CLAUDE.md on Linux) |
Organisation-wide, set by IT | N/A (deployed by admins) |
~/.claude/CLAUDE.md |
Global, applies to every project | N/A (personal machine) |
CLAUDE.md (project root) |
Project-specific, shared with team | Yes |
CLAUDE.local.md (project root) |
Personal project overrides, never credentials | No (gitignored) |
Claude reads the user, project and local files in ordinary project sessions. The global file sets your baseline preferences. The project file layers on specifics. The local file handles personal workflow overrides that do not belong in the shared repository. Gitignore only prevents a file from being committed; it does not prevent Claude from reading it, so passwords, tokens, private keys, and other secrets belong in credentials stores or environment variables, not in any CLAUDE.md file.
The memory hierarchy guide covers where CLAUDE.md fits in the broader memory system. This guide is about writing a good one.
A four-section structure
After studying dozens of production CLAUDE.md files, including the ones the community has shared publicly, I’ve settled on a structure that rarely needs more than four sections:
- Project identity: two or three sentences. What this project is, the tech stack, what matters most.
- House rules: non-default conventions Claude needs to follow. Build commands it can’t guess, naming patterns, architectural boundaries, forbidden patterns.
- Output format: how you want code and text structured. Commit message format, test patterns, response style preferences. Where a linter or formatter can enforce these rules, put them there and keep only the remainder in CLAUDE.md.
- Hard boundaries: things Claude should never do in this project. Security rules, protected files, off-limits operations.
Here’s a condensed version of the CLAUDE.md for this site, the one you’re reading right now:
# CLAUDE.md
## Project
Kam's personal site: an isometric "knowledge islands" scene over
static pages. 11ty v3 (Nunjucks), Tailwind CSS v4, ES-module
client scripts with no bundler.
## Commands
npm run dev # 11ty --serve + Tailwind watch
npm run gate # clean build + deploy checks (links, SEO, markup, weight)
npm test # node --test runs test/*.test.js
## House rules
npm run build wipes _site/, including the dev server's CSS:
touch src/css/main.css or restart dev afterwards.
A page file holds front matter and Markdown, never HTML.
Old URLs go in ops/redirects.journal.jsonl, append only.
A page module that adds a listener removes it in destroy().
## Hard boundaries
Never decode the protected email into source, data or HTML.
Never hand-edit _redirects.
## Rules files
Subsystem detail lives in .claude/rules/, one file per area,
each loaded only for the paths it covers.
The real file is 216 lines. It was about 740 until I moved the subsystem detail (the router, the reading window, the chart generator, the PDF scripts) into twelve path-scoped files in .claude/rules/. Those load only when Claude reads or edits a matching file, so a session that never touches the charts never pays for their 56 lines, and the text loaded every session fell from about 12,700 tokens to about 3,000. That still sits a little over Anthropic’s 200-line guide, and every line left would cause a mistake if removed. The _site/ rule exists because a build run during a dev session silently deleted the dev server’s CSS, twice in one afternoon.
Compare that with what I typically see: files with 300+ lines documenting module purposes Claude can see by reading the code, explaining file structures Claude can explore with a single ls, and restating coding conventions already enforced by linters.
The litmus test is simple: would removing this line cause Claude to make a mistake? If not, delete it.
CLAUDE.local.md and the team split
The .local.md file solves a specific tension: your team needs shared project instructions, but you have personal preferences, local paths, and context that doesn’t belong in git.
CLAUDE.md (committed, shared):
- Build and test commands
- Project conventions the whole team follows
- Architectural decisions and boundaries
- Hard constraints
CLAUDE.local.md (gitignored, personal):
- Your preferred response style (“keep responses terse”, “Australian English”)
- Local tool paths or environment specifics
- Where local test credentials live, named but never pasted: the environment variable or credential store to read them from. A CLAUDE file is read into context and quoted back; secrets do not belong in one, gitignored or not
- Personal workflow shortcuts
The team practice Boris Cherny runs is instructive: the shared CLAUDE.md is checked into git and updated multiple times per week, and the trigger isn’t a scheduled review, it’s whenever Claude makes a mistake. “Anytime we see Claude do something incorrectly we add it to the CLAUDE.md.” Claude used camelCase instead of snake_case? Add the rule. Forgot to run the linter before committing? Add the rule. Over weeks, the file becomes a precise record of every mistake Claude has made in this codebase, and a precise set of instructions to prevent recurrence.
Your global ~/.claude/CLAUDE.md sits above both project files. Mine started as the textbook version, preferred languages, absolute rules (never commit secrets, never force push to main), git conventions, and has since grown to about 105 lines, almost all of it lessons added the day something went wrong. One early entry read: “zsh kills unmatched globs before the command runs, bit twice: 2026-06-10, 2026-06-11.” Each entry exists because a mistake happened once and I refuse to pay for it twice. That’s the add-on-mistake model applied globally, and it’s why I don’t apologise for the line count: a global file earns length the same way a project file does, one documented error at a time.
Subdirectory files and scoped rules
A single root CLAUDE.md works for small projects. For larger codebases, monorepos, multi-service architectures, projects with distinct subsystems, the root file either bloats to accommodate everything or stays lean and misses context-specific rules.
Two mechanisms reduce context cost here.
Subdirectory CLAUDE.md files
Any directory can have its own CLAUDE.md. When Claude accesses a file in that directory, the local CLAUDE.md loads automatically. The instructions only consume tokens when they’re relevant.
project/
├── CLAUDE.md ← universal rules
├── api/
│ └── CLAUDE.md ← API conventions, endpoint patterns
├── frontend/
│ └── CLAUDE.md ← component patterns, state management
└── tests/
└── CLAUDE.md ← test conventions, fixture patterns
Community reports of this restructuring on large monorepos describe an order-of-magnitude drop in per-session instruction load, because Claude only loads the instructions relevant to the files it’s touching. The bloated root file doesn’t get shorter; it gets split into pieces that mostly never load.
.claude/rules/ files
For finer control, .claude/rules/ files use YAML frontmatter with path globs:
---
paths:
- test/controllers/**/*.rb
---
- Always use integration tests, never unit tests for controllers
- Test both JSON and HTML response formats
- Include authentication context in every test
These rules activate only when Claude works with files matching the glob pattern. A rule about test conventions doesn’t load when Claude is editing a migration. A rule about API serialisation doesn’t load during frontend work. This is how you keep instructions tightly scoped without stuffing everything into the root file. One caveat from the issue tracker: path-scoped rules have had version-dependent bugs, rules silently ignored, or triggering on read but not write. After adding one, run /context to confirm it actually loaded.
Both mechanisms expire at compaction
Deferred loading has a cost the token maths doesn’t show. Root CLAUDE.md is re-injected after /compact; subdirectory files and path-scoped rules are not. They loaded once, when Claude first read a matching file, and the summariser treats them like any other conversation content: compressible, and usually compressed away.
So a long session drifts. Claude follows the API conventions for the first hour, compaction fires, and the next endpoint comes back written in the house style of the root file. The rule didn’t get overridden; it stopped being present.
Two things follow. Anything that must hold for the whole session, security boundaries, protected paths, the conventions you’d catch in review, belongs in the root file even when it reads as subsystem-specific. And after a compaction in a long session, touch a file in the relevant directory before resuming work there; the read reloads the scoped instructions. The terminal prints a Loaded <path> line when one comes back, and an InstructionsLoaded hook can log every load.
The @import problem
Claude Code supports an @import syntax for splitting a root CLAUDE.md into referenced files. It looks like organisation. It isn’t, at least not the kind that saves tokens.
Imported files load at startup, the same as if their content were inlined in the root file. A 280-line CLAUDE.md split into five @imported files is still 280 lines of context consumed at session start. The only difference is editorial convenience in your editor.
I’ve seen developers split their CLAUDE.md into imports thinking they’ve reduced context cost. They haven’t. If you want instructions that load only when relevant, use subdirectory CLAUDE.md files or .claude/rules/ with path filters. Those defer loading. @imports are fine for editorial organisation, but don’t mistake them for optimisation.
There is a third option, and it’s the cheapest of the lot: drop the @ and write the path in backticks. `docs/deployment.md` sitting in a sentence costs about ten tokens. The harness ignores it entirely. Claude reads the file with the Read tool, on its own initiative, only if the task at hand looks like it needs deployment knowledge. A 400-line deployment procedure goes from a permanent tax on every session to a pointer you pay for once, when it’s actually relevant.
The catch is in the word “initiative”. A backtick path is a hint, not a mechanism, so a bare path gets skipped more often than followed. Give it a trigger. My global file doesn’t just name the reference docs for my custom CLIs, it says “before the first use of any of them in a session, read its skill” and then lists the paths. The sentence is what gets obeyed; the path is only where it sends Claude. That distinction sets the boundary for the technique: use backtick pointers for situational material (procedures, flag references, lookup tables, anything you’d look up rather than memorise) and keep hard constraints inline, where they load every session whether or not Claude thinks to go looking. This is what the “email with links” idea below actually looks like in a file.
Auditing an overgrown file
If your CLAUDE.md has grown past 200 lines, Anthropic’s recommended ceiling, it’s probably carrying weight it doesn’t need. /init can analyse a project and suggest improvements to CLAUDE.md, and /doctor now proposes trims for a checked-in CLAUDE.md too, cutting content Claude can already derive from the codebase while keeping the pitfalls and conventions that differ from tool defaults. I still do the manual pass because it makes the trade-offs explicit.
Step 1: Categorise every line.
Go through the file and tag each instruction as one of three types:
- Essential: Claude needs this every session and can’t discover it from the code. Build commands with non-obvious flags, naming conventions not enforced by linters, architectural boundaries, hard constraints.
- Situational: needed for specific tasks but not all tasks. Deployment procedures, API integration quirks, client-specific domain rules.
- Discoverable: information Claude can find by reading the codebase. File structure, module purposes, existing patterns, config values.
Step 2: Delete discoverable content.
Claude can read config/routes.rb. It can check the test suite to understand patterns. It can look at the component directory to see what exists. Documenting this in CLAUDE.md pays tokens to tell Claude what it can see for itself.
Step 3: Move situational knowledge out.
Deployment procedures become a skill loaded on demand. Client-specific domain rules go to subdirectory CLAUDE.md files or .claude/rules/. API integration patterns become a skill triggered when touching those endpoints. Only the short skill description is present at startup; the body and references are deferred until relevant. The skills guide covers this migration in detail.
Step 4: Tighten what remains.
Essential instructions should be concise. “Use kebab-case for all URL slugs” is enough. “It’s important to use kebab-case for URL slugs because this ensures consistency across the project and improves SEO readability” wastes five times the tokens for zero additional compliance.
Step 5: Test.
Run the same tasks before and after the trim. In my experience, output quality either stays the same or improves slightly; fewer instructions competing for the same attention budget.
Diagnostic quick reference
| Symptom | Likely cause | Fix |
|---|---|---|
| Claude ignores a specific rule | Too many competing instructions | Trim file, move non-essential rules out |
| Claude asks about things in CLAUDE.md | File is too long, rule is buried in the middle | Move the rule closer to the top or into a targeted .claude/rules/ file |
| Claude “forgets” conventions mid-session | Context filling, instructions deprioritised | Condense the file; check the session craft guide |
| A subsystem rule holds early, then lapses | Compaction dropped a subdirectory CLAUDE.md or scoped rule | Re-read a file in that directory, or promote the rule to the root file |
| LLM-generated CLAUDE.md produces worse output | Content parrots the code, wastes instruction slots | Delete and rewrite manually, add only rules that prevent real mistakes |
Maintaining the file
A CLAUDE.md isn’t written once. It has a specific maintenance trigger: Claude made a mistake.
The cycle:
- Claude does something wrong
- You correct it in the session
- You add a rule to CLAUDE.md preventing recurrence
- No other trigger causes an addition
This is Boris Cherny’s approach, and it’s how he sustains high-throughput PR shipping across multiple parallel Claude instances. He doesn’t pre-emptively add rules or brainstorm what Claude might get wrong. He waits for the error, then adds the correction. Over time, the file converges on exactly the instructions this codebase needs.
The counterpart to adding is pruning. Once a quarter, or when the file crosses 200 lines, revisit the audit. Rules that were essential six months ago may no longer apply; a linter now enforces the convention, the deprecated API was removed, the framework version was upgraded, or a newer model handles the behaviour reliably without the extra instruction. Stale instructions actively mislead Claude into following outdated patterns, which is worse than having no instruction at all.
Letting Claude generate your CLAUDE.md is a trap. Running “generate a CLAUDE.md for this project” produces a file that looks thorough but typically restates what’s already in the code. Sabrina Ramonov puts it well: treat your CLAUDE.md like “an email with links, not a 2,000-page employee handbook.” If you want Claude’s help with the file, start blank and add rules only as you encounter mistakes. Let the codebase teach you what Claude needs to know.
In a fractional CTO engagement, I write these conventions down for the team’s repositories and keep each file lean.
Related reading
- The memory hierarchy, CLAUDE.md, memory vault, and persistent context, where CLAUDE.md fits in the broader memory system
- Token economics, what Claude Code actually costs, the cost of CLAUDE.md in context and dollars
- Skills, hooks, agents, and commands, the definitive guide, moving knowledge from CLAUDE.md to on-demand skills
- Session craft, when to start fresh, resume, or compact, managing the context window your CLAUDE.md loads into
- How I use CLAUDE.md across six client projects, these conventions applied day to day across real client engagements
Sources
- CLAUDE.md Best Practices, Lakshmi Narasimhan, The ~150-200 instruction compliance ceiling argument. Blog post
- How Boris Uses Claude Code, The add-on-mistake maintenance pattern; the ~100-line figure is community-reported. Secondary source (fan-made collection)
- Claude Code Docs, CLAUDE.md configuration, Official documentation for file structure and loading order. Documentation
- How Anthropic teams use Claude Code, Internal teams treating CLAUDE.md as the new-hire codebase tour. White paper
- 6 Ways I Cut My Claude Token Usage, Sabrina Ramonov, The 2,000-token CLAUDE.md target and “email with links” framing. Blog post
Common questions
What is a CLAUDE.md file?
It is the instruction file Claude Code loads at the start of every session: project identity, house rules, output format and hard boundaries a competent collaborator needs. It shapes every response, so it belongs to the project, not to a single conversation.
What should go in a CLAUDE.md file?
Only what prevents mistakes or sets direction: project context, key commands, design and architecture rules, and hard boundaries. Keep it short. Add a rule when Claude makes a mistake, and delete anything Claude could learn by reading the code.
How long should a CLAUDE.md be?
Short enough that every line earns its place: under 200 lines, Anthropic’s own figure. I trimmed a 280-line file to 85 with no drop in quality and one task improved, because fewer competing instructions get followed more reliably.
Do subdirectory CLAUDE.md files survive compaction?
No. A subdirectory CLAUDE.md loads when Claude reads a file in that directory, and a path-scoped .claude/rules file loads when it touches a matching path, but neither is re-injected after /compact. Only what loads at launch is re-injected: the root CLAUDE.md, rules without `paths` and auto memory. Keep session-wide constraints in the root file, and re-read a file in the directory to reload the scoped instructions.