Skip to main content
Claude Code tips · Lesson 1 of 6 · 10 min

CLAUDE.md File Example: Global, Project and Local Layers

A real CLAUDE.md file example: the managed, user, project and local layers, how they load, @imports, and what belongs in a skill or a hook instead.

By Matthews WongReviewed on Checked against Claude Code v2.1.296

Key takeaway

CLAUDE.md is a Markdown file of instructions that Claude Code loads into every session. Files from four scopes, managed, user, project and local, are concatenated rather than overridden, broadest first, and files in subfolders load only when Claude works there. Keep it short, keep procedures in skills, and back every hard rule with a hook, permission or test.

You will be able to

  • Put an instruction in the right CLAUDE.md layer and predict the order Claude reads them in.
  • Decide whether a rule belongs in CLAUDE.md, a path-scoped rule, a skill or a hook.
  • Confirm with /context what actually loaded, instead of assuming it did.
Watch it work

Step through the diagram

Press Play, or step with the arrows. Every step is written out below the diagram.

How CLAUDE.md files load into Claude's context

Instruction files from four scopes join the context in order after the system prompt, imports expand at launch, a subfolder's file waits for a file read, and only a check outside CLAUDE.md can enforce a rule.

Reviewed on 13 October 2026 · Claude Code v2.1.296

Step 1 of 7. Claude Code reads CLAUDE.md files from four scopes: your organisation, you on every project, your team in this repo, and you alone in this repo.

Keys: ← → step · Home / End · K play or pause

  1. Claude Code reads CLAUDE.md files from four scopes: your organisation, you on every project, your team in this repo, and you alone in this repo.

  2. At launch the four files are joined in that order, broadest first, after the system prompt. Nothing overrides anything; if two disagree, Claude may follow either.

  3. An @path line loads another file at launch, alongside the CLAUDE.md that names it. Imports tidy a long file, but cost exactly as much context.

    - git workflow @docs/git.mdPredict the next step Claude now reads src/api/app.ts. What happens to src/api/CLAUDE.md? (It joins the context now · Nothing: it loaded at launch · It replaces the root CLAUDE.md)
  4. A CLAUDE.md inside a subfolder waits. It joins the context the first time Claude reads, writes or edits a file in that folder.

  5. The docs target under 200 lines per file. This repo's runs past 500, so its long blog procedures belong in a skill, which loads only when needed.

    .claude/skills/publishing-blog-posts/SKILL.mdPredict the next step Does a rule in CLAUDE.md stop Claude from breaking it? (Yes, if it sits in the project file · No, only a check that fails does)
  6. CLAUDE.md is a request, not a guarantee. This repo pairs its hard rules with checks that fail, like the parity check that stops the build.

    `assertBlogParity()` fails the build otherwise.
  7. Run /context and read the Memory files list. If a file is missing there, Claude cannot see a word of it.

    /context

Where do CLAUDE.md files live, and in what order do they load?

A CLAUDE.md can live in four places. A managed file, deployed by IT, applies to everyone on the machine and cannot be excluded. Your user file at ~/.claude/CLAUDE.md applies to you in every project. The project file, ./CLAUDE.md or ./.claude/CLAUDE.md, is committed so the whole team shares it. CLAUDE.local.md holds your personal notes for one project and belongs in .gitignore.

Read more (2)

Claude Code does not pick one of them. It loads every file in your working directory and the directories above it and concatenates them, broadest scope first, so a project instruction is read after a user instruction. In each directory, CLAUDE.local.md comes after CLAUDE.md. A CLAUDE.md in a subfolder below where you started loads later, the first time Claude reads, writes or edits a file in that folder.

Because nothing overrides anything, a contradiction is a bug. If your user file says npm and the project file says pnpm, the docs warn that Claude may pick one arbitrarily. This repo keeps only this site's facts in its project file; preferences that apply to every project live in the author's own user file, which is never committed.

How do @imports work in a CLAUDE.md?

Write @path/to/file anywhere in a CLAUDE.md and Claude Code loads that file into context at launch, alongside the file that references it. Relative paths resolve from the importing file, not from where you started Claude, and imports can nest up to four hops deep. A path inside backticks or a code block is left alone, so you can mention a file without importing it.

Read more (1)

