Claude Code project memory: how CLAUDE.md files work across nested directories
If you've been using a single CLAUDE.md at your project root, you're missing half of how Claude Code's memory system works.
Claude Code reads multiple CLAUDE.md files in a hierarchy. Understanding this unlocks a completely different way to organize instructions for complex projects.
The CLAUDE.md hierarchy
When you run Claude Code in a directory, it reads:
-
~/.claude/CLAUDE.md— your global instructions (applies everywhere) -
{project-root}/CLAUDE.md— project-wide instructions -
{current-subdirectory}/CLAUDE.md— directory-specific instructions - Any CLAUDE.md in intermediate directories
All of these get merged together. More specific files override more general ones.
your-project/
├── CLAUDE.md # "This is a Node.js API, use async/await"
├── src/
│ ├── CLAUDE.md # "These are route handlers, always validate input"
│ ├── routes/
│ └── models/
│ └── CLAUDE.md # "These are Mongoose models, always add timestamps"
└── tests/
└── CLAUDE.md # "Use Jest, describe/it format, 90% coverage minimum"
Global CLAUDE.md: your personal AI preferences
This one is underused by most developers.
Create ~/.claude/CLAUDE.md to set preferences that apply across every project:
# Global Claude preferences
## Communication style
- Be concise. Skip the preamble.
- When suggesting changes, show diffs not full files
- If something is ambiguous, ask ONE clarifying question before proceeding
## Code style (my defaults)
- TypeScript over JavaScript when possible
- Functional patterns preferred over OOP
- Named exports, not default exports
- Always handle errors explicitly
## Things I never want
- No inline comments explaining what the code does (I can read code)
- No TODO comments — either do it or don't
- No `any` types in TypeScript
Every project you open will inherit these preferences automatically.
Project CLAUDE.md: team-shared context
The root CLAUDE.md should contain context that every developer on your team needs:
# Project: Payment API
## Architecture
- Express + TypeScript + Prisma
- PostgreSQL on RDS
- Deployed to AWS ECS via GitHub Actions
## Critical rules
- NEVER log payment card data, even partially
- All financial calculations use decimal.js (not floating point)
- Database migrations go in /migrations, never modify existing ones
## Test database
- Local: postgresql://localhost/payments_test
- Seed: `npm run db:seed`
## Common commands
- `npm run dev` — start with hot reload
- `npm run test:watch` — jest in watch mode
- `npm run prisma:studio` — browse database
Subdirectory CLAUDE.md: specialist context
This is where the hierarchy gets powerful. Give Claude different instructions depending on where it's working:
# /src/routes/CLAUDE.md
## Route handler conventions
- Input validation with zod before any business logic
- All handlers are async, return void, use res.json()
- Error handling: throw ApiError, middleware catches it
- Rate limiting is applied at the router level, not handler level
## Authentication
- req.user is always populated by auth middleware
- Never trust req.body.userId — always use req.user.id
## Response format
{
success: boolean,
data: T | null,
error: string | null
}
# /src/workers/CLAUDE.md
## Background job context
- Workers run in separate processes, no access to request context
- Use job-scoped logger: getLogger(job.id)
- Always handle job failure explicitly — BullMQ won't retry by default
- Max execution time: 5 minutes. Long jobs must checkpoint progress.
## Database in workers
- Create a fresh Prisma client per worker process
- Close it in the finally block
Now when Claude is editing a route handler, it knows the route conventions. When it's editing a worker, it knows the job constraints. Without you having to explain it every time.
The ~/.claude/settings.json complement
CLAUDE.md is for what Claude should know. settings.json is for what Claude is allowed to do.
{
"permissions": {
"allow": [
"Bash(npm run test:*)",
"Bash(npm run lint)",
"Bash(git diff:*)",
"Bash(git log:*)"
],
"deny": [
"Bash(rm -rf:*)",
"Bash(git push:*)",
"Bash(npm publish:*)"
]
}
}
Brain (CLAUDE.md) + Guardrails (settings.json) = a Claude that knows your codebase and can't accidentally nuke production.
The rate limit problem
As your CLAUDE.md files get richer — global preferences, project context, subdirectory specializations — your token usage goes up. Every conversation starts by loading all relevant CLAUDE.md files into context.
If you're hitting rate limits mid-session, there are two solutions:
Option 1: Keep CLAUDE.md files lean. Use bullet points, not prose. Reference don't explain.
Option 2: Route through a proxy that removes rate limits entirely.
export ANTHROPIC_BASE_URL=https://api.simplylouie.com
export ANTHROPIC_API_KEY=your-key
SimplyLouie is ✌️$2/month and removes the overloaded errors so your CLAUDE.md-heavy sessions don't get interrupted.
Quick reference: what goes where
| Content | Where |
|---|---|
| Personal code style preferences | ~/.claude/CLAUDE.md |
| My preferred communication style | ~/.claude/CLAUDE.md |
| Project architecture overview | {root}/CLAUDE.md |
| Team conventions & critical rules | {root}/CLAUDE.md |
| Route/module specific patterns | {subdir}/CLAUDE.md |
| Tool permissions | ~/.claude/settings.json |
| Project-specific tool overrides | {root}/.claude/settings.json |
Setting this up in your project
# Create global preferences
mkdir -p ~/.claude
touch ~/.claude/CLAUDE.md
# Create project-level memory
touch ./CLAUDE.md
# Create subdirectory specializations
touch ./src/routes/CLAUDE.md
touch ./src/workers/CLAUDE.md
touch ./tests/CLAUDE.md
# Tell Claude about its own memory system
claude "Read all CLAUDE.md files in this project and summarize what you know about the codebase"
That last command is useful to verify Claude is reading everything correctly — it will tell you exactly what context it loaded.
The hierarchy is one of Claude Code's most powerful features, and most people only use 20% of it by having a single root CLAUDE.md. Subdirectory specialization is what makes Claude genuinely feel like a team member who knows the codebase.
Running Claude Code sessions that get interrupted by rate limits? Set ANTHROPIC_BASE_URL=https://api.simplylouie.com and ANTHROPIC_API_KEY=your-key — SimplyLouie is ✌️$2/month.
Top comments (0)