← blog··13 min read

How to Write a CLAUDE.md File (With Examples)

CLAUDE.md is the single biggest lever you have on Claude Code output quality. Here's what to include, what to leave out, and two complete production-ready examples for Next.js and a Python API.

claude-codeclaude-mdconfigurationtutorial
Kev Gary
Senior Software Engineer, Credit Karma at Intuit

Using Claude Code without a CLAUDE.md is like writing code without a linter config. You can do it, but you'll spend every session correcting Claude back toward your conventions. The fix takes about ten minutes and makes every subsequent session meaningfully better.

Despite that, most engineers I watch use Claude Code never write one. They don't know what it is, or they think it's optional, or they've seen a screenshot of one and assumed theirs would have to be just as detailed. None of those are good reasons.

This is the complete guide to writing a CLAUDE.md — what it is, why it works, what to include, what to leave out, two complete real examples you can steal, and the common mistakes I see engineers make when they finally write one.

What CLAUDE.md is and why it matters

A CLAUDE.md file is a plain-English contract between you and Claude Code. It lives at the root of your repo, and Claude Code loads it automatically at the start of every session. It's where you tell Claude what your project is, what stack you use, what conventions to follow, and what never to do.

The reason it matters so much is simple: without context, Claude has to guess. And when Claude guesses, it defaults to the patterns it's seen most often in its training data. Those patterns might be Express instead of Fastify, callbacks instead of async/await, Jest instead of Vitest, any instead of narrowed types, default exports instead of named ones. None of those are wrong — they just might not be what your team does.

With a CLAUDE.md, Claude doesn't guess. It knows. And the difference between Claude guessing and Claude knowing is the difference between "Claude keeps using the wrong patterns" and "Claude already writes code the way we write code here." It's the single biggest leverage point in a Claude Code workflow.

One more thing: CLAUDE.md is a file in your repo. It's committed. It's shared with your team. When a new engineer joins and runs Claude Code, they get the same context you get. It's a living piece of team documentation that also happens to be machine-readable by Claude.

The CLAUDE.md hierarchy: global, project, subdirectory

Claude Code loads CLAUDE.md files in a specific order, with later files overriding earlier ones:

  1. ~/.claude/CLAUDE.md — your personal, global preferences. These apply to every Claude Code session on your machine regardless of which repo you're in. This is where you put things like "always use pnpm" or "I prefer semicolons."
  2. <repo-root>/CLAUDE.md — the project-level file. Committed to the repo, shared with the team. This is the main event and the file most of this post is about.
  3. <repo-root>/<subdir>/CLAUDE.md — scoped rules for a subdirectory. Useful in monorepos or in repos where different sections have different conventions (e.g., packages/ui uses React conventions, packages/api uses Node/Fastify conventions, and each has its own CLAUDE.md).

You don't have to use all three levels. Start with project-level. Add subdirectory-level later if you notice your conventions diverge meaningfully between folders. Add global-level if you find yourself repeating the same personal preferences across multiple projects.

What to include

A good CLAUDE.md has a few sections. Not all of them are mandatory, but they cover the bases. Here's the minimal set:

Project overview. One or two sentences about what the project is, who uses it, and what problem it solves. Claude uses this to make higher-level decisions about naming, structure, and trade-offs.

Tech stack. The specific frameworks, libraries, and versions you use. Be specific: "Next.js 14 App Router" not "Next.js." "Fastify" not "Node.js." "pnpm" not "a package manager."

Code standards. The rules you want Claude to follow when writing or editing code. Things like "prefer async/await over Promise chains," "never use any," "named exports only," "Server Components by default."

Testing conventions. What test framework you use, where tests live, what naming conventions apply. This one matters a lot because Claude will often write tests for you, and having it match your style out of the gate is a huge time save.

File structure. A brief description of how your repo is organized. "Routes go in src/routes, services in src/services, DB code in src/db." This helps Claude place new files in the right spot instead of dropping them at the root.

"Always do" rules. Specific positive rules. "Always run pnpm typecheck before claiming a task is done." "Always co-locate tests next to source files."

"Never do" rules. Specific negative rules. "Never use npm — always pnpm." "Never commit console.log." Negative rules are often more powerful than positive ones because they prevent whole classes of issues.

Project-specific context. Anything weird or non-obvious about this codebase. Custom conventions, deprecated patterns that are being phased out, specific gotchas.

What to leave out

Just as important as what to include is what to skip.

Secrets and credentials. Never put API keys, tokens, or passwords in CLAUDE.md. It's committed to your repo.

Novel-length instructions. Aim for under 500 lines. Beyond that, you're better off splitting into subdirectory-scoped files or restructuring. If Claude has to read 2,000 lines of context before every task, you're burning tokens and Claude is more likely to skim.

