← All guides

How to Write Effective CLAUDE.md Files (With 12 Real Examples)

What actually belongs in a CLAUDE.md file, what doesn't, and 12 real-world examples annotated with what each pattern is doing and why.

How to Write Effective CLAUDE.md Files (With 12 Real Examples)

A CLAUDE.md is the single document Claude Code reads first in every session. Get it right and the agent stops asking questions you already have answers to. Get it wrong and you pay tokens every turn for no behavioral change. This post is the working rubric I use, with twelve examples from real repositories.

TL;DR — What a good CLAUDE.md does

  1. Answers five boring questions before the agent asks: stack, where code lives, how to run tests, how to deploy, what not to touch.
  2. Declares the invariants that make this codebase different from the average of its stack.
  3. Stays under ~200 lines. Every line you add is tokens on every request in this project.
  4. Avoids aspiration. "We should someday…" is comment rot. If it's not true right now, it does not belong.
  5. Points at authoritative files, not restates them. A one-line pointer to docs/architecture.md beats a ten-line summary that drifts.

The five questions every CLAUDE.md must answer

I have audited over 60 CLAUDE.md files in open source and internal repos. The ones that produced measurable productivity gains all answered these five. The ones that did not were indistinguishable from no file at all.

1. What is this project, in one paragraph?

Not a marketing paragraph. A technical paragraph an engineer would write on the inside cover of a physical notebook.

## What this is
Payment reconciliation service. Reads nightly bank statements from S3,
matches against Stripe events, emits a reconciliation report and a queue
of exceptions. Runs once per day at 04:00 UTC. No user-facing UI.

The agent now knows it is not allowed to suggest adding a frontend, and that anything touching the schedule is high-stakes.

2. What is the stack?

Exact versions. Not "we use Node." Not "recent TypeScript."

## Stack
- Node 22.11 LTS, TypeScript 5.7 strict
- Postgres 16 via Drizzle ORM
- Fastify 5.2
- Vitest 2.1 for tests
- Deployed on Fly.io (3 regions: iad, fra, nrt)

Versions matter because Claude's training cutoff may be earlier than your actual dependencies, and idiomatic code differs meaningfully across major versions.

3. Where does code live, and why?

The file tree alone is not enough. You want a one-liner per important directory that tells Claude what belongs there.

## Layout
- src/routes/ — HTTP handlers; thin, 1 file per resource
- src/services/ — business logic; no HTTP, no SQL
- src/repos/ — SQL only; no business logic
- src/jobs/ — scheduled tasks; independent of HTTP
- tests/ — mirrors src/; integration tests only, unit tests colocated

Now when you ask for "a new endpoint that charges a customer," the agent places the HTTP wrapper in routes, the business logic in services, and the SQL in repos — without being told per-file.

4. How do I run things?

Two command lists: the ones you use every day, and the ones that run in CI. The agent will pick the right level for the task.

## Commands
Dev:
  pnpm dev              # Fastify on :3000
  pnpm test             # Vitest watch mode
  pnpm test:unit        # unit only
  pnpm db:push          # Drizzle migration to dev DB

CI:
  pnpm build
  pnpm test:ci          # single-run, JUnit output
  pnpm lint
  pnpm typecheck

5. What is off-limits?

This is the section most people skip. It is the highest-leverage section in the file.

## Don't
- Do not run `pnpm db:push` against anything other than `localhost:5432`.
- Do not add new npm dependencies without proposing first; the team has a 
  boring-tech policy and a dependency budget.
- Do not commit migrations that drop or rename columns; we use 
  expand-then-contract migrations only.
- Do not touch `src/billing/` without a design doc in `docs/rfcs/`.

Every one of these rules was learned the hard way. Writing them into CLAUDE.md prevents the agent from independently rediscovering them.

What does NOT belong in CLAUDE.md

These are all tempting and all wrong.

Twelve real examples, annotated

Example 1 — A small Next.js SaaS

# CLAUDE.md — 03-saas-cost-tracker

## What
Claude API cost tracker for solo builders. Reads Anthropic Admin API usage,
displays dashboards, sets budget alerts. Next.js 15 App Router, Drizzle + Neon,
Polar for billing.

## Run
- `pnpm dev` (Next.js on :3000)
- `pnpm db:studio` (Drizzle Studio)
- `pnpm test` (Vitest)

## Deploy
Vercel (main branch auto-deploys). Preview deploys per PR.

## Invariants
- `app/` is client-ish; `lib/` is server-only. Never import `lib/db` from `app/`.
- Anthropic pricing table lives in `lib/pricing/claude.ts` — one canonical source.
- `.env.local` for dev, Vercel env for prod.

## Don't
- Do not write to DB in client components.
- Do not add routes under `/admin/` without checking `lib/auth/adminGuard.ts`.

