1. What Is CLAUDE.md and Why Does It Matter?
Every Claude Code session starts with a blank slate. Claude has no memory of the conversation you had yesterday, no recollection of the naming convention you spent 20 minutes explaining last week, and zero knowledge of the landmines buried in your codebase. You start from zero, every single time.
CLAUDE.md is the only file Claude Code loads automatically at session start. It's your project's onboarding document — not for a new human developer, but for your AI collaborator. Think of it as a README, except instead of explaining the project to humans, you're explaining it to Claude in a way that immediately changes its behavior.
Without a CLAUDE.md, you waste the first 10–15 messages of every session re-explaining context. Claude makes wrong assumptions about your tech stack, uses the wrong test command, and occasionally reaches into a folder it absolutely should not touch. With a well-written CLAUDE.md, Claude follows your conventions from message one, runs the right commands, and avoids your landmines.
It's also worth knowing that CLAUDE.md isn't exclusive to Claude Code. Cursor, Zed, and OpenCode use AGENTS.md — same concept, different filename. The principles in this guide apply to all of them. Once you understand how to write a good CLAUDE.md, you can adapt it to any AI coding tool in minutes.
⚡ The Token Budget Reality
LLMs can reliably follow approximately 150–200 distinct instructions. Claude Code's built-in system prompt already consumes around 50 of those slots. That leaves you roughly 100–150 instruction slots in your CLAUDE.md before compliance quality starts to degrade. This is why lean CLAUDE.md files outperform comprehensive ones — you're working with a finite budget.
2. Where to Put CLAUDE.md (File Hierarchy Explained)
Claude Code supports CLAUDE.md files in three locations. They all load together and cascade — think of it like CSS specificity, where more specific (deeper) files win conflicts.
| Location | Scope | Best For |
|---|---|---|
| ~/.claude/CLAUDE.md | Global — all projects | Personal preferences: tone, response format, output language |
| ~/project/CLAUDE.md | Project root — most common | Stack, commands, style rules, architecture constraints. Commit to git. |
| ~/project/src/CLAUDE.md | Directory-scoped | Monorepos — per-package rules that override the root file |
3. The 7 Sections Every CLAUDE.md Needs
After months of iteration, I've landed on seven sections that cover everything Claude needs without bloating the file. Here's each one with the reasoning behind it.
Project Overview
2–3 lines max. What it is, the core tech, and a one-liner purpose.
# Project: ShopFlow
E-commerce platform for D2C brands. Next.js 14 App Router,
Prisma ORM, Stripe, Tailwind CSS. Deployed on Vercel.💬 The overview anchors every response. Without it, Claude sometimes drifts toward patterns from its training data rather than your actual project.
Tech Stack & Conventions
5–10 lines. Language version, framework, key libraries, naming conventions, import style.
## Tech Stack
- Node 20 / TypeScript 5.4 (strict mode)
- Next.js 14 App Router (NOT Pages Router)
- Tailwind CSS — no CSS modules
- Prisma + PostgreSQL
- Zod for all input validation
- camelCase variables, PascalCase components
- ES modules only — never require()💬 The most important line is usually the one clarifying what version or pattern you're NOT using. "App Router, NOT Pages Router" has saved me countless corrections.
Key Commands
5–8 lines. The exact commands Claude should run, not guesses.
## Commands
- Dev: npm run dev
- Build: npm run build
- Test: npx jest --watch
- Lint: npm run lint
- DB: npx prisma migrate dev
- Deploy: vercel --prod💬 Claude will try to guess your commands from package.json patterns. It's usually right, but "usually" isn't good enough when it's about to run a destructive migration.
Code Style Rules
5–10 lines. Only rules Claude gets wrong without explicit instruction.
## Code Style
- Named exports only — never default exports
- API routes return { success: boolean, data?: T, error?: string }
- Use React Query for data fetching — never useEffect for data
- Server Components are default — only add "use client" when needed
- All dates stored/returned as ISO 8601 strings💬 The key insight: don't document things Claude already knows. Only add rules for things you've had to correct Claude on at least twice.
Architecture Constraints
3–5 lines. The "never touch" rules — phrased with NEVER for emphasis.
## Architecture Rules
- NEVER modify files in /prisma/migrations/ directly
- All DB changes go through: npx prisma migrate dev
- NEVER commit .env — use .env.local
- API routes must validate with Zod before any DB call
- Images must use next/image — never raw <img>💬 These are your landmines. Phrasing with NEVER in caps genuinely improves compliance — Claude weights it differently than softer language.
File Structure Overview
5–10 lines. Only what's non-obvious. Skip anything standard.
## Structure
- app/ Pages and API routes (App Router)
- components/ Reusable UI components
- lib/ Utilities and shared logic
- lib/db.ts Singleton Prisma client — use this, don't import Prisma directly
- prisma/ Schema and migrations
- public/ Static assets💬 The lib/db.ts note is the kind of thing that saves a session. Without it, Claude might instantiate a new Prisma client in every file.
Active TODOs / Current Focus
3–5 lines. What you're working on right now.
## Current Focus
- Migrating checkout flow from Pages Router to App Router
- DO NOT refactor auth — it's being replaced next sprint
- New API routes should follow pattern in app/api/products/route.ts💬 This section has the highest ROI of any section. It eliminates the 5-minute context dump at the start of every session. Update it when your focus shifts.
Complete assembled example (all 7 sections):
# Project: ShopFlow
E-commerce for D2C brands. Next.js 14 App Router, Prisma + PostgreSQL,
Stripe, Tailwind CSS. Deployed on Vercel.
## Tech Stack
- Node 20 / TypeScript 5.4 strict
- Next.js 14 App Router (NOT Pages Router)
- Tailwind CSS (no CSS modules)
- Prisma ORM + PostgreSQL
- Zod for all input validation
- camelCase vars, PascalCase components, kebab-case files
- ES modules only — never require()
## Commands
- Dev: npm run dev
- Build: npm run build
- Test: npx jest --watch
- Lint: npm run lint
- DB: npx prisma migrate dev
- Deploy: vercel --prod
## Code Style
- Named exports only — no default exports
- API responses: { success: boolean, data?: T, error?: string }
- Use React Query for data fetching — not useEffect
- Server Components are default — add "use client" only when needed
- All dates: ISO 8601 strings
## Architecture Rules
- NEVER edit /prisma/migrations/ directly
- NEVER commit .env — use .env.local
- All API routes validate input with Zod before any DB call
- Images: next/image only — never raw <img>
## Structure
- app/ Pages and API routes
- components/ Reusable UI
- lib/ Shared utilities
- lib/db.ts Singleton Prisma client — import this, not prisma directly
- prisma/ Schema + migrations
## Current Focus
- Migrating checkout from Pages to App Router
- DO NOT refactor auth — being replaced next sprint
- Follow pattern in app/api/products/route.ts for new routes4. 7 Copy-Paste Templates by Project Type
These templates are written as if I'm actually working with each stack daily — because I have been. Each one is immediately usable. Copy the one matching your project, swap the placeholder names, and prune anything that doesn't apply.
Template 1: Next.js 14 / React
# Project: [Name]
Next.js 14 App Router with TypeScript, Tailwind CSS, and Prisma.
## Tech Stack
- Next.js 14 (App Router, NOT Pages Router)
- TypeScript strict mode
- Tailwind CSS (no CSS modules)
- Prisma ORM with PostgreSQL
- Deployed on Vercel
## Commands
- Dev: npm run dev
- Build: npm run build
- Test: npx jest --watch
- Lint: npm run lint
- DB migrate: npx prisma migrate dev
- DB studio: npx prisma studio
## Code Style
- Named exports (not default exports) for all components
- "use client" only when hooks or browser APIs are needed
- Server Components are default — don't add "use client" unnecessarily
- ES module imports only — never require()
- Destructure: import { useState } from "react"
- All API input validated with Zod
## Architecture Rules
- NEVER edit /prisma/migrations/ directly
- All API routes in app/api/ return NextResponse.json()
- API routes must validate input with Zod before DB calls
- Environment vars: .env.local (never commit .env)
- Images: next/image only — never raw <img>
- Singleton Prisma client lives in lib/db.ts
## File Structure
- app/ Pages and API routes (App Router)
- components/ Reusable React components
- lib/ Utilities and shared logic
- lib/db.ts Prisma singleton — use this everywhere
- prisma/ Schema and migrations
- public/ Static assets
## Current Focus
- [What you're working on right now]Template 2: Python / Django
# Project: [Name]
Django 5.x REST API + PostgreSQL. Serves a React frontend via DRF.
## Tech Stack
- Python 3.12 / Django 5.1
- Django REST Framework (DRF) for API
- PostgreSQL via psycopg2-binary
- Redis for caching and Celery
- Black formatter, isort, mypy for type checking
- Virtual env: .venv (activate: source .venv/bin/activate)
## Commands
- Dev server: python manage.py runserver
- Shell: python manage.py shell_plus
- Migrate: python manage.py migrate
- Make migration:python manage.py makemigrations
- Tests: python manage.py test --verbosity=2
- Coverage: coverage run -m pytest && coverage report
- Celery worker: celery -A config worker -l info
## Code Style
- Type hints on all function signatures (PEP 484)
- Black formatting (line length 88) — run before every commit
- isort for import ordering
- Docstrings on all public methods (Google style)
- snake_case for variables and functions
- Use Django ORM — never raw SQL unless explicitly needed
- All API views use DRF serializers — no manual response dicts
## Architecture Rules
- NEVER run migrations in production directly — always review first
- Settings split: config/settings/base.py, local.py, production.py
- Secrets via environment variables — never hardcode
- All model changes require migration files committed with the change
- Use select_related / prefetch_related — NEVER query inside loops
- Custom managers in models.py — not raw .filter() in views
## File Structure
- config/ Django project settings and URLs
- apps/ All Django apps (one app per domain concept)
- apps/users/ Auth, user model (custom AbstractUser)
- templates/ Django templates (admin only — DRF handles API)
- requirements/ base.txt, dev.txt, prod.txt
## Current Focus
- [Current sprint / active feature]Template 3: Node.js / Express API
# Project: [Name]
Node.js + Express REST API. TypeScript. PostgreSQL via Knex.
## Tech Stack
- Node 20 LTS / TypeScript 5.4
- Express 4.x
- Knex.js query builder + PostgreSQL
- Zod for request validation
- Jest + Supertest for testing
- ESLint + Prettier
## Commands
- Dev (hot reload): npm run dev (uses tsx watch)
- Build: npm run build (tsc)
- Start prod: npm start
- Test: npm test
- Test watch: npm run test:watch
- Migrate: npm run db:migrate
- Rollback: npm run db:rollback
- Seed: npm run db:seed
## Code Style
- All route handlers are async — always try/catch or use asyncHandler wrapper
- Standard error response: { success: false, error: string, code?: string }
- Standard success response: { success: true, data: T }
- Validate ALL request bodies/params with Zod schemas at route level
- Middleware order: cors → auth → validate → handler → errorHandler
- Named exports — never default exports from route files
## Architecture Rules
- NEVER write raw SQL — use Knex builder
- NEVER return database error messages to clients — sanitize first
- Auth middleware must run before any protected route handler
- Rate limiting applied at router level, not individual routes
- All DB queries go through repository functions in /repositories
- NEVER access req.user without running auth middleware first
## File Structure
- src/routes/ Express route definitions
- src/controllers/ Request handlers (thin — delegate to services)
- src/services/ Business logic
- src/repositories/ Database queries (Knex)
- src/middleware/ Auth, validation, error handling
- src/lib/ DB connection, logger, config
## Current Focus
- [Current active work]Template 4: Flutter / Dart
# Project: [Name]
Flutter app (iOS + Android). Riverpod for state management.
## Tech Stack
- Flutter 3.24 / Dart 3.5
- Riverpod 2.x (code generation enabled)
- go_router for navigation
- Dio for HTTP (with interceptors)
- Freezed + json_serializable for models
- Hive for local storage
## Commands
- Run: flutter run
- Build Android: flutter build apk --release
- Build iOS: flutter build ios --release
- Test: flutter test
- Analyze: flutter analyze
- Pub get: flutter pub get
- Code gen: dart run build_runner build --delete-conflicting-outputs
## Code Style
- Always run dart run build_runner after model changes
- Use Freezed for all data classes — never plain Dart classes for models
- Riverpod providers: prefer AsyncNotifierProvider for server data
- No setState in stateful widgets — use Riverpod ConsumerWidget
- Platform-specific code goes in platform/ with conditional imports
- Widget files: one widget per file, PascalCase filename
## Architecture Rules
- NEVER call APIs directly from widgets — go through a repository
- ALL API calls must handle DioException — never let them bubble raw
- Avoid BuildContext across async gaps — use mounted check
- Navigation only through go_router — never Navigator.push directly
- Local storage keys defined as constants in lib/constants/storage.dart
## File Structure
- lib/features/ Feature-based modules (auth, home, profile)
- lib/shared/ Reusable widgets and utilities
- lib/core/ App config, theme, router, DI
- lib/data/ Repositories and API services
- assets/ Images, fonts, translations
## Current Focus
- [Active feature or platform]Template 5: Go / Golang
# Project: [Name]
Go REST API. Standard library net/http + chi router. PostgreSQL via pgx.
## Tech Stack
- Go 1.23
- chi v5 router
- pgx v5 for PostgreSQL (no ORM)
- sqlc for type-safe SQL queries
- golang-migrate for migrations
- golangci-lint for linting
- testify for assertions
## Commands
- Run: go run ./cmd/server
- Build: go build -o bin/server ./cmd/server
- Test: go test ./... -v
- Test coverage: go test ./... -coverprofile=coverage.out
- Lint: golangci-lint run
- Generate SQL: sqlc generate
- Migrate up: migrate -path db/migrations -database $DATABASE_URL up
- Format: gofmt -w . && goimports -w .
## Code Style
- Always run gofmt — non-negotiable, CI will fail without it
- Error handling: check every error, no _ for error values
- Errors wrap with context: fmt.Errorf("getUserByID: %w", err)
- Use structured logging via slog (stdlib) — never fmt.Println in production
- Table-driven tests for all pure functions
- No panic() outside of main initialization
## Architecture Rules
- NEVER use ORM — SQL goes through sqlc-generated functions only
- All DB queries in internal/store/ — never in handlers
- Handlers are thin: parse → validate → call service → respond
- Middleware applied to router groups, not individual routes
- Config loaded from environment via os.Getenv — no config files in repo
- NEVER log sensitive data (passwords, tokens, PII)
## File Structure
- cmd/server/ main.go entry point
- internal/ All application code (not exported)
- internal/api/ HTTP handlers and router
- internal/store/ sqlc-generated DB layer
- internal/service/ Business logic
- db/migrations/ SQL migration files
- db/queries/ SQL queries for sqlc
## Current Focus
- [Active module or feature]Template 6: Monorepo (Turborepo)
# Project: [Name] Monorepo
Turborepo monorepo. Web (Next.js), API (Express), shared packages.
## Workspace Structure
- apps/web Next.js 14 frontend
- apps/api Express REST API
- packages/ui Shared component library
- packages/types Shared TypeScript types
- packages/utils Shared utility functions
- packages/config Shared ESLint, TS, Tailwind configs
## Tech Stack
- Node 20 / TypeScript 5.4
- Turborepo for task orchestration
- pnpm workspaces (ALWAYS use pnpm — never npm or yarn in this repo)
- Changesets for versioning packages
## Commands
- Dev all: pnpm dev (turbo runs all dev tasks)
- Dev web only: pnpm --filter web dev
- Dev api only: pnpm --filter api dev
- Build all: pnpm build
- Test all: pnpm test
- Lint all: pnpm lint
- Add dep: pnpm --filter [app] add [package]
- Add shared: pnpm --filter [app] add @repo/ui
## Code Style
- Imports from shared packages: @repo/ui, @repo/types, @repo/utils
- NEVER copy-paste code between apps — create a shared package
- All shared types live in packages/types — never duplicate
- Component exports from packages/ui follow named export pattern
## Architecture Rules
- NEVER install the same dependency in two apps — extract to packages/
- Cross-package changes must run: pnpm build in affected packages first
- Internal packages use workspace: * versioning
- API types shared via packages/types — always keep in sync
- NEVER import from apps/ directory in packages/ (one-way dependency)
## Which Package to Touch
- UI change: packages/ui → then apps that use it
- New API endpoint: apps/api + packages/types (for response types)
- New page: apps/web only
- Shared logic: packages/utils
## Current Focus
- [Current cross-package work or migration]Template 7: WordPress / PHP
# Project: [Name] — WordPress
Custom WordPress theme + plugin. PHP 8.2. WooCommerce.
## Tech Stack
- WordPress 6.7 / PHP 8.2
- Custom theme: wp-content/themes/[theme-name]
- Custom plugin: wp-content/plugins/[plugin-name]
- WooCommerce 9.x
- WP-CLI for automation
- Composer for PHP dependencies
- Node + webpack for asset bundling (npm run build)
## Commands
- Dev assets: npm run dev (webpack watch)
- Build assets: npm run build
- WP CLI: wp [command] (from /var/www/html)
- Install plugin: wp plugin install [slug] --activate
- Export DB: wp db export backup.sql
- Cache flush: wp cache flush
- PHP lint: vendor/bin/phpcs --standard=WordPress .
## Code Style
- Follow WordPress Coding Standards (WPCS) — CI enforces this
- snake_case for all PHP functions and variables
- Prefix ALL functions, hooks, and classes: [prefix]_function_name
- Enqueue scripts/styles via wp_enqueue_scripts hook — never inline
- Use wp_nonce_field() for all forms — CSRF protection required
- Escape ALL output: esc_html(), esc_attr(), esc_url(), wp_kses_post()
- Sanitize ALL input: sanitize_text_field(), absint(), etc.
## Architecture Rules
- NEVER modify core WordPress files or WooCommerce files directly
- Extend WooCommerce via hooks/filters — never edit plugin files
- All custom DB tables use $wpdb with proper escaping
- Custom post types registered in [prefix]-post-types.php
- NEVER use short PHP tags: <?php only
- Meta values stored with update_post_meta — no custom tables for simple data
## File Structure (Theme)
- functions.php Autoloader + hook registration
- inc/ PHP includes (post types, taxonomies, helpers)
- template-parts/ Reusable template fragments
- assets/src/ Raw JS and SCSS
- assets/dist/ Built assets (gitignored)
## WP Hooks Reference
- Init logic: add_action('init', ...)
- Admin pages: add_action('admin_menu', ...)
- WC cart change: add_action('woocommerce_add_to_cart', ...)
## Current Focus
- [Active theme/plugin feature]5. CLAUDE.md vs Skills vs docs/ — Where Does It Belong?
One of the most common mistakes I see is treating CLAUDE.md as a catch-all documentation dump. It's not. Here's the decision framework I use.
| Content Type | Put it in… | Why |
|---|---|---|
| Project overview, tech stack | CLAUDE.md | Needed every session |
| Build / test / lint commands | CLAUDE.md | Used constantly |
| Code style rules | CLAUDE.md | Applies to every edit |
| API documentation | docs/API.md | Too long for CLAUDE.md; reference when needed |
| Deployment procedures | docs/DEPLOY.md or Skill | Only needed occasionally |
| Complex workflow (e.g. release) | Skill | On-demand, not every session |
| One-time setup instructions | docs/SETUP.md | Only needed once |
| Architecture deep-dive | docs/ARCHITECTURE.md | Reference, not every-session |
| Linting / formatting rules | Hooks | Deterministic 100% enforcement |
| Security rules (never commit secrets) | CLAUDE.md + Hook | Belt and suspenders |
6. CLAUDE.md Diagnostic — Fix What's Broken
If Claude isn't behaving the way your CLAUDE.md says it should, here's how to diagnose it. These are the exact symptoms I've hit — and the fixes that actually worked.
Symptom: Claude ignores your rules entirely
Cause: CLAUDE.md is too long (>200 lines)
Fix: Prune ruthlessly. Remove anything Claude knows by default. Move docs to docs/. Move workflows to Skills. Target 80–120 lines.
Symptom: Claude asks questions already answered in CLAUDE.md
Cause: Instructions are phrased as suggestions, not commands
Fix: Rewrite ambiguous lines as direct commands. "You should use named exports" → "Use named exports. Never use default exports."
Symptom: Claude uses wrong build/test commands
Cause: Commands section is missing or the commands are stale
Fix: Add a Commands section with exact, copy-paste commands. Update it every time your scripts change.
Symptom: Claude modifies files it shouldn't
Cause: No architecture constraints defined
Fix: Add NEVER rules in caps. "NEVER edit /prisma/migrations/ directly." The capitalization genuinely improves compliance.
Symptom: Claude's code style doesn't match yours
Cause: Style rules are too vague
Fix: Replace vague rules with concrete DO/DON'T examples in the code style section. Show the actual pattern you want.
Symptom: Sessions start noticeably slow to get productive
Cause: CLAUDE.md is loading too much context
Fix: Move heavy documentation to Skills (invoked on demand). Keep CLAUDE.md under 5,000 tokens.
Symptom: Claude gets worse (sloppier) mid-session
Cause: Context window bloat — your CLAUDE.md is competing with growing conversation
Fix: Run /compact periodically. Keep CLAUDE.md lean so it stays dominant. Put standing instructions in the highest-priority position (early in the file).
7. Before & After — Real Token Savings
I tracked my actual Claude Code usage across two projects before and after adding a proper CLAUDE.md. The results were more dramatic than I expected.
Example 1: React / Next.js Project
❌ Before (No CLAUDE.md)
- • 15 messages per session just to set up context
- • ~3,000 tokens per setup conversation
- • 15 sessions/week = ~45,000 wasted tokens/week
- • Claude used wrong test runner 40% of the time
- • Default exports on every component
✅ After (35-line CLAUDE.md)
- • 0 setup messages — productive from message 1
- • CLAUDE.md costs ~900 tokens per session
- • 15 sessions/week = 13,500 tokens on context
- • Correct commands every time
- • Named exports consistently applied
~70% reduction in context tokens. Sessions start productive immediately.
Example 2: Monorepo Project
❌ Before (400-line CLAUDE.md)
- • Full API docs embedded in CLAUDE.md
- • ~12,000 tokens per session just for the file
- • Claude followed only ~40% of the rules
- • Architecture rules ignored half the time
- • Slow session starts, frequent /compact needed
✅ After (80-line CLAUDE.md + Skills)
- • Core rules in CLAUDE.md (~2,400 tokens)
- • API docs moved to docs/API.md (Skill-loaded)
- • Release workflow moved to a Skill
- • Rule compliance improved to ~85%
- • Sessions faster, less /compact needed
80% fewer context tokens AND better rule compliance.
8. Advanced: Dynamic CLAUDE.md with /init
If you're setting up CLAUDE.md for the first time, don't start from scratch. Run /init inside Claude Code at your project root. Claude will scan your project and automatically detect your build system, test framework, code patterns, key dependencies, and file structure. The result is a generated starter CLAUDE.md that takes about 30 seconds instead of 30 minutes.
What /init detects: package manager (npm/pnpm/yarn), test runner (Jest/Vitest/pytest), framework version, CI configuration, linting setup, and common code patterns across your files. It's not perfect — it won't know your architecture constraints or your current focus — but it gives you 60% of a solid CLAUDE.md instantly.
After running /init: treat the output as a first draft. Read every line. Prune what Claude already knows. Add your architecture rules, NEVER constraints, and current focus. The generated file tends to be verbose — your job is to cut it down to what genuinely changes Claude's behavior.
For ongoing maintenance, I've built a simple habit: every two weeks, I paste my current CLAUDE.md into a session and ask: "Review my CLAUDE.md. Flag anything outdated, ambiguous, or redundant. Suggest what to remove and what's missing." Claude is genuinely good at this — it spots rules it's been silently ignoring and conventions that have drifted.
/init workflow
# 1. Generate starter
/init
# 2. Review what was generated
# Claude opens the file for you to inspect
# 3. Prune aggressively
# Remove anything Claude knows by default
# Remove anything you haven't needed to correct
# 4. Add your specifics
# Architecture constraints, NEVER rules, current focus
# 5. Periodic review (every 2 weeks)
# "Review my CLAUDE.md and suggest improvements"
# Ask Claude to flag: outdated commands, ambiguous rules,
# things it's been correcting without being asked9. Common Mistakes That Make Claude Ignore Your Rules
I've made all of these. They're not obvious until you understand how Claude actually processes the file — which is why most guides don't cover them.
❌ Don't
Writing a comprehensive project manual
✅ Do
Document only what Claude gets wrong without explicit instruction
If Claude already handles it correctly, the rule wastes a precious instruction slot. Every line should earn its place.
❌ Don't
"Never use --foo-bar flag" (negative-only)
✅ Do
"Never use --foo-bar; use --baz instead" (give the alternative)
Negative-only rules leave Claude guessing. Always pair a prohibition with the correct alternative — it's more efficient and more effective.
❌ Don't
Auto-generating with /init and never editing
✅ Do
Treat CLAUDE.md like code: review, prune, test, iterate
The generated file is a starting point. Left unedited, it's usually 2–3x longer than it needs to be and includes rules Claude already follows.
❌ Don't
Duplicating your linter or formatter rules in CLAUDE.md
✅ Do
Use hooks for formatting enforcement, CLAUDE.md for judgment calls
If ESLint catches it, CLAUDE.md doesn't need to say it. Reserve CLAUDE.md for things that require Claude's judgment, not mechanical rules.
❌ Don't
One massive CLAUDE.md for the entire monorepo
✅ Do
Per-package CLAUDE.md files that cascade from the root
A 500-line CLAUDE.md for a 10-package monorepo is almost useless. Scope rules to the packages where they apply.
❌ Don't
Writing instructions Claude already follows by default
✅ Do
Add a rule only after Claude has gotten something wrong at least twice
This is the most common bloat source. Claude is already trained on best practices for most stacks — you don't need to re-teach them.
❌ Don't
Adding more emphasis when Claude ignores a rule
✅ Do
Prune the file — the rule is being crowded out, not overlooked
The instinct is to bold the text or add IMPORTANT: prefix. The real fix is removing 20 other rules so this one has room to land.
⚡ TL;DR — CLAUDE.md Cheatsheet
Start with /init — then prune ruthlessly to under 200 lines / 5,000 tokens
7 sections: overview · stack · commands · style · constraints · structure · TODOs
Move API docs to docs/ — move workflows to Skills — move enforcement to Hooks
NEVER rules in caps improve compliance — always pair with the correct alternative
A lean 80-line CLAUDE.md outperforms a bloated 400-line one, every time
Review every 2 weeks: "Claude, review my CLAUDE.md and suggest improvements"
If Claude ignores a rule — cut the file, don't add emphasis
Commit project CLAUDE.md to Git — your team deserves consistent Claude behavior