Rigid templates for every possible task. CLAUDE.md is principles and conventions, not step-by-step playbooks for individual tasks. If you find yourself writing "to add a new route, first do X, then Y, then Z" — that belongs in a custom slash command, not CLAUDE.md.

Things you don't actually do. It's tempting to aspirationally write rules you want your team to follow. Don't. If your codebase is full of any and your CLAUDE.md says "never use any," Claude will get confused and start refactoring unrelated files it touches. CLAUDE.md should reflect reality, not aspiration.

Obvious restatements of the language or framework. "JavaScript is dynamically typed." "React components return JSX." Claude already knows. You're wasting context tokens.

A complete example: Next.js / TypeScript project

Here's a real CLAUDE.md for a Next.js 14 + TypeScript project. You can drop this into your repo, edit to match your actual conventions, and you're 80% of the way there.

# Project: Dashboard (internal analytics)
 
## Overview
Internal analytics dashboard used by the data and ops teams. Renders
KPIs, runs ad-hoc queries, and exports reports. Team of 4 engineers.
We optimize for shipping velocity over novelty.
 
## Tech stack
- Next.js 14 (App Router, Server Components by default)
- TypeScript strict mode, no `any`
- Tailwind CSS + shadcn/ui for primitives
- PostgreSQL via Drizzle
- React Server Components for data fetching, TanStack Query only for
  client-side mutations
- react-hook-form + zod for forms
- Vitest for unit tests, Playwright for e2e
- pnpm workspaces (never npm or yarn)
 
## Code standards
- Always use Server Components unless interactivity requires "use client".
  When using "use client", leave a brief comment explaining why.
- Named exports only. Default exports are reserved for Next.js page/layout files.
- Tailwind classes sorted by the prettier plugin — never reorder manually.
- Prefer composition over prop explosion. If a component has more than
  7 props, split it.
- `any` is banned. Use `unknown` + narrowing, or write the proper type.
- Error boundaries at route boundaries, not leaf components.
 
## Testing conventions
- Co-locate tests next to source: `Button.tsx``Button.test.tsx`.
- Every utility gets a unit test. Every new route gets a smoke test.
- Test names: `it("returns X when Y")` — no implementation details in names.
- Use semantic queries in Testing Library (`getByRole`, `getByLabelText`).
  Only fall back to `getByTestId` when semantic queries truly don't work.
- E2E tests live in `/e2e` and run against a local preview build.
 
## File structure
- src/app/ — routes, layouts, server actions
- src/components/ — shared components (`ui/` for primitives, feature dirs otherwise)
- src/lib/ — pure utilities, no React imports
- src/hooks/ — client hooks only
- src/server/ — server-only code (db, auth, actions), imports `"server-only"` package
 
## Always do
- Run `pnpm typecheck` and `pnpm test` before claiming a task is done.
- Update the relevant test when changing behavior.
- Use `next/image` for images, `next/font` for fonts, `next/link` for internal nav.
- Prefer `zod` for any external data boundary (form inputs, API responses).
 
## Never do
- Fetch data in client components if a server component could do it.
- Import server-only modules from client components.
- Commit `console.log` or commented-out code.
- Add new dependencies without flagging in the PR description.
- Use `useEffect` for data fetching — use Server Components or TanStack Query.

That file is about 55 lines and it encodes roughly two years of team conventions. Every Claude Code session on this repo now starts with that much context loaded automatically. The difference is dramatic.

A second example: Python API service

Because not everyone works in Next.js, here's the same approach for a Python API.

# Project: Billing API
 
## Overview
Internal billing service. Serves metering events, subscription state,
and invoice generation. ~500 req/sec at p99 < 50ms. Deployed to GKE,
owned by the platform team.
 
## Tech stack
- Python 3.11+, type hints required everywhere
- FastAPI (not Flask, not Django)
- Poetry for deps (never pip or pipenv)
- pydantic v2 for validation
- SQLAlchemy 2.0 + Alembic for DB
- structlog (JSON output)
- pytest + pytest-cov for testing
- ruff + mypy strict for linting/typing
 
## Code standards
- Type hints on every function signature. `Any` is banned.
- Public functions have Google-style docstrings (Args / Returns / Raises).
- All I/O boundaries use pydantic models for validation.
- Services are pure functions that take dependencies as arguments.
- Route handlers are thin: validate → call service → shape response.
- Errors throw typed `AppError` subclasses; global error handler maps to HTTP codes.
 