Annotation: twenty-five lines, answers all five questions, enforces the "don't import lib/db from app/" invariant that previously caused one production deploy failure per month.

Example 2 — A Python CLI tool

# CLAUDE.md — ingest

## What
Ingests legacy XML exports into the current schema. One-off migration tool; 
runs maybe 10 times total in the project's lifetime.

## Stack
Python 3.12, click, pydantic 2, uv-managed env.

## Run
`uv run python -m ingest <file>` (dry-run by default).
Add `--write` to commit. Anything other than `--write` is read-only.

## Don't
- No concurrency. Sequential only. Ingestion order matters.
- Do not generalize this tool. It is deliberately throwaway.

Annotation: the "do not generalize" line is critical. Without it, Claude will helpfully refactor the tool into a plugin architecture that none of the five remaining invocations needs.

Example 3 — A Go microservice

# CLAUDE.md — auth-service

## What
Token issuance and validation for the internal platform. Issues JWTs signed
with ES256, validates on each request. No user-facing endpoints.

## Stack
Go 1.23, chi router, jwx/v3 for tokens, sqlc for DB.

## Layout
- cmd/auth-service/ — main.go
- internal/token/ — pure crypto, zero deps on net/http
- internal/http/ — handlers, middleware
- internal/storage/ — sqlc-generated + queries

## Run
`go test ./...` (fast, no DB)
`go test -tags=integration ./...` (needs Postgres)

## Don't
- Do not add a sessions table. We are stateless by design.
- Do not import `internal/storage/` from `internal/token/`. The crypto layer
  must remain testable without a DB.
- Do not change the JWT claim names; downstream services pin them.

Annotation: the last "don't" is worth the entire file. Renaming a JWT claim is a 10-minute change that breaks every other service. The file prevents it.

Example 4 — A monorepo with Turborepo

# CLAUDE.md — root

## What
Turborepo housing the web app (`apps/web`), the API (`apps/api`), and two
shared packages (`packages/ui`, `packages/db`).

## Per-workspace guidance
Each package/app has its own CLAUDE.md. Read it first before editing inside.

## Root-level rules
- Dependencies go in the workspace that owns them, not the root.
- Shared types go in `packages/db` (not `packages/ui`).
- Do not introduce a new workspace without updating `turbo.json` pipelines.

## Commands (at root)
- `pnpm dev` (everything in parallel via turbo)
- `pnpm test` (all workspaces)
- `pnpm -C apps/web dev` (only web)

Annotation: the "read the workspace CLAUDE.md first" line keeps the root file short and distributes detail appropriately.

Example 5 — A Rust library

# CLAUDE.md — url-normalize

## What
Pure-function library: takes a URL string, returns a canonicalized URL string.
No allocation in the hot path.

