Most CLAUDE.md files fail for the same reason: they describe the project in general terms and hope Claude figures out the details. Claude does not figure out the details. It follows explicit instructions.
This guide extracts 12 concrete patterns from analyzing over 100 real CLAUDE.md files in production repositories. These are the patterns that produce measurably better Claude behavior — more consistent code style, fewer rejected changes, less context re-explanation across sessions.
The repos analyzed include prominent open-source projects (Next.js, Deno, LangChain, Excalidraw, Linear), plus dozens of private codebases shared by developers in the Claude Code community.
Why Most CLAUDE.md Files Underperform
Before the patterns: understanding what goes wrong.
Problem 1: Project description, not instructions. The most common CLAUDE.md failure is writing a README for Claude instead of a rulebook. “This is a Next.js app using TypeScript and PostgreSQL” tells Claude what the project is, not how to work in it. Claude already infers the stack from your files.
Problem 2: Rules without consequences. “Follow the existing code style” is not a rule Claude can act on. It requires Claude to infer the style from samples, which works inconsistently. The effective equivalent: “Use single quotes for strings, no semicolons, 2-space indentation. Match the patterns in existing files exactly.”
Problem 3: Too long to be useful. Some CLAUDE.md files exceed 2000 lines. At that length, the most important rules get buried and diluted. Claude’s attention degrades for low-priority items at the bottom of a long document. Ruthless editing beats comprehensiveness.
Problem 4: Static files about dynamic codebases. CLAUDE.md is maintained manually. When the test framework changes from Jest to Vitest and nobody updates CLAUDE.md, Claude will keep generating npx jest commands. Without a process for keeping CLAUDE.md current, it becomes a liability.
Problem 5: Missing the negative space. Rules about what to do are necessary. Rules about what not to do are often more impactful. The best CLAUDE.md files dedicate real space to anti-patterns — specific things Claude tends to do wrong in this particular codebase.
Pattern 1: Lead with Commands, Not Context
The most universally practiced pattern in top repositories is front-loading commands. Build, test, and lint commands belong near the top, before any prose explanation.
# Project
## Commands
- Install: `pnpm install`
- Dev server: `pnpm dev`
- Build: `pnpm build`
- Test: `pnpm test`
- Test single file: `pnpm test path/to/file.test.ts`
- Lint: `pnpm lint`
- Type check: `pnpm typecheck`
- Format: `pnpm format`
Why it works: Claude Code runs commands constantly — to verify changes, run tests, check types. If commands are buried 300 lines in, Claude either guesses (and gets it wrong) or uses expensive tool calls to explore package.json. Front-loading reduces errors and token waste.
Anti-pattern: Putting commands in a “Technical Details” section at the end. Deno’s CLAUDE.md, one of the most effective in open source, opens with the build and test commands before any other content.
Pattern 2: Define the Boundaries Explicitly
Every effective CLAUDE.md we analyzed had a section dedicated to what Claude should not touch. Not vague warnings, but specific directories, files, and operations.
## Off-Limits
### Directories (never modify)
- `generated/` — auto-generated by protobuf compilation
- `dist/` — build output
- `vendor/` — vendored Go dependencies
- `.github/workflows/` — CI pipeline, requires human review
### Operations (never perform without explicit instruction)
- Run database migrations
- Modify `package.json` dependency versions
- Change TypeScript configuration
- Add new npm scripts to `package.json`
### Files (never delete)
- `*.lock` files — always commit changes to lockfiles
- `.env.example` — template for environment setup
The specificity is what makes this work. “Don’t touch generated files” requires Claude to infer which files are generated. “Don’t touch generated/” is unambiguous.
Several repos (including Excalidraw) add a brief reason for each boundary. “auto-generated by protobuf compilation” tells Claude why the rule exists, which improves adherence when edge cases arise.
Pattern 3: Anti-Pattern Blocks for Common Failure Modes
Every codebase has specific patterns where AI coding assistants reliably go wrong. Top CLAUDE.md files name these explicitly.
From a TypeScript project:
## Anti-Patterns (Never Do This)
- No `any` types — use `unknown` and narrow with type guards
- No `as` type assertions — narrow instead of asserting
- No `// @ts-ignore` — fix the underlying type issue
- No `console.log` in production code — use the Logger service
- No inline event handlers in JSX — extract to named functions
- No direct `localStorage` access — use the storage service abstraction
- No hardcoded strings for UI text — use the i18n translation system
From a Go project:
## Anti-Patterns
- No `panic` outside of truly unrecoverable states (startup config errors)
- No `init()` functions — explicit initialization in main
- No global variables — use dependency injection
- Error messages: lowercase, no trailing punctuation
- Context propagation: always pass ctx as first argument, never store in struct
This pattern is absent from most average CLAUDE.md files but present in almost every high-performing one. The specificity of “no init() functions” beats “follow Go idioms” by a large margin in practice.
Pattern 4: The Testing Contract
How tests should be written, run, and verified is one of the highest-leverage things to specify in CLAUDE.md. AI-generated tests are often superficial or test implementation details rather than behavior. Explicit testing contracts address this.
## Testing
### Framework
Vitest with React Testing Library for component tests, MSW for API mocking.
### What to test
- Public API behavior (inputs → outputs), not implementation internals
- Edge cases that would cause user-visible failures
- Error paths and boundary conditions
- Integration tests for database operations (use test transactions that roll back)
### What not to test
- Private functions — test them through the public interface
- Implementation details that might change during refactoring
- Third-party library internals
### Conventions
- Test files live alongside source: `auth.ts` → `auth.test.ts`
- Use `describe` blocks to group related tests
- Test names: "should [expected behavior] when [condition]"
- Never use `test.only` or `describe.only` in committed code
### Running tests
- Full suite: `pnpm test`
- Watch mode: `pnpm test --watch`
- Single file: `pnpm test src/auth/auth.test.ts`
- Coverage: `pnpm test --coverage`
The LangChain CLAUDE.md is particularly thorough on testing contracts — it specifies not just how to run tests but what constitutes a complete test, how to structure integration tests, and what the CI pipeline expects.
Pattern 5: Code Style as Executable Rules
Vague style guidance (“follow the existing code style”) is nearly useless. Effective CLAUDE.md files encode style as explicit rules.
## Code Style
### Formatting (enforced by Prettier, do not override)
- Single quotes for strings
- No semicolons
- 2-space indentation
- 100 character line length
- Trailing commas in multi-line structures
### Naming
- Files: kebab-case (`user-service.ts`, not `UserService.ts` or `userService.ts`)
- React components: PascalCase in PascalCase files
- Hooks: camelCase prefixed with `use` (`useAuthState`)
- Types/interfaces: PascalCase, no `I` prefix (`User`, not `IUser`)
- Constants: SCREAMING_SNAKE_CASE only for module-level primitives
### Imports
- Group: external packages → internal packages → relative imports
- No default exports from utility files — use named exports
- Avoid barrel re-exports in performance-sensitive paths
### Functions
- Prefer named function declarations over arrow functions for top-level functions
- Keep functions under 50 lines — extract helpers if longer
- One thing per function
Repos that enforce formatting with Prettier or Black can shorten this section significantly — just specify the config file. “Run prettier --write before committing” makes Claude internalize the constraint.
Pattern 6: Architecture as Context
High-quality CLAUDE.md files include enough architectural context that Claude understands why rules exist, not just what they are.
## Architecture
This is a hexagonal architecture (ports and adapters). Key implication:
- Domain layer (`src/domain/`) must not import from infra layer (`src/infra/`)
- Use cases in `src/application/` orchestrate domain objects
- Infra adapters implement interfaces defined in domain
### Data flow
Request → Router → Controller → Use Case → Domain Service → Repository Interface
↑ HTTP adapter ↑ orchestration ↑ domain logic ↑ infra adapter
### State management
- Server state: React Query (never use useEffect for data fetching)
- Client UI state: Zustand stores, one per feature
- Form state: React Hook Form (never manage form state manually)
### Authentication
- JWT tokens, 15min expiry for access, 7 days for refresh
- Auth context lives in `src/features/auth/`
- Use `useAuth()` hook — never read tokens from localStorage directly
This level of context prevents a specific failure mode: Claude making changes that are locally correct but architecturally wrong. Without the domain/infra boundary explanation, Claude might import an infra adapter directly into domain code.
Pattern 7: The PR Checklist Pattern
Several of the highest-performing CLAUDE.md files include explicit criteria Claude should verify before considering a task complete.
## Before Marking a Task Done
- [ ] Tests pass: `pnpm test`
- [ ] No TypeScript errors: `pnpm typecheck`
- [ ] No lint errors: `pnpm lint`
- [ ] No new `any` types introduced
- [ ] All new public functions have JSDoc comments
- [ ] Error paths are handled (no unhandled promise rejections)
- [ ] No hardcoded values that belong in config or constants
This is qualitatively different from just listing commands. It establishes that Claude is expected to self-verify before handing work back, and it makes the definition of “done” explicit. Repos that use this pattern report fewer round-trips on code review.
Pattern 8: Dependency Policy
AI coding assistants frequently add dependencies without thinking about it. A one-liner in CLAUDE.md prevents most of these:
## Dependencies
Do not add new npm packages without explicitly being asked. When a task requires
new functionality, prefer:
1. Using existing packages already in package.json
2. Implementing it with standard library / Web APIs
3. Asking whether to add a dependency before doing so
Banned packages: moment.js (use date-fns), lodash (use native methods), request (use fetch)
The “banned packages” section is particularly valuable for codebases migrating away from legacy dependencies. Without it, Claude will keep reaching for lodash because it is trained on millions of repos that use it.
Pattern 9: Environment and Configuration
## Environment
### Local setup
- Copy `.env.example` to `.env.local` for local development
- Database: PostgreSQL 16, connection string in `DATABASE_URL`
- Redis: required for session storage, `REDIS_URL` in env
### Environment variables (never hardcode)
- Never commit `.env` files
- Never hardcode credentials, tokens, or connection strings
- Use `process.env.VARIABLE_NAME` through the config service in `src/config/`
### Secrets in tests
- Use `TEST_DATABASE_URL` for integration tests
- Mock external services in unit tests with MSW
This section prevents the most dangerous AI coding mistake: hardcoded credentials or environment-specific values committed to version control.
Pattern 10: Version Pinning and Deprecation Notes
## Tech Stack (pinned versions)
- Node.js: 22.x (LTS) — do not use v23+ features
- TypeScript: 5.7 — see tsconfig.json for enabled features
- React: 19.x — use new features (Server Components, use() hook) where appropriate
## Deprecated Patterns (do not use)
- `getServerSideProps` — use React Server Components
- `useEffect` for data fetching — use React Query
- Class components — functional components only
- Moment.js — use date-fns
- `var` — use `const` or `let`
Version pinning prevents Claude from using features available in newer versions that your codebase does not support. The deprecation list is particularly valuable for Next.js projects where the App Router and Pages Router coexist — without explicit guidance, Claude will often mix patterns from both.
Pattern 11: Monorepo-Specific Scoping
For monorepos, per-package CLAUDE.md files with imports from the root are the most effective pattern we found.
Root CLAUDE.md:
# Monorepo
pnpm workspaces with 4 packages.
## Workspace Commands
- Install all: `pnpm install`
- Build all: `pnpm -r build`
- Test all: `pnpm -r test`
- Run in specific package: `pnpm --filter @org/package-name <command>`
## Cross-Package Rules
- Shared types live in `@org/types` — do not duplicate type definitions
- Do not create circular dependencies between packages
- Internal packages use `workspace:*` version specifier
## Package-Specific Rules
@packages/web/CLAUDE.md
@packages/api/CLAUDE.md
@packages/shared/CLAUDE.md
Each package CLAUDE.md then contains only package-specific rules. The import system prevents drift: update packages/api/CLAUDE.md and all agents get the updated API rules automatically.
Pattern 12: The “Minimal Change” Principle
One of the most impactful single lines found across many effective CLAUDE.md files:
## Working Style
Prefer minimal, targeted changes. Do not refactor code outside the scope of the
current task. If you see code that could be improved, mention it but do not change it
unless asked.
This prevents a common frustration: asking Claude to fix one bug and getting back a diff that touches 15 files. The minimal change principle keeps diffs reviewable and regressions limited.
Variations seen across repos:
- “Change only what the task requires”
- “One task, one diff”
- “If you want to refactor something, ask first”
The CLAUDE.md Maintenance Checklist
A CLAUDE.md is only as good as how current it is. The highest-performing repos have an implicit process (sometimes explicit) for keeping it current:
Update triggers:
- New tool added to the stack → add to commands section
- Dependency deprecated or removed → add to deprecated list
- New directory created → decide if it needs a boundary rule
- New architectural pattern adopted → document it
- Claude makes the same wrong assumption twice → add an explicit rule
Review cadence:
- Run through the CLAUDE.md whenever onboarding a new developer — they will read it and notice what is missing
- Check that all commands still work after dependency upgrades
- Review the off-limits section when the directory structure changes
Putting It Together: A Complete Example
Here is a CLAUDE.md that incorporates all 12 patterns, scaled to a realistic size (neither 50 lines that say nothing nor 2000 lines nobody reads):
# Project
SaaS analytics dashboard. React 19 + TypeScript, Go API, PostgreSQL.
## Commands
- Install: `pnpm install` (root), `go mod download` (api/)
- Frontend dev: `pnpm --filter web dev`
- API dev: `cd api && go run ./cmd/server`
- Test frontend: `pnpm --filter web test`
- Test API: `cd api && go test ./... -race`
- Lint all: `pnpm lint && cd api && golangci-lint run`
- Type check: `pnpm --filter web typecheck`
## Architecture
Frontend in `apps/web/`, Go API in `api/`, shared DB schemas in `db/`.
Frontend talks to API only through the generated OpenAPI client in `apps/web/src/api/`.
Never call API endpoints directly — always use the generated client.
## Off-Limits
- `api/generated/` — OpenAPI client, regenerated by `make generate`
- `db/migrations/` — use `make migration name=description` to create new ones
- `apps/web/src/api/` — generated OpenAPI client
## Anti-Patterns (Never)
- Frontend: no `any`, no `useEffect` for data fetching (use React Query), no direct fetch calls
- API: no `panic` in handlers, no global variables, context as first argument always
- Both: no hardcoded values (use `config/` package in API, `constants/` in frontend)
## Testing
- Frontend: Vitest + React Testing Library. Test behavior, not implementation.
- API: standard Go testing. Table-driven tests. Subtests for related cases.
- Before considering any task done: run tests + typecheck + lint. All must pass.
## Dependencies
- Do not add packages without asking. Prefer existing packages first.
- Banned frontend: moment.js, lodash, axios (use native fetch via generated client)
## Before Marking Done
- [ ] Tests pass
- [ ] No type errors
- [ ] No lint errors
- [ ] Minimal diff — no unrequested refactors
This is 55 lines. It is specific, complete for its scope, and maintainable. Compare this to a 500-line CLAUDE.md that explains the company history and team structure — the 55-line version will produce better behavior every time.
Related Resources
For copy-paste starting points for different project types, see 10 CLAUDE.md templates by use case. For how CLAUDE.md compares to AGENTS.md and CONVENTIONS.md, see the three-format comparison. For Python-specific patterns, the CLAUDE.md for Python guide covers modern Python toolchain specifics.