CLAUDE.md is the most underused feature in Claude Code. Most people know it exists, few know the full scope: three separate levels of hierarchy, cross-file imports, tight integration with auto-memory, and a specific loading order that determines what gets priority. This is a complete reference.
The three levels
Claude Code reads CLAUDE.md files from three locations, loaded simultaneously at the start of every session:
| Level | Path | Scope |
|---|---|---|
| Global | ~/.claude/CLAUDE.md |
Every project on this machine |
| Project root | CLAUDE.md in your working directory |
This project; commit to repo so the team benefits |
| Sub-directory | CLAUDE.md in any sub-directory Claude navigates to |
That directory only; useful for monorepos |
All three are loaded and combined. There's no strict override — Claude synthesizes all context it finds, with the more-specific file generally winning when instructions conflict directly. If your global file says "use tabs" and your project file says "use spaces," Claude Code sees both and defers to the project-level instruction.
What goes at each level
Global is for preferences that don't change across projects: your default language, testing philosophy, commit message format, tools you use everywhere, and habits you want enforced everywhere. Think of it as your developer profile.
Project-level is for everything specific to this codebase: architecture, commands to run, current working context, and constraints that aren't obvious from the code. This file belongs in version control. Every engineer on the team benefits from it, and it evolves alongside the project.
Sub-directory files are useful in large monorepos where packages have genuinely different conventions — a React frontend with different lint rules than a Go service, or a data pipeline that needs different tooling than the API layer.
A concrete global CLAUDE.md
# Preferences
Language: TypeScript (strict: true). Never disable a strict check without explaining why.
Package manager: pnpm
Testing: avoid mocks where possible — prefer integration tests against real dependencies.
Commits: conventional commits (feat/fix/chore/docs/test). Never mention tools in commit messages.
Comments: only write them when the WHY is non-obvious. Never restate what the code does.
# Things I hate
- Barrel files (index.ts that re-exports everything)
- console.log left in production code
- Optional chaining when the value is never actually undefined in practice
- TODO comments without a ticket reference
- Over-abstracted helpers for code that's only used in one place
Short enough to scan in two seconds. Specific enough to actually change Claude's default behavior.
A concrete project-level CLAUDE.md
# my-saas-platform
Architecture: pnpm monorepo
- apps/web — Next.js App Router (frontend)
- apps/api — Express REST API
- apps/worker — BullMQ background jobs
- packages/db — Drizzle ORM + generated types
Rebuild: `cd packages/db && npm run build` when schema changes
## Commands
- dev: `pnpm dev` (turborepo starts all apps)
- test: `pnpm test -- --run` (non-watch)
- migrate: `pnpm --filter @myapp/db run migrate`
## Non-obvious patterns
- Sidebar uses a --sw CSS variable for width; both sidebar and main content reference it
- Route groups use parens: (app)/ and (auth)/. Parens are excluded from URL paths.
- Paid tier logic: isPro = tier === 'pro' || tier === 'team'. CSS class stays .pill-pro for both;
only label text changes. Never gate features on isTeam alone.
## What we're actively working on
Stripe Team billing — price IDs are in k8s secrets. Webhook endpoint needs testing.
Don't touch auth middleware — legal review pending.
This is committed to the repo. Every engineer who opens a Claude Code session in this project gets this context without any setup.
The @import syntax
You can pull in other files from within CLAUDE.md using the @ import syntax:
# Project context
@docs/architecture-decisions.md
@CONVENTIONS.md
Claude Code reads those files inline, as if their content appeared directly in CLAUDE.md. This is useful when conventions are already documented elsewhere — you don't duplicate them, you just reference them. The path is relative to the file containing the import.
It supports any plain text or markdown file. The most common use case is importing an architecture decision record or a conventions document that the team was already maintaining.
What auto-memory adds to this picture
CLAUDE.md is what you write upfront. Auto-memory is what Claude writes during your sessions.
With Settings → Memory enabled, Claude Code stores corrections and preferences as it learns them — in ~/.claude/projects/<project>/ as structured markdown files. If you push back on a suggestion, if you say "no, we don't do it that way," if you establish a pattern — Claude captures it as a separate memory file and loads it in future sessions.
CLAUDE.md ← this project's context
~/.claude/projects/…/*.md ← what Claude learned this month
────────────────────────────────
combined context at session start
The combination is more durable than either alone. CLAUDE.md captures what you know to document before starting. Auto-memory captures what Claude learns by doing. Neither replaces the other.
Version control strategy
Commit your project-level CLAUDE.md. It's documentation. It improves the experience for the whole team. Update it when architecture changes, when you finish a major feature, when focus shifts. Treat it like a living document, not static config.
Don't commit your global ~/.claude/CLAUDE.md. It's personal. If you work across machines, manage it through your dotfiles repo or copy it manually.
Auto-memory files are machine-local by default. If you want them to follow you across machines, forgein syncs them to the cloud: install the skill into Claude Code, run /forgein auth once, then /forgein mem sync to push and pull. On your next machine, the same accumulated context is waiting.
Common mistakes
Too long. If your CLAUDE.md is several hundred lines, most of it won't be useful. Claude Code loads it all, but sheer volume doesn't help — specificity does. Prefer twenty specific constraints over two hundred vague ones.
Documenting what's in the code. If Claude can grep for it or read it from the file system, it doesn't belong in CLAUDE.md. Architecture decisions that aren't obvious from the code do belong there. File-by-file inventories don't.
Never updating it. A CLAUDE.md that describes what the project looked like six months ago is worse than no CLAUDE.md — it actively misleads. Treat it like documentation you'd want a new teammate to find accurate. Update it when you'd update a README.
The ceiling here is surprisingly high. A well-maintained CLAUDE.md makes Claude Code feel like it has continuity — like it was there last week, knows the codebase, and doesn't need to be re-briefed. That's the version of the tool worth building toward.