Chapter 2 of 8
CLAUDE.md Writing Best Practices
How to write effective CLAUDE.md files — persistent context, project instructions, living documentation, hierarchical structure, and comparison with .cursorrules and AGENTS.md.
CLAUDE.md Writing Best Practices
CLAUDE.md is the persistent memory file that Claude Code reads at the start of every session. It functions as a project-level system prompt — a living document that tells the AI agent your codebase conventions, build commands, architectural constraints, and workflow rules. Unlike ad-hoc prompts that you retype each session, CLAUDE.md persists across sessions and ensures consistent behavior without repeated instructions.
This guide covers how to write effective CLAUDE.md files, from basic templates to hierarchical multi-file structures. We also compare CLAUDE.md with competing formats — .cursorrules, AGENTS.md, and .github/copilot-instructions.md — so you can choose the right approach for your toolchain.
Related: For hands-on loop patterns using CLAUDE.md, see the Loop Engineering in Claude Code tutorial. For extending CLAUDE.md with automated hooks, see the Hooks Lifecycle Guide.
Why CLAUDE.md Matters
Every Claude Code session starts the same way: the agent reads your project's CLAUDE.md files, merges them with its built-in system prompt, and begins work. This means CLAUDE.md is not optional documentation — it is the primary control surface for how Claude Code behaves in your project.
The key benefits:
- Consistency — the same rules apply across every session, every developer, every machine.
- Token efficiency — instructions are loaded once, not re-sent per iteration.
- Onboarding — new team members (human and AI) get project context immediately.
- Living documentation — the file evolves with the project, staying accurate by construction.
The Hierarchical Structure
Claude Code reads CLAUDE.md files from multiple locations, merging them in a defined priority order. Understanding this hierarchy is essential for organizing instructions at the right scope.
┌─────────────────────────────────────────────────────────────┐
│ CLAUDE.md Hierarchy │
│ │
│ 1. ~/.claude/CLAUDE.md (user-global) │
│ Personal preferences, tool aliases, global conventions │
│ ↓ merges into ↓ │
│ 2. <project>/CLAUDE.md (project-root) │
│ Project-wide rules, build commands, architecture │
│ ↓ merges into ↓ │
│ 3. <project>/dir/CLAUDE.md (directory-level) │
│ Directory-specific rules (e.g., src/api/, src/db/) │
│ ↓ merges into ↓ │
│ 4. In-session memory (conversation-level) │
│ Commands, /init output, user preferences │
└─────────────────────────────────────────────────────────────┘
Lower-priority files are read first and can be overridden by higher-priority ones. Directory-level CLAUDE.md files are only loaded when Claude Code accesses files in that directory, which keeps the context window lean.
Practical Hierarchy Example
Consider a monorepo with a frontend app and a backend API:
monorepo/
CLAUDE.md # Global: pnpm workspaces, shared lint config
apps/
web/
CLAUDE.md # Next.js-specific: app router, i18n config
api/
CLAUDE.md # Express-specific: middleware chain, auth
packages/
shared/
CLAUDE.md # Shared lib: export conventions, type rules
When Claude Code works in apps/web/, it loads the project-root CLAUDE.md and the apps/web/CLAUDE.md. When it moves to apps/api/, the web-specific instructions are dropped and the API-specific ones are loaded. This scoped loading prevents context bloat — a problem documented in where context overflow ranks among the four major production failure scenarios.
Essential Sections for Every CLAUDE.md
Not every CLAUDE.md needs every section, but the following covers the core structure most projects benefit from:
# Project Name
One-paragraph description of what this project does and why it exists.
## Tech Stack
- Language: TypeScript 5.4
- Runtime: Node.js 20
- Framework: Next.js 15 (App Router)
- Package manager: pnpm
- Database: PostgreSQL 16 via Prisma
## Build and Test Commands
- `pnpm dev` — Start development server on :3000
- `pnpm build` — Type-check, lint, and build for production
- `pnpm test` — Run Vitest unit tests
- `pnpm test:e2e` — Run Playwright end-to-end tests
- `pnpm lint:fix` — Auto-fix ESLint and Prettier issues
## Code Conventions
- Use named exports, not default exports
- Prefer `interface` over `type` for object shapes
- All async functions must handle errors explicitly (no unhandled rejections)
- Component files use PascalCase: `UserProfileCard.tsx`
## Architecture
- `src/app/` — Next.js App Router pages and layouts
- `src/components/` — Shared React components
- `src/lib/` — Utility functions and helpers
- `src/api/` — API route handlers
## Rules
- Never modify files in `prisma/migrations/` — use `pnpm prisma migrate dev`
- Always run `pnpm lint:fix` before committing
- API routes must validate input with Zod schemas
A good CLAUDE.md is a reference card, not a wiki page. Anthropic recommends keeping it under 500 lines — beyond that, you risk wasting context tokens on instructions the agent rarely needs.
Writing Effective Instructions
The difference between a useful CLAUDE.md and a waste of tokens comes down to how instructions are phrased. Four principles apply:
1. Be Specific, Not Vague
# Bad: vague
- Write clean code
- Follow best practices
# Good: specific and verifiable
- Use named exports for all modules (no default exports)
- Run `pnpm test` after any code change; fix failures before moving on
- All API responses must follow the { data, error, meta } envelope pattern
Specific instructions produce specific behavior. Vague instructions produce inconsistent results.
2. Write Rules, Not Preferences
Every line in CLAUDE.md is a directive, not a suggestion. If unsure whether something should be a rule, leave it out:
# Bad: preference that creates inconsistency
- Try to use TypeScript where possible
- Prefer functional components but class components are okay too
# Good: clear rule
- All new files must be TypeScript (.ts or .tsx)
- React components must be functional with hooks (no class components)
3. Keep It Factual
Describe what the project is, not what it should be:
# Bad: aspirational
- We plan to migrate to GraphQL eventually
- Consider using Rust for performance-critical paths
# Good: factual
- API layer uses REST with OpenAPI 3.1 schema in /src/api/openapi.yaml
- Performance-critical paths are in /src/core/ with WASM compilation
4. Include Negative Rules
Agents need to know what not to do as much as what to do:
## Do Not
- Do not modify files in `generated/` — auto-generated from Prisma schema
- Do not add new dependencies without running `pnpm dlx npm-check-updates` first
- Do not use `any` type — use `unknown` and narrow with type guards
- Do not commit `.env.local` or any file matching `.env*`
Directory-Level CLAUDE.md Files
For large projects, a single root CLAUDE.md becomes unwieldy. Directory-level files provide scoped instructions loaded only when the agent works in that directory — a direct token optimization. Create one when the directory has 10+ files with conventions differing from the root.
Example: API Directory
# src/api/ — API Route Handlers
All API routes follow the same pattern:
1. Validate input with Zod schema
2. Call service layer (never database directly from routes)
3. Return response in { data, error, meta } envelope
## Error Handling
- Use `handleApiError()` from `@/lib/errors` for all catch blocks
- Return appropriate HTTP status codes (400/401/404/500)
- Never expose stack traces in production responses
Example: Database Directory
# prisma/ — Database Schema and Migrations
## Schema Rules
- All tables must have `createdAt` and `updatedAt` timestamps
- Use UUIDs for primary keys, not auto-incrementing integers
- All foreign keys must have ON DELETE behavior specified
## Migration Workflow
- Schema changes: `pnpm prisma migrate dev --name descriptive-name`
- Never edit migration SQL files manually
- Production migrations: `pnpm prisma migrate deploy` (never `migrate dev` in prod)
Living Documentation: Keeping CLAUDE.md Current
The biggest risk with CLAUDE.md is staleness. Instructions that no longer match the codebase actively mislead the agent into applying outdated rules.
Claude Code's /init command generates a CLAUDE.md by scanning your project structure — useful for bootstrapping but should be reviewed and refined by hand:
# Generate initial CLAUDE.md from project structure
claude /init
# Review it, correct inaccuracies, and add project-specific rules
For ongoing maintenance, treat CLAUDE.md updates as part of the definition of done — update it whenever you change a build command, add a convention, or remove a directory.
Comparison: CLAUDE.md vs. .cursorrules vs. AGENTS.md
Multiple AI coding tools now support project-level instruction files. The formats overlap but serve different tools with different capabilities:
| Feature | CLAUDE.md | .cursorrules | AGENTS.md | .github/copilot-instructions.md |
|---|---|---|---|---|
| Tool | Claude Code | Cursor | Amazon Q / Codewhisperer | GitHub Copilot |
| Location | Project root or any directory | Project root only | Project root only | .github/ directory |
| Hierarchy | Multi-level (user, project, directory) | Single file | Single file | Single file |
| Directory scoping | Yes — loaded per-directory | No — always global | No — always global | No — always global |
| Auto-generation | /init command | Not available | Not available | Not available |
| Community adoption | Growing rapidly (Anthropic-backed) | Large (Cursor user base) | Small (AWS ecosystem) | Large (GitHub ecosystem) |
The key architectural difference is CLAUDE.md's hierarchical loading. .cursorrules, AGENTS.md, and .github/copilot-instructions.md are all single-file solutions — one global config per project. CLAUDE.md supports per-directory overrides, which is a significant advantage for monorepos and large codebases where different directories follow different conventions.
Choosing Between Formats
Many teams use multiple tools simultaneously. Rather than choosing one format, maintain a canonical CLAUDE.md and derive the others. Since formats are similar Markdown, a simple copy works:
cp CLAUDE.md .cursorrules
CLAUDE.md uses standard Markdown with no tool-specific syntax, making it the best candidate for a canonical instruction file. .cursorrules often uses YAML-like key:value pairs (# Framework: Next.js 15), while AGENTS.md may include tool metadata (# @tool amazon-q). CLAUDE.md sticks to plain ## headers and imperative mood.
Advanced Patterns
Model-Specific Instructions
When using Claude Code with model tiering (as described in the Claude Models for Loop Engineering guide), include model-selection guidance in CLAUDE.md:
## Model Selection
- Use opus for: architecture decisions, cross-module refactoring, production debugging
- Use sonnet for: feature implementation, test writing, code review
- Use haiku for: lint fixes, import sorting, file renaming
- If a single iteration exceeds 50K input tokens, pause and confirm before continuing
Cost Guardrails
CLAUDE.md can encode cost-conscious behavior that persists across sessions:
## Cost Constraints
- Budget cap: $2.00 per session
- Before running any bulk operation (renaming, reformatting), estimate affected files
- Batch file operations into groups of 20; confirm before each batch
Integration with Hooks
Claude Code hooks (configured in .claude/settings.json) can extend CLAUDE.md with automated behaviors. A common pattern is auto-updating CLAUDE.md when the project structure changes:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"command": "if [ \"$FILE\" = \"package.json\" ]; then claude /init --quiet; fi"
}
]
}
}
Common Mistakes
1. Making CLAUDE.md Too Long
Every token in CLAUDE.md is loaded into the context window at session start. The InfoQ context engineering principles identify "compression" as the first step in managing agent context — and a bloated CLAUDE.md is the opposite. Fix: Keep the root under 200 lines; move details to directory-level files.
2. Duplicating README Content
CLAUDE.md gives actionable instructions to an AI agent; the README explains the project to humans. Fix: README describes what and why. CLAUDE.md prescribes how.
3. Including Static Information
License text, contributor lists, and changelogs never change agent behavior and are wasted tokens. Fix: Only include information that affects how the agent works.
4. Forgetting to Test
Start a fresh Claude Code session and ask it to perform a task guided by your instructions. If it ignores a rule, the rule may be too vague or buried too deep.
Quick Reference Template
For new projects, start with this minimal template and expand as needed:
# Project Name
<One sentence.>
## Commands
- `npm run dev` — Development server
- `npm run build` — Production build
- `npm run test` — Test suite
## Conventions
- <Convention 1>
- <Convention 2>
## Architecture
- `src/` — Source code
- `tests/` — Test files
## Rules
- <Rule 1>
- <Rule 2>
This 15-line template gives Claude Code enough context to be productive immediately. Expand it as the project grows and the agent's behavior reveals gaps.
Related Resources
- Loop patterns: Loop Engineering in Claude Code tutorial
- Model tiering: Claude Models for Loop Engineering guide
- Token management: Token Overload Prevention tutorial
- Automation: Hooks Lifecycle Guide