## Testing conventions
- Tests live in `tests/` mirroring `src/` structure.
- Unit tests for services, integration tests for routes using real Postgres in Docker.
- Test names follow `test_<what>_when_<condition>_then_<result>`.
- >80% coverage target on `src/services` and `src/routes`.
 
## File structure
- src/routes/ — FastAPI routers, pydantic schemas, thin handlers
- src/services/ — business logic, pure where possible
- src/db/ — SQLAlchemy models, repositories
- src/lib/ — cross-cutting utilities (logger, errors, config)
- src/schemas/ — pydantic models for boundaries
 
## Always do
- Run `poetry run ruff check --fix && mypy src && pytest` before claiming done.
- Log with structured context: `log.info("message", user_id=id, action="x")`.
- Wrap DB writes touching multiple tables in a transaction.
- Validate every request body and response with pydantic.
 
## Never do
- Trust client-supplied IDs without verifying ownership.
- Return raw SQLAlchemy errors to clients.
- Use `print()` — always `structlog`.
- Commit secrets or `.env` files.
- Add new frameworks or ORMs without team discussion.

Again — about 50 lines, encoding most of the conventions a new team member would need to know. Claude Code picks it all up automatically.

Common mistakes

Too vague. "Write good code." "Use best practices." Useless. Claude already tries to do those things. Be specific: "Use named exports. Co-locate tests next to source. Never use any."

Too detailed. A 2,000-line CLAUDE.md full of step-by-step instructions for individual tasks. Claude will skim, miss things, and you'll burn tokens. If you find yourself writing procedural steps, move them to custom slash commands.

Aspirational rules that don't match reality. "Never use any" in a codebase where 40% of the files use any. Claude will try to refactor unrelated files to match the rule. Either enforce the rule in the existing code, or loosen the rule to "prefer narrow types over any in new code."

No "never do" rules. Positive-only rule sets miss a lot of footguns. Negative rules — "never do X" — are often more powerful because they eliminate whole classes of bad output.

No examples. Abstract rules are harder to apply than concrete ones. "Service functions look like createUser, sendEmail, deleteInvoice — verb + noun, camelCase, in src/services/<domain>.ts" is way clearer than "follow service conventions."

Never iterating. You write it once on day one and never update it. CLAUDE.md is a living file. When Claude gets a convention wrong, the fix isn't to correct the output — it's to add the rule.

How CLAUDE.md relates to AGENTS.md, memory, and slash commands

CLAUDE.md is one piece of a bigger context system in Claude Code. Worth knowing the others:

  • AGENTS.md is a sibling file with agent-specific instructions. It's used by the Agent SDK and by headless / automated workflows. Most engineers don't need an AGENTS.md — start with CLAUDE.md and only add AGENTS.md if you build custom agents on top of the Agent SDK.
  • Memory is a per-session store of things Claude learns during a conversation. You can persist things to memory with /memory. It's useful for long-running projects where you want Claude to remember working assumptions between sessions.
  • Custom slash commands (.claude/commands/) are reusable prompts. Where CLAUDE.md encodes conventions, slash commands encode workflows. If you find yourself writing the same multi-step prompt often, turn it into a command.

Together, these four — CLAUDE.md, AGENTS.md, memory, slash commands — form the "context stack" of a tuned Claude Code environment. Most engineers never touch any of them. The ones who do report feeling like they're working with a different tool entirely.

The compound effect

The best reason to write a CLAUDE.md isn't any single session — it's the compound effect over time. Every session that starts with a tuned CLAUDE.md is a session that's 10–20% more accurate, 10–20% more aligned with your conventions, and 10–20% less frustrating. Multiply that by hundreds of sessions per year and the delta is enormous.

It's also — and this is the part nobody talks about — a meaningful piece of team documentation. A good CLAUDE.md is a concise summary of how your team writes code. New engineers reading it learn your conventions quickly. Code reviewers can reference it when giving feedback. It's living documentation that also happens to be machine-readable.

Write one today. Spend ten minutes on it. Commit it. Use Claude Code for a week, note where it gets things wrong, iterate. Within two weeks you'll have a tuned CLAUDE.md that makes every session better.

If you want more context on where CLAUDE.md fits in the broader Claude Code workflow, check out my Claude Code tutorial for the basics, and my comparison of Claude Code with Cursor and Copilot for how it fits into a multi-tool stack.

And if you want the full version of this — CLAUDE.md mastery, nesting, interactions with AGENTS.md and memory, custom commands, and MCP integration — that's Day 2 of Claude Camp, the 3-day live cohort bootcamp. We tune CLAUDE.md on your actual codebase together, live. See the full curriculum or join the waitlist for the next cohort.

// keep reading

Related posts

← all postsPublished April 4, 2026