Theory is useful, but seeing complete examples is better. Here are real-world CLAUDE.md patterns for different types of projects, followed by common mistakes to avoid.
Pattern 1: React + TypeScript project
# CLAUDE.md
## Stack
React 18 + TypeScript 5.4, Vite for bundling, Vitest for testing.
## Commands
- npm run dev - start on port 5173
- npm test - run Vitest (watch mode)
- npm run test:ci - run Vitest once (CI mode)
- npm run lint - ESLint + Prettier
## Conventions
- Functional components only, no class components
- State: Zustand for global, useState for local, no Redux
- Styling: Tailwind utility classes, no CSS modules or styled-components
- Named exports only, never export default
- File structure: one component per file, colocate tests as ComponentName.test.tsx
## Types
- All component props must have an explicit interface (not inline)
- Never use `any` - use `unknown` and narrow with type guards
- Shared types live in src/types/, component-specific types stay in the component file
## Testing
- Every component needs at least one test
- Use React Testing Library, not Enzyme
- Test behavior, not implementation (query by role/text, not by class/id)Notice what this does well: it is 25 lines long, every line is actionable, and it covers the decisions that Claude would otherwise guess wrong.
Pattern 2: Python FastAPI project
# CLAUDE.md
## Stack
Python 3.12, FastAPI, SQLAlchemy 2.0 with async, PostgreSQL, Alembic for migrations.
## Commands
- make dev - start uvicorn with hot reload on port 8000
- make test - run pytest with coverage
- make lint - ruff check + mypy
- make migrate - run alembic upgrade head
- make migration msg="description" - create new migration
## Conventions
- All endpoints in app/routers/, one file per resource (users.py, orders.py)
- Request/response models use Pydantic v2 with model_config
- Dependencies injected via FastAPI Depends(), defined in app/deps.py
- All database operations are async (use async session, await queries)
- Error responses: raise HTTPException with detail as dict { "code": str, "message": str }
## Structure
- app/main.py - FastAPI app instance and middleware setup
- app/routers/ - route handlers, one per resource
- app/models/ - SQLAlchemy ORM models
- app/schemas/ - Pydantic request/response models
- app/deps.py - dependency injection functions
- app/core/config.py - settings via pydantic-settings
## Rules
- Never use raw SQL, always go through SQLAlchemy
- All routes must have type hints for request and response
- Use Python 3.12 features (type alias, match/case) when appropriatePattern 3: MonorepoWhat is monorepo?A single repository that contains multiple related projects or packages, managed together with shared tooling. with multiple packages
In a monorepo, use the CLAUDE.md hierarchy to layer instructions.
Root CLAUDE.md (shared conventions):
# CLAUDE.md
## Monorepo structure
- packages/api - Express.js backend
- packages/web - Next.js frontend
- packages/shared - shared TypeScript types and utilities
## Global rules
- TypeScript strict mode everywhere
- Shared types must be imported from @company/shared, never duplicated
- Use pnpm, not npm or yarn
- Run pnpm install from root only
## Commands
- pnpm dev - start all packages concurrently
- pnpm test - run tests across all packages
- pnpm lint - lint all packagespackages/apiWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses./CLAUDE.md (API-specific):
# API conventions
- Express routes follow RESTful naming: GET /users, POST /users, GET /users/:id
- All middleware in src/middleware/, never inline in route files
- Database: Prisma ORM, schema in prisma/schema.prisma
- Auth: JWT verification middleware applied per-route, not globallypackages/web/CLAUDE.md (frontend-specific):
# Web conventions
- Next.js App Router only, no Pages Router
- Server components by default, add "use client" only when needed
- Styling: CSS Modules with Tailwind @apply for reusable classes
- Data fetching: server components use direct DB queries, client uses SWRThis way, when Claude works in packages/api/, it gets both the global rules and the API-specific rules. No duplication needed.
packages/web when it should come from packages/shared. The "shared types must be imported from @company/shared" rule in the example above prevents this.| Pattern | CLAUDE.md size | Key sections | Focus |
|---|---|---|---|
| React + TS | ~25 lines | Stack, Conventions, Types, Testing | Component patterns, state management |
| Python API | ~25 lines | Stack, Commands, Conventions, Structure | Endpoint patterns, DB access |
| Monorepo | Root: ~15 lines, per-package: ~10 lines | Global rules + package-specific rules | Cross-package imports, shared conventions |
Common mistakes
Mistake 1: Making it too long
A 500-line CLAUDE.md is counterproductive. Every line consumes tokens from Claude's context windowWhat is context window?The maximum amount of text an AI model can consider at once, including your conversation history and any files it has read., tokens that could be used for your actual conversation. Aim for under 100 lines. If you need more detail, put it in folder-level CLAUDE.md files.
Mistake 2: Being too vague
# BAD
- Follow best practices
- Write clean code
- Use proper error handling
# GOOD
- All async functions must have try/catch with error logged to Sentry
- Functions over 30 lines should be split into smaller functions
- API errors return { error: string, code: string, status: number }Mistake 3: Duplicating existing docs
If you have a CONTRIBUTING.md that explains your git workflow, do not copy it into CLAUDE.md. Instead, reference it:
## Git workflow
Follow the conventions in CONTRIBUTING.md. Key points:
- Branch naming: feature/JIRA-123-short-description
- Commit messages: conventional commits (feat:, fix:, chore:)Mistake 4: Including content that never changes
# BAD - Claude knows this already
TypeScript is a typed superset of JavaScript. Use `interface` to define object shapes.
# GOOD - Claude doesn't know your specific choices
We prefer `type` over `interface` for non-extensible shapes. Use `interface` only
for objects that will be extended with declaration merging.The iterative approach
The best CLAUDE.md files are not written in one sitting. They grow through real usage:
Week 1: Claude uses any in TypeScript code.
Add: Never use any. Use unknown with type guards, or define explicit types.
Week 2: Claude puts test files in a separate __tests__ directory.
Add: Colocate test files next to source: Button.tsx and Button.test.tsx in the same directory.
Week 3: Claude uses console.log for debugging.
Add: Use the logger utility (src/utils/logger.ts), never console.log.
Each rule comes from a real problem. After a few weeks, Claude rarely makes project-specific mistakes because the most common issues are all covered.