## Stack
Rust 1.83, no_std compatible (don't break this).

## Run
`cargo test` — ~40 tests, all pure.
`cargo bench` — runs criterion; do not regress p50 > 5% without discussion.

## Invariants
- No heap allocation inside `normalize()`.
- No dependencies beyond `core` + `alloc`. This is enforced in Cargo.toml.
- MSRV 1.75. Do not use features added later.

Annotation: the MSRV line prevents Claude from helpfully using let-else where it would break users.

Example 6 — A Python ML training repo

# CLAUDE.md — vision-classifier

## What
Fine-tunes small vision classifiers on custom datasets.

## Stack
Python 3.11, PyTorch 2.4, HuggingFace transformers 4.45, wandb for tracking.

## Layout
- configs/ — one YAML per experiment (required for reproducibility)
- src/data/ — dataset loaders, augmentation
- src/train/ — training loops (one per task type)
- src/eval/ — evaluation only; never import from train/
- notebooks/ — exploratory, not production

## Don't
- Do not change training loops without bumping the config version.
- Do not hardcode paths; use `os.getenv("DATA_ROOT")`.
- Do not run training from notebooks. Notebooks are for post-hoc analysis.

Example 7 — A simple static site (Astro)

# CLAUDE.md — blog

## What
Personal blog. Astro 4.x + MDX. Published to Cloudflare Pages.

## Content rules
- New posts go in `src/content/blog/YYYY-MM-DD-slug.mdx`.
- Frontmatter fields: title, description, published, tags. Validated by Zod.
- Images go in `public/img/` and are served unoptimized.

## Don't
- Do not add runtime JS unless a component actually needs it. Prefer
  Astro-only components for layout.
- Do not install React unless you are specifically adding an interactive
  component (none exist today).

Example 8 — A Kubernetes operator

# CLAUDE.md — kafka-operator

## What
Custom operator that reconciles `KafkaTopic` CRDs against an EKS-hosted cluster.

## Stack
Go 1.23, operator-sdk 1.38, controller-runtime 0.19.

## Layout standard: operator-sdk defaults. Do not deviate.

## Commands
- `make generate manifests` (after CRD changes)
- `make test` (envtest, needs `setup-envtest`)
- `make run` (runs controller locally against current kubeconfig)

## Don't
- Do not reconcile synchronously from webhook. Always enqueue.
- Do not touch `api/v1alpha1/`. Shipped; frozen. Evolve in `v1alpha2`.

Annotation: the "frozen" rule prevents accidental breaking changes to a shipped CRD version — a class of bug that causes operator rollouts to fail silently.

Example 9 — A PHP Laravel app

# CLAUDE.md — billing-dashboard

## Stack
PHP 8.3, Laravel 11, MySQL 8, Redis 7 (queues + cache).

## Layout
Standard Laravel. Controllers thin; logic in app/Services/.

## Testing
`php artisan test` — uses SQLite in-memory.
Integration tests (`Tests\Feature`) hit a real MySQL under Docker Compose.

## Don't
- Do not add Eloquent events (observers / model events). We had a nasty
  production bug; business logic is explicit via Services.
- Do not use Blade in new files; we are migrating to Livewire for dynamic
  pages and to pure HTML for static.

Example 10 — A CLI tool in Deno

# CLAUDE.md — devtools

## What
Personal CLI: repo bootstrapping, dependency auditing, release scripting.

## Stack
Deno 2.x, TypeScript. Zero npm dependencies; use `jsr:@std/*` only.

## Layout
- cli.ts — single entry point
- cmd/ — one file per subcommand
- lib/ — shared helpers

## Don't
- Do not add npm-compatibility imports. The whole point of this tool is
  that it runs with a single binary.
- Do not add network calls without explicit `--allow-net` in the permission
  flags documented in each command's help text.

Example 11 — A machine learning inference service

# CLAUDE.md — inference-service

## What
gRPC inference service. Serves the latest trained vision model (see 
`vision-classifier` repo).

## Stack
Python 3.12, Triton Inference Server 24.08 sidecar, FastAPI for health/admin.

## Deploy
Image built nightly; canaried to 1% → 50% → 100% over 6 hours.

## Don't
- Do not ship changes that alter the model signature without bumping the
  API version. Every client pins a version.
- Do not increase the batch size past 16 without re-running latency tests.
  We are SLA-bound to p99 < 80ms.

Example 12 — A tiny utility library, 200 lines of code

# CLAUDE.md — tiny-util

## What
Micro utility for [one specific thing]. ~200 lines total; stays small.

## Stack
TypeScript, zero runtime deps.

## Don't
- Do not add features. If a user asks for one, write it in their project,
  not here. This library is done.

Annotation: the shortest useful CLAUDE.md I have. Eight lines. Saves me from Claude "helpfully" adding features I have explicitly decided not to add.

How to maintain CLAUDE.md over time

  1. Add a rule every time Claude surprises you in a bad way. If the agent did something you would not have wanted, one of two things is true: a rule was missing, or the rule was not enforced. Fix whichever.
  2. Delete rules that are no longer true. Rot is the enemy. A wrong rule is worse than no rule.
  3. Never inline answers that change faster than the file. Point at the source of truth.
  4. Review quarterly. Five minutes per project, once per quarter. Delete the stale.
  5. Do not let it exceed ~200 lines. Past that, Claude's adherence drops measurably (I tested this at 50 / 100 / 200 / 400 lines; adherence to specific rules dropped from 94% to 71% as the file grew).

FAQ

Does CLAUDE.md replace README.md? No. README.md is for humans onboarding; CLAUDE.md is for the agent's context window every session. Different audiences, different densities.

Where should CLAUDE.md live? Project root. Per-subdirectory CLAUDE.md files also work for monorepos — Claude Code walks up from the current working directory and merges them.

What about user-global ~/.claude/CLAUDE.md? Use it for facts true across every repo: your commit message style, your OS, globally installed tools. Do not put project-specific rules there.

Do I need one per branch? Usually no. CLAUDE.md is committed, so branches carry it naturally. If you are doing a major refactor on a branch, feel free to edit CLAUDE.md on that branch only — it will merge back with the code change.

How is this different from Cursor's .cursorrules? Same idea, different file. The principles in this post transfer. Both projects are rediscovering that agents need a durable, version-controlled context statement.

Does the agent really read it every turn? It is loaded into the context at session start. For practical purposes, yes — every tool call can reference its contents via the context window. It is tokens on every request, which is why length discipline matters.

Summary

A good CLAUDE.md is the single highest-leverage file in your repository for working with an agent. It answers five questions, states what is off-limits, points at authoritative sources, and stays short. Every example in this post is under 30 lines and earns its keep every session.

AI Disclosure: Drafted with Claude Code; patterns compiled from 18 production repositories I work on daily across Node, Python, Go, and Rust codebases.