CLAUDE.md & the Memory Hierarchy
Teach Claude your project's DNA
- Map the four-tier CLAUDE.md hierarchy and predict the exact order in which files concatenate into context
- Explain why instructions are advisory context (not system prompt) and when to reach for hooks instead
- Use /init to generate and refine a project CLAUDE.md, including the interactive CLAUDE_CODE_NEW_INIT=1 flow
- Write short, concrete, project-specific instructions and apply the 'would Claude err without this?' test
- Predict how CLAUDE.md behaves across subdirectories and after /compact
CLAUDE.md is how you teach Claude Code your project's DNA — build commands, conventions, architecture — once, so you don't re-explain it every session. This lesson covers the four-tier memory hierarchy, how files are discovered and concatenated, the /init command, and the crucial fact that these instructions are advisory context, not enforced configuration.
- 1The mental model: a persistent briefing, not a config file
- 2The four-tier hierarchy
- 3Discovery and load order
- 4Advisory context, not enforced config
- 5Bootstrapping with /init
- 6Keep it short, keep it concrete
- 7What survives compaction
The mental model: a persistent briefing, not a config file
Every Claude Code session starts with a fresh context window — Claude remembers nothing from yesterday. A CLAUDE.md file is how you carry knowledge across that gap. Think of it as the onboarding doc you hand a new teammate: the build command, the test runner, the directory layout, the two or three conventions they'd get wrong on day one.
The single most important thing to internalize: CLAUDE.md is loaded as context, not as enforced configuration. Claude reads it at session start and tries to follow it — but there is no hard guarantee of compliance, exactly as a human teammate might forget a guideline under pressure. This one fact explains almost every design decision in the system, so hold onto it.
A good CLAUDE.md answers a precise question for every line: "Would Claude make a mistake here without this instruction?" If Claude already does the right thing — say, it naturally runs your tests — the rule is noise. Delete it. The goal is a small, dense file of facts Claude can't guess, not a style manual.
There is a second, complementary system worth naming up front: auto memory, where Claude writes its own notes (build commands it discovered, a debugging insight) to MEMORY.md. The split is clean: you write CLAUDE.md; Claude writes auto memory. This lesson focuses on CLAUDE.md, the part you control.
Key insight
The test for every line
Before adding a line to CLAUDE.md, ask: would Claude err without this? "Run pytest -q, not the full suite" passes the test — Claude can't guess your preference. "Write clean code" fails it — it's vague and Claude tries anyway. Curate for facts, not vibes.
The four-tier hierarchy
CLAUDE.md instructions can live in four places, each with a different scope. They are listed here from highest precedence to lowest, but precedence here doesn't mean override — it means load order. All discovered files concatenate into context; nothing replaces anything. The rule to memorize: the closer a file is to your working directory, the later it loads — and the last thing Claude reads carries the most weight.
| Tier | Location | Scope | Who writes it |
|---|---|---|---|
| 1. Managed policy | macOS: /Library/Application Support/ClaudeCode/CLAUDE.md<br/>Linux/WSL: /etc/claude-code/CLAUDE.md<br/>Windows: C:\Program Files\ClaudeCode\CLAUDE.md | Organization-wide, every repo | IT / DevOps |
| 2. User | ~/.claude/CLAUDE.md | You, across all your projects | You |
| 3. Project | ./CLAUDE.md or ./.claude/CLAUDE.md | The team, via source control | Team |
| 4. Local | ./CLAUDE.local.md | You, this project only (gitignore it) | You |
Think of it as concentric rings, from broadest to most specific. The managed policy tier is special: deployed by an organization via MDM/Group Policy/Ansible, it applies to every session on the machine and cannot be excluded by individual settings — that's the whole point of a policy. (In a monorepo you can skip other CLAUDE.md files with the claudeMdExcludes setting, but never the managed-policy file.)
Because everything concatenates with project-level last, your project's ./CLAUDE.md is read after the user file, and your private ./CLAUDE.local.md is appended after that — so your most specific, most local instructions get the final word.
Note
Local vs. worktrees
CLAUDE.local.md is gitignored and exists only in the worktree where you created it — it does not follow you across git worktrees. To share personal instructions across worktrees, import a home-directory file instead: put @~/.claude/my-project-notes.md in your project CLAUDE.md.
Tip
Managed policy without a separate file
Organizations can skip deploying a file entirely and put the content inline via the claudeMd key in managed-settings.json (e.g. {"claudeMd": "Always run make lint before committing."}). It carries the same precedence as a managed-policy CLAUDE.md and is honored only from managed/policy settings.
Discovery and load order
How does Claude find these files? It walks up the directory tree from your current working directory, checking each directory for CLAUDE.md and CLAUDE.local.md. Run Claude in foo/bar/ and it loads foo/bar/CLAUDE.md, foo/CLAUDE.md, and any CLAUDE.local.md alongside them — all the way up to the filesystem root.
The ordering rule is precise and worth committing to memory:
- Across the tree, content is ordered from the filesystem root down to your cwd. So
foo/CLAUDE.mdappears beforefoo/bar/CLAUDE.md— instructions closer to where you launched are read last. - Within a single directory,
CLAUDE.local.mdis appended afterCLAUDE.md— your personal notes are the last thing read at that level.
# Launched in foo/bar/ — load order, top = first, bottom = last (most weight):
Managed policy CLAUDE.md (if deployed)
~/.claude/CLAUDE.md (user)
foo/CLAUDE.md (ancestor project)
foo/CLAUDE.local.md
foo/bar/CLAUDE.md (closest project)
foo/bar/CLAUDE.local.md <- read last, most weightSubdirectories below your cwd behave differently. Claude discovers CLAUDE.md files in subdirectories too, but it does not load them at launch. Instead, a subdirectory's CLAUDE.md is pulled in on demand — only when Claude actually reads a file in that directory. This keeps a big monorepo's context lean: you only pay for the instructions in the parts of the tree you're actually touching.
Tip
HTML comments are free
Block-level HTML comments (<!-- maintainer note -->) are stripped before injection — they cost zero context tokens. Use them for notes to human maintainers. Caveat: comments inside code blocks are preserved, and all comments stay visible if you open the file with the Read tool.
Note
Debugging what loaded
Run /memory to list every CLAUDE.md, CLAUDE.local.md, and rules file currently loaded. If a file you expected isn't there, Claude literally can't see it. For deeper tracing of when and why files load, use the InstructionsLoaded hook.
Advisory context, not enforced config
Here is the most consequential detail in the whole system, stated exactly: CLAUDE.md content is delivered as a user message after the system prompt — it is not part of the system prompt itself. Claude reads it and tries to follow it, but there is no guarantee of strict compliance, especially for vague or conflicting instructions.
Three practical consequences follow:
-
Specific beats vague — every time. Concrete, verifiable instructions are followed far more reliably than fuzzy ones.
Write this Not this Use 2-space indentationFormat code properly Run npm test before committingTest your changes API handlers live in src/api/handlers/Keep files organized -
Contradictions get resolved arbitrarily. If two files give conflicting guidance for the same behavior, Claude may pick one at random. Periodically review your files (and nested ones) to prune outdated or clashing rules.
-
For guarantees, don't use CLAUDE.md at all. If something must happen at a specific moment — lint before every commit, block writes to a path — write a hook. Hooks run as shell commands at fixed lifecycle events and apply regardless of what Claude decides. That's the enforcement layer; CLAUDE.md is the advice layer.
If you genuinely need an instruction at the system-prompt level, the escape hatch is the --append-system-prompt flag — but it must be passed on every invocation, so it suits scripts and automation, not interactive use.
Watch out
Don't put hard guarantees in prose
"Never push to main" written in CLAUDE.md is a suggestion Claude can drift from, and it can even be dropped during compaction. A safety rule you cannot afford to lose belongs in a PreToolUse hook or a permissions.deny rule — the client enforces those no matter what Claude decides.
Bootstrapping with /init
You rarely write a CLAUDE.md from a blank page. Run /init and Claude analyzes your codebase, then generates a starter file with the build commands, test instructions, and project conventions it discovers. From there you refine — add the handful of things Claude wouldn't discover on its own (the gotchas, the "never touch legacy/" rules).
Key behaviors:
- If a CLAUDE.md already exists,
/initdoes not overwrite it — it reads what's there and suggests improvements instead. Safe to re-run. - It's codebase-aware beyond just Claude: running
/initin a repo with an existingAGENTS.mdreads it and folds the relevant parts into the generatedCLAUDE.md. It also reads other tool configs like.cursorrulesand.windsurfrules.
For a richer setup, set the environment variable CLAUDE_CODE_NEW_INIT=1 to enable an interactive, multi-phase flow:
CLAUDE_CODE_NEW_INIT=1 claude
# then, inside the session:
/initIn that mode /init asks which artifacts to set up — CLAUDE.md files, skills, and hooks — explores the codebase with a subagent, asks follow-up questions to fill gaps, and presents a reviewable proposal before writing any files.
Tip
Already using AGENTS.md?
Claude Code reads CLAUDE.md, not AGENTS.md. If your repo standardizes on AGENTS.md for other agents, don't duplicate — create a CLAUDE.md whose first line is @AGENTS.md (an import), then add any Claude-specific instructions below it. A ln -s AGENTS.md CLAUDE.md symlink also works if you have nothing Claude-specific to add.
Keep it short, keep it concrete
CLAUDE.md is loaded into the context window at the start of every session, spending tokens alongside your actual conversation. That cost drives the cardinal rule: target under 200 lines per file. Longer files consume more context and reduce adherence — the irony of a bloated instruction file is that the very rules you wrote get lost in the noise and ignored.
A discipline that keeps files tight:
- Apply the test to every line: would Claude err without it? If not, cut it.
- Structure with markdown headers and bullets. Claude scans organized sections the way you do; dense paragraphs are harder to follow.
- Add a rule when you've hit the same problem twice — Claude repeats a mistake, a review catches something it should've known, or you type the same correction you typed last session. That's the signal to write it down.
- Move the wrong-shaped content out. A multi-step procedure belongs in a skill (loads only when invoked). An instruction that only matters for one part of the tree belongs in a path-scoped rule under
.claude/rules/— it loads only when Claude touches matching files.
When a file genuinely needs to grow, split by topic into .claude/rules/. A rule with a paths: frontmatter (e.g. src/api/**/*.ts) loads only when Claude reads matching files, saving context everywhere else. (Note: @path imports help organize but do not save context — imported files still load in full at launch.)
Example
A lean project CLAUDE.md
# Project: billing-service
## Commands
- Build: `make build`
- Test (fast): `pytest -q tests/unit`
- Lint: `make lint` (must pass before commit)
## Conventions
- API handlers live in `src/api/handlers/`
- Money is always cents (int), never float
- Never edit `legacy/` — it's frozen
<!-- maintainers: keep this under 50 lines -->Every line earns its place: each is a fact Claude would otherwise get wrong.
What survives compaction
Long sessions eventually hit /compact, which summarizes the conversation to reclaim context. A natural worry: does my CLAUDE.md survive? For the part that matters most, yes.
- Project-root CLAUDE.md is re-injected from disk after
/compact. Claude re-reads the file and puts it back into the freshly compacted session. Your core project instructions persist by design. - Nested subdirectory CLAUDE.md files are not automatically re-injected. They reload the next time Claude reads a file in that subdirectory — the same on-demand mechanism as initial discovery.
This yields a useful diagnostic. If an instruction vanished after compaction, it was one of two things: (a) it was given only in conversation and never written to a file, or (b) it lives in a nested CLAUDE.md that hasn't been re-triggered yet. The fix for (a) is the recurring theme of this lesson — if you want it to persist, write it to CLAUDE.md, don't just say it in chat.
Key insight
The throughline
Fresh context each session, advisory not enforced, re-injected on compaction — every behavior points to one habit: durable knowledge goes in a file; hard guarantees go in a hook. Conversation is for the work of the moment, not for things you need to last.
Try it: Author and stress-test a project CLAUDE.md
Pick a real repo of yours. 1) Bootstrap: run /init, let Claude generate a starter CLAUDE.md, and read what it inferred about your build/test commands and conventions. 2) Curate: apply the line test — for each line, ask 'would Claude err without this?' and delete the ones that fail. Add 2-3 facts Claude couldn't discover (a gotcha, a 'never touch X' rule, the exact fast-test command). Keep the whole file under ~50 lines. 3) Verify loading: run /memory and confirm your file is listed. Add an HTML comment (<!-- note to maintainers -->) and confirm via behavior that it doesn't reach Claude. 4) Test the hierarchy: create a ./CLAUDE.local.md with a personal preference (e.g. your sandbox URL) and re-run /memory to see both files loaded; gitignore the local file. 5) Prove the advisory boundary: write 'never run destructive commands' into CLAUDE.md, then recognize why a real safety rule belongs in a PreToolUse hook or permissions.deny rule instead. Write three sentences: what /init got right, what you had to add by hand, and one rule you moved from CLAUDE.md to a hook (or path-scoped rule) and why.
Key takeaways
- 1Four tiers concatenate (never override), highest-to-lowest precedence: Managed Policy → User (~/.claude/CLAUDE.md) → Project (./CLAUDE.md or ./.claude/CLAUDE.md) → Local (./CLAUDE.local.md). Closer-to-cwd loads last and carries the most weight.
- 2Managed-policy files (macOS /Library, Linux /etc/claude-code, Windows /Program Files) apply org-wide and cannot be excluded by individual settings.
- 3CLAUDE.md is delivered as a user message after the system prompt — advisory, not enforced. Specific instructions beat vague ones; for guaranteed behavior use hooks.
- 4/init scans the codebase to generate a starter CLAUDE.md (and only suggests improvements if one exists); CLAUDE_CODE_NEW_INIT=1 enables an interactive multi-phase flow.
- 5Keep each file under 200 lines — bloat reduces adherence. Every line should answer 'would Claude err without this?'; HTML comments strip out for free.
- 6Project-root CLAUDE.md re-injects from disk after /compact; nested subdir files reload only on the next read in that directory.
Quiz
Lock in what you learned
Check your understanding
0 / 4 answered
1.You launch Claude in a subdirectory of a project. Which CLAUDE.md is read LAST and therefore carries the most weight?
2.Your team has a hard rule: never push directly to main. Where should this live to be reliable?
3.You run /init in a repository that already contains a CLAUDE.md. What happens?
4.Your CLAUDE.md has grown to 600 lines and Claude has started ignoring rules that used to work. What's the best explanation and fix?
Go deeper
Hand-picked sources to keep learning
The authoritative reference: four tiers, load order, /init, auto memory, path-scoped rules, and compaction behavior.
Full /init, /memory, and /compact behavior plus every other built-in command.
The enforcement layer: when a guarantee belongs in a hook instead of advisory CLAUDE.md.
How CLAUDE.md re-injects after /compact and what else persists across compaction.
Practical guidance on keeping CLAUDE.md short, concrete, and project-specific.
Source repository, issues, and release notes for the CLI.