Previously in the series
Introduction
In the previous article, I covered the config file hierarchy, a minimal settings.json, and why deny rules are not a reliable security boundary. That gave us a working installation. This one is about the single file that determines whether Claude Code feels like a capable colleague or a confused intern.
CLAUDE.md looks simple — it is just markdown — but I have rewritten my own files enough times to know there is a real gap between having one and getting value from it. This article covers the full file hierarchy, the import mechanism, path-scoped rules, and the mistakes that cause Claude to quietly ignore what you wrote.
How CLAUDE.md gets loaded
Claude Code walks up the directory tree from your working directory, checking each level for a CLAUDE.md. It also checks a few fixed locations. From broadest scope to most specific, here is what Claude Code checks:
At the top sits managed policy — /Library/Application Support/ClaudeCode/CLAUDE.md on macOS, /etc/claude-code/CLAUDE.md on Linux. This is for enterprise teams pushing organization-wide rules. Most individual developers will never see this file, let alone create one.
User global at ~/.claude/CLAUDE.md loads in every session, across every project. Personal defaults go here — things like "never use rm -rf without confirmation" and "use the gh CLI for GitHub operations."
Project root at ./CLAUDE.md is the most common location. You commit this. It holds your stack, build commands, and coding conventions. The whole team shares it. There is an alternative at ./.claude/CLAUDE.md if you prefer keeping config files grouped under .claude/. Pick one. Do not use both — they have the same priority, and Claude will load whichever it finds, which gets confusing fast.
Local project at ./CLAUDE.local.md is for personal project-specific tweaks you do not want to commit. You need to add this to .gitignore yourself — it is not handled automatically.
Subdirectory files like packages/api/CLAUDE.md or src/components/CLAUDE.md are not loaded at launch. They load on demand when Claude starts reading files in that directory. This is where monorepos become manageable — your frontend and backend can have completely different conventions without bloating the root file.
The precedence rule is the same as for settings.json: more specific wins. If your global file says "use spaces" and your project file says "use tabs," tabs win. But do not count on clean conflict resolution. I have seen Claude blend instructions from both levels or follow whichever appeared more recently in context. Treat cross-level conflicts as undefined behavior and keep each file's scope distinct.
When the File Gets Too Long
A focused service might only need fifteen lines in its CLAUDE.md. But real projects accumulate context, and at some point, you need to split the file.
The @ import syntax handles this:
# Project overview
See @README.md for architecture details.
# Code conventions
@docs/coding-standards.md
# Personal overrides
@~/.claude/my-project-preferences.md
Claude replaces each @path with the contents of that file at load time. Your main CLAUDE.md stays short, but Claude still gets everything.
The first time Claude Code encounters external imports in a project, it shows an approval dialog. If you decline, those imports stay disabled, and the dialog does not come back — you would need to re-approve manually. Also, the @ syntax is only evaluated outside of code blocks, so file paths in code examples will not trigger an import.
Watch out for heading levels in imported files. They do not get adjusted. If your main file has a # Code conventions section and the imported file starts with its own # heading, that becomes a top-level sibling, not a subsection. There was a GitHub issue about this, closed as not planned — so do not expect a fix. Keep heading levels consistent across files yourself.
Path-Scoped Rules
For larger projects, even imports are not granular enough. You do not want your migration rules loaded every time Claude touches a React component. The .claude/rules/ directory solves this.
.claude/rules/
├── api-conventions.md
├── testing.md
├── migrations.md
└── frontend.md
Each file can include a globs frontmatter field:
---
globs: src/api/**/*.ts
---
# API conventions
- All endpoints must validate input with Zod schemas
- Use the standard error response format from src/api/errors.ts
- Never return raw database objects — always map to DTOs
Rules without frontmatter apply globally, the same as the content in the root CLAUDE.md. Rules with globs only load when Claude reads files matching those patterns. Run /memory to verify what actually loaded — I cannot stress this enough, because failures here are silent.
There is a gotcha. The documentation originally described a paths: frontmatter field. In practice, globs: is the format that works. I spent real time debugging why path-scoped rules were being silently ignored before finding the GitHub issue about it. Use globs: with comma-separated patterns.
Pitfalls
The Bloated File
This is the single most common mistake I see.
A team starts with a reasonable ten-line file. Over months, everyone adds to it. Nobody removes anything. Six months later, it is 400 lines, and Claude ignores half of it.
Anthropic's own documentation says files over 200 lines consume more context and reduce instruction adherence. That matches my experience. Even with a 1M token context window, the issue is not capacity — it is attention. The more text Claude has to wade through at the start of a session, the less reliably it follows any single instruction. Every line of CLAUDE.md competes with the code Claude is actually working on.
Here is what a bloated file looks like:
# Project overview
This is a Next.js application built with TypeScript and Tailwind CSS.
We use React Server Components where possible.
The application was started in 2023 and has grown significantly.
Our team consists of 5 developers across 2 time zones.
We follow agile methodology with 2-week sprints.
Our product manager is Sarah and our tech lead is Mike.
The application serves approximately 50,000 daily active users.
We deploy to AWS using CDK...
Claude does not need to know your team size. It does not care about your sprint cadence or your PM's name. Strip it:
# Project
Next.js 15, TypeScript strict mode, Tailwind, React Server Components by default.
One line. Claude has everything it needs to write correct code for this stack.
For every line in CLAUDE.md, ask yourself: Would removing this cause Claude to make a mistake? If the answer is no, cut it.
The Instruction-Stuffing Trap
Related to bloat, but distinct. I have seen CLAUDE.md files where half the rules ban things Claude would never do unprompted:
- Never use var, always use const or let
- Always use arrow functions for callbacks
- Never use any type, use unknown instead
- Always handle errors with try/catch
- Never use console.log in production code
- Always use named exports
- Never use default exports
Twenty rules like this eat a real chunk of context for things Claude mostly gets right on its own. I have yet to see Claude reach for var unprompted. Save your CLAUDE.md space for rules that fix problems you have actually observed. If Claude has never done the thing, you do not need a rule banning it.
Conflicting Instructions Across Files
The risk grows as you add more files to the hierarchy.
Your global file says "always use Prettier." Your project file says "use ESLint for formatting." A subdirectory rule says nothing about formatting. There is no merge strategy, no error message. Claude sees all the instructions and picks one. You get inconsistent behavior that feels random.
Treat the hierarchy like code. Review it when things go wrong. When you add a project-level rule, check whether it contradicts something in your global file.
Treating CLAUDE.md as Configuration
This one bit me more than once. CLAUDE.md is not a config file that gets parsed and enforced. It is text that goes into the prompt. Claude treats it as strong guidance, but it is not deterministic.
Adding IMPORTANT: or YOU MUST to a rule does increase the odds that Claude pays attention. It does not guarantee compliance. When every other line says IMPORTANT, Claude has no way to tell which ones you actually mean.
Hard stops belong in settings.json permissions, a linter, or a pre-commit hook. A markdown file in the prompt is the wrong place for rules that cannot tolerate exceptions.
What Earns Its Place
Here is what I have learned about what consistently earns its place in CLAUDE.md and what does not.
Build and test commands are the highest-value content. Claude needs to know how to build your project, run tests, and lint code. Without this, it guesses. With it, you can add a feedback loop — "run npm test after changing any test file," — and Claude will catch its own mistakes.
Non-obvious architecture belongs in the file. Things Claude cannot infer from the code alone. "All database access goes through /src/repositories.” “The /legacy directory is frozen — do not modify files there." These save you from explaining the same thing every session.
Conventions that break from defaults belong in the file, but only the ones where your project does something unusual. If you use standard React patterns, do not document them. If you have a custom error class or a specific import alias, that goes in.
Gotchas belong in the file. Things that have already cost you time. "The auth module uses a custom JWT library, not jsonwebtoken." "Database migrations must be reversible." You add these one at a time, each time Claude makes a mistake that a one-line rule would have prevented.
History, rationale, and business logic do not belong in the file. If you are writing a paragraph explaining why a module is structured a certain way, that belongs in a code comment or an ADR.md. CLAUDE.md is for operations.
Keeping It Alive
A static CLAUDE.md decays. Dependencies change, conventions evolve, and rules that made sense six months ago become irrelevant or get contradicted by newer additions.
Two things help. First, the # key during a session. When you catch Claude doing something wrong, press # to add a correction to CLAUDE.md without leaving the conversation. Anthropic calls this "compounding engineering" — each correction becomes a permanent context. It is the single best habit I have picked up.
Second, treat debugging sessions as maintenance triggers. When Claude does something wrong despite having a rule against it, the file is probably too long, and the rule got lost. Do not add another rule on top. Go back and trim whatever has gone stale — then look at the rule that got ignored and ask whether the wording was actually clear enough.
For new projects, /init is worth running before you write anything by hand. Claude Code scans the codebase and generates a starter file. The output tends to be verbose — trim it heavily. But it beats starting from nothing.
Summary
The file hierarchy gives you layers — global for personal defaults, project for team conventions, subdirectories for module-specific rules. The @ import syntax and .claude/rules/ keep the main file short as complexity grows.
The mistakes are predictable: files that are too long, rules for problems that do not exist, conflicts across hierarchy levels, and expectations that a markdown file will behave like an enforced configuration.
Do not try to write the perfect file on day one. Start with the basics — stack, build commands, conventions, and gotchas. Add a line each time Claude wastes your time with a mistake that a single rule would have prevented. Over weeks, those additions compound into something that makes Claude genuinely useful for your specific project.
In the next article, I will cover models, tiers, and effort — how to pick the right model for each task and what the effort settings actually change.