Imports organise; they do not save anything. An imported file loads at launch exactly like inline text, so splitting a 600-line file into five imports costs the same context. To defer instructions until they matter, use a path-scoped rule in .claude/rules/ or a skill. The first time a project imports a file from outside the working directory, Claude Code asks you to approve it.

What belongs in CLAUDE.md, and what belongs in a skill or a hook?

CLAUDE.md is for facts Claude needs in every session and cannot guess: the build and test commands, conventions that differ from the defaults, and gotchas. The docs suggest asking of every line, "Would removing this cause Claude to make mistakes?" File-by-file tours, tutorials and anything Claude can work out by reading the code do not belong there.

Read more (2)

A multi-step procedure, or anything that matters for only one part of the codebase, belongs elsewhere. A skill loads on demand, when you invoke it or when Claude decides it is relevant. A rule in .claude/rules/ with paths frontmatter loads only when Claude touches a matching file. That is why this repo's blog publishing pipeline lives in its publishing-blog-posts skill.

Anything that must happen every time belongs in a hook or a permission rule. CLAUDE.md reaches Claude as a user message after the system prompt, so it shapes behaviour without enforcing it. Each of this repo's hard rules has a backstop that fails on its own: assertBlogParity() breaks the build, the merge script rejects bad blog JSON, and a test fails if a figure creeps back into a figure-free post.

How long should a CLAUDE.md file be?

The docs target under 200 lines per file. Longer files consume more context and reduce adherence, and Claude Code shows a warning at startup and in /status when a file passes the recommended length. If Claude keeps ignoring a rule you did write down, the file is probably too long and the rule is getting lost.

Read more (1)

Three cheap habits help. Block-level HTML comments are stripped before the file reaches Claude, so a note explaining why a rule exists costs no context. Emphasis works only when it is rare: mark many lines IMPORTANT and none stands out. And for a checked-in file, /doctor proposes cuts for content Claude can derive from the code, while /doctor prompt-audit looks for stale or conflicting instructions.

How this site does it

The real config behind this lesson

Excerpts from the repository that builds and deploys this website, quoted as they are.

CLAUDE.md:103-109
## Hard rules — build breaks if violated

1. **`messages/en.json` and `messages/id.json` must have the exact same key tree.** Missing key in either file → runtime error.
2. **No `{`, `}`, or `<` in blog `content.*` JSON values.** The merge script rejects them. Put markup in JSX; put ICU interpolation only in non-blog UI strings.
3. **Every slug in `BLOG_POSTS_STATIC` must have an entry in `BLOG_META` (and vice versa).** `assertBlogParity()` fails the build otherwise.
4. **Every component key read in a blog component must exist in the translation fragment.** The merge script validates this.
5. **Always import `Link`, `useRouter`, `usePathname` from `@/i18n/navigation`, not from `next/link` or `next/navigation`.** Bare Next.js navigation loses the locale prefix.

Five rules, one line each, and each says what breaks. That is the docs' advice in practice: concrete enough to check, short enough to read every session. Rules 2 to 4 also name their backstop, the merge script or assertBlogParity(), so the rule holds even on a day Claude misreads it.

CLAUDE.md:32-38
npm run dev        # Turbopack dev server → http://localhost:3000
npm run deck       # regenerate public/Matthews-Wong-Profile.pdf (the 16:9 profile deck)
cd video && npm run film:intro  # re-render the home page's parallax intro film (see video/README.md)
npm run build      # Production build — runs assertBlogParity(), typecheck implicit
npm run lint       # ESLint
npm run posts:stale # which posts are going stale, flagship topics first
npx tsc --noEmit   # Explicit typecheck without building

The Commands block: the first thing in the file after the stack table. It lists commands Claude cannot guess, such as this repo's own deck and film renders, and each comment says what the command is for, so Claude picks the right one without reading package.json first.

CLAUDE.md:48-49
`npm run build` runs a `prebuild` step that fails if `lib/blog-sources.json` is
stale — regenerate with `npm run sources:build` after editing any sources block.

A gotcha written down once. Without these two lines, Claude would see the build fail after editing a sources block and have to work out on its own that lib/blog-sources.json is generated. The line tells it the cause and the fix together.

What breaks

