Writing a CLAUDE.md That Actually Works
Every CLAUDE.md file gets loaded into context on every session. Most teams treat it like documentation — a place to describe the project, list the tech stack, explain what the tests do. That is the wrong mental model and it is why most CLAUDE.md files are both too long and too ineffective.
CLAUDE.md is behavioral programming. Its job is to change how Claude makes decisions, not to describe facts that Claude can read from the codebase itself.
The Core Mental Model
flowchart LR
subgraph Wrong[Wrong approach - Documentation]
D1[What React is]
D2[Tech stack list]
D3[What the tests do]
D4[Architecture overview]
end
subgraph Right[Right approach - Behavioral programming]
R1[Commands Claude cannot guess]
R2[Conventions that differ from defaults]
R3[Decision rules for ambiguous situations]
R4[Known gotchas and failure modes]
end
Wrong -->|Wastes tokens\nClaude ignores it| X[Poor results]
Right -->|Shapes decisions\nHighly actionable| Y[Consistent results]
Claude already knows what React is. It can read your package.json to learn your stack. It understands standard testing patterns. Writing those things in CLAUDE.md wastes context without influencing behaviour.
What Claude cannot know without being told: the custom deploy command your team built, the specific reason you chose a non-standard folder structure, the production database host name format, the fact that your auth service requires a specific environment variable to initialise.
Structure That Works
Here is a template that covers what matters without bloating the file:
# Project: Platform API
## Commands
Commands Claude cannot guess from reading the code:
- Build: `npm run build`
- Tests: `npm test` — run a single test with `npm test -- --grep "auth"`
- Staging deploy: `./scripts/deploy.sh staging`
- Database migrations: `npm run db:migrate`
- Seed development data: `npm run db:seed`
## Conventions (where we differ from defaults)
- 4-space indentation, not 2
- camelCase for functions and variables, PascalCase for classes and types
- ES modules only — never use CommonJS require()
- All async functions must have explicit error handling — no unhandled rejections
## Architecture Decisions
- API handlers: `src/api/handlers/` — one file per resource
- Shared utilities: `src/lib/` — only pure functions, no side effects
- Database queries: `src/db/queries/` — raw SQL only, no ORM
- Tests colocate with source: `auth.ts` → `auth.test.ts` in the same folder
## Model Routing
- Haiku: renaming, formatting, simple lookups, one-liners
- Sonnet: standard implementation, debugging, code review, tests
- Opus: architecture decisions, complex refactors, security analysis
## Known Gotchas
- Auth service requires NODE_ENV=production to initialise — set it in tests
- Database connections pool at 10 — do not exceed in tests or you get timeouts
- The payment processor sandbox rejects amounts over $10,000 — use $99.99 in tests
- Migrations do not auto-run in tests — call `db.migrate()` manually in test setup
## Git
- Feature branches: `feature/description`
- All changes via PR — never push to main directly
- PR titles must include the ticket number: `PLAT-123: description`
What to Include vs What to Skip
flowchart TD
Q[New piece of information] --> A{Can Claude learn this\nby reading the codebase?}
A -->|Yes - it is in the code| Skip[Skip it\nWaste of context]
A -->|No - it requires\nexternal knowledge| B{Does it affect\nhow Claude makes decisions?}
B -->|No - it is just a fact| Skip
B -->|Yes - it changes behaviour| Include[Include it\nHigh value]
Include:
- Commands Claude cannot construct from reading the code
- Conventions that differ from widely-accepted standards
- Rules for ambiguous situations (“when in doubt, write a test before the fix”)
- Known failure modes and their causes
- Model routing guidelines (saves significant tokens over time)
- Security rules: “never log request bodies”, “all user input must be validated at the API boundary”
Skip:
- Technology definitions and descriptions
- Standard language conventions (Claude knows TypeScript style)
- Anything documented in README.md (Claude can read that)
- Team member names and responsibilities
- Historical decisions that are no longer relevant
- Anything that changes frequently (put it in a file Claude can read on demand)
Path-Scoped Rules for Large Projects
For monorepos or projects with multiple distinct areas, use path-scoped rule files instead of one giant CLAUDE.md:
.claude/
├── CLAUDE.md ← global rules (always loaded)
└── rules/
├── api.md ← loads when editing src/api/**
├── infra.md ← loads when editing infra/**
└── frontend.md ← loads when editing src/frontend/**
Each file includes a frontmatter paths section:
---
paths:
- "src/api/**/*.ts"
---
# API Rules
- All endpoints must validate the Authorization header before any business logic
- Return 422 (not 400) for validation errors — include field-level error details
- Never return stack traces in error responses
- Log request IDs for all errors using logger.error({ requestId, error })
This keeps each rule set small and relevant. Rules about API security do not load when Claude is editing Terraform configs.
The Memory System
For persistent context across sessions, Claude Code supports auto memory in ~/.claude/projects/<project>/memory/. These are markdown files that Claude reads and writes automatically to remember facts about your project and your preferences.
Organise memory by type:
~/.claude/projects/my-project/memory/
├── MEMORY.md ← index file with pointers
├── user_prefs.md ← your personal preferences
├── feedback.md ← corrections and lessons learned
├── project_ctx.md ← project-specific context
└── decisions.md ← architectural decisions reached
Sample feedback.md:
---
name: Feedback - Database queries
type: feedback
---
Always use parameterised queries in this project, never string interpolation.
**Why:** A previous incident where a dynamic filter was built with string concatenation
caused a data leak in the staging environment.
**How to apply:** Any time writing a database query, use `db.query(sql, [params])` not
`db.query(sql + userInput)`.
The “Why” and “How to apply” sections are important — they help Claude judge edge cases rather than blindly following a rule.
Common Mistakes
1. Writing for humans, not for Claude
Your team lead reads CLAUDE.md and adds context like “We switched from Mongoose to Prisma in 2023 because of type safety issues.” This is interesting history but gives Claude no actionable instruction. Cut it.
2. Contradicting yourself
“Always write tests before code” and “tests are optional for hotfixes” in the same file creates inconsistent behaviour. Claude will apply one or the other unpredictably. Pick one and remove the other.
3. Exceeding 150 lines
The longer the file, the more rules Claude deprioritises. A 300-line CLAUDE.md means some rules at the bottom are routinely ignored. Better to have 80 lines of rules that are always followed than 300 lines of rules that are sometimes followed.
4. Stale rules
CLAUDE.md needs maintenance. Review it every month and remove anything that is no longer true. Stale rules reduce trust in the rules that remain.
Quick Start
Run /init in any project and Claude will generate a starter CLAUDE.md based on what it can read from your codebase. Edit the output — remove the generic parts, add your specific commands and gotchas — and you will have a solid file in under 15 minutes.