Mistakes that cost real time

  • Treating CLAUDE.md as a policy engine

    A line like "never edit .env" in CLAUDE.md is a request. Claude usually follows it, but nothing stops the call. Anything that must hold every time needs a permission deny rule or a PreToolUse hook; the next lesson covers the rules.

  • Two layers that contradict each other

    Files are concatenated, not ranked, so "use npm" in your user file and "use pnpm" in the project leave Claude to choose. Remove the contradiction instead of hoping the load order settles it.

  • Splitting into imports to save context

    Every @import loads at launch, so a long file split five ways costs exactly what it did before. Move rules for one folder into .claude/rules/ with paths frontmatter, and procedures into skills.

  • What I'd change here: the length

    This repo's CLAUDE.md runs past 500 lines, well over the 200-line target. About 135 of them, under "Adding a blog post", repeat procedures that already live in the publishing-blog-posts and drawing-blog-diagrams skills. Moving them out would leave the facts and the hard rules in the always-loaded file.

Where an instruction should live
When it loadsEvery session, at launchWhen Claude touches a matching fileWhen invoked, or when Claude finds it relevantRuns on its event; adds no context unless it returns output
Best forCommands, conventions, gotchasRules for one folder or file typeProcedures and reference materialChecks that must run every time
Enforced?No: it is contextNo: it is contextNo: Claude applies itYes: it always fires
Try it yourself

Do it on your own machine

You need Claude Code and any git repository you can experiment in. Ten minutes, nothing to install.

  1. Run /init in a practice repo. If a CLAUDE.md already exists, /init suggests improvements instead of overwriting it. Then delete every line Claude could work out by reading the code.

    /init
  2. Rewrite vague rules as checkable ones, and leave a note for humans in an HTML comment. Comments are stripped before Claude reads the file.

    - Run `npm test` before committing
    - API handlers live in `src/api/handlers/`
    
    <!-- why: the API tests need a local Redis -->
  3. Move a rule that only matters for one folder into a path-scoped rule. It loads only when Claude reads or edits a matching file.

    mkdir -p .claude/rules
    cat > .claude/rules/api.md <<'EOF'
    ---
    paths:
      - "src/api/**/*.ts"
    ---
    
    - All API endpoints must include input validation
    EOF
  4. Create a subfolder CLAUDE.md from your shell, not through Claude, so you can watch it load later.

    mkdir -p src/api && touch src/api/app.ts
    echo '- Use the shared error format in src/api/errors.ts' > src/api/CLAUDE.md
  5. Start a new session and run /context. The root CLAUDE.md is under Memory files and src/api/CLAUDE.md is not. Ask Claude to read src/api/app.ts and watch a Loaded line appear.

    /context
Quick quiz

Three questions before you move on

Pick an answer to see why it is right or wrong. Nothing is saved or graded, so guess freely.

  1. 1.You add src/api/CLAUDE.md, start Claude in the repo root and ask it to summarise the README. /context does not list the new file. Why?
  2. 2.Your API conventions take 80 lines of CLAUDE.md, but most sessions never touch the API. Which change stops them costing context in those sessions?
  3. 3.Your organisation's managed CLAUDE.md says never to read .env, yet one session reads it. What makes the rule hold every time?
Think it through

Answer in your head, then check

  1. 1.Your ~/.claude/CLAUDE.md says "use npm" and the project's CLAUDE.md says "use pnpm". Which wins?Show the answer

    Answer:Neither overrides the other. Both are concatenated, with the project file read later, and the docs warn that Claude may pick either. Remove the contradiction instead of relying on order.

  2. 2.You split a 600-line CLAUDE.md into five @imports. Did you save context?Show the answer

    Answer:No. Imported files load at launch, the same as inline text. To defer loading, use path-scoped rules or skills.

  3. 3.An instruction seems lost after /compact. Where did it probably live?Show the answer

    Answer:It was given only in conversation, or it sits in a subfolder CLAUDE.md or a path-scoped rule that has not loaded again yet. The project-root CLAUDE.md is re-read from disk after compaction.

  4. 4.Claude must never edit .env. Is a line in CLAUDE.md enough?Show the answer

    Answer:No. CLAUDE.md is context, not enforcement. Use a permission deny rule or a PreToolUse hook, which Claude Code applies whatever Claude decides.

Go deeper

Related deep dives on this site