How I Structure CLAUDE.md for Production Projects

If you have been using Claude Code for more than a week, you have almost certainly written a CLAUDE.md file. And if you are like most developers, your first version was not very good — mine was not, either.

The file probably read something like: “This is a TypeScript project. We use Express and Prisma. Follow best practices.”

That is not a CLAUDE.md. That is a README. And it produces the same result as no CLAUDE.md at all, because it tells the agent about the project without telling it how to behave.

This article is about the difference between those two things, and the structure I have settled on after iterating through a lot of versions.

The mental model shift

CLAUDE.md is not documentation. It is a behavioral contract.

Documentation describes what something is. A behavioral contract specifies what an agent must do, must not do, and should do by default in the absence of other instructions.

When you write “This is a TypeScript project using Prisma,” you are writing documentation. The agent might infer some behavioral rules from it — it might assume you want TypeScript syntax, might guess you want Prisma for database access — but it is guessing, and guesses are inconsistent.

When you write “All database access must go through the repository layer in /src/repositories. Never write Prisma queries directly in route handlers,” you have written a policy. The agent follows policies reliably. It guesses at implications inconsistently.

This distinction shapes everything.

The structure I use

Every CLAUDE.md I write now has six sections. Not all sections need to be long — some projects have two lines in a section, others have fifteen — but the structure is consistent.

1. Project identity (one paragraph maximum)

Not documentation. A one-sentence description of the project type and domain, followed by one sentence about the primary development mode: “This is a production API, not a prototype. Stability and test coverage take priority over velocity.”

That second sentence is the behavioral load-bearing one. It tells the agent how to weight tradeoffs throughout every session.

2. Always do

Explicit, specific behaviors the agent should apply without being asked. This is not a wishlist — every item must be something you actually want on every task, not just some tasks.

Examples from my current API project:

- After implementing any new function, run the test suite before reporting completion.
- Every new function needs at least one happy-path test and one error-case test.
- When modifying database-touching code, check for missing index usage before finishing.
- Log at the appropriate level: debug for development noise, info for business events, error for failures that need attention.

The specificity matters. “Always write tests” generates one trivial test. “Every new function needs at least one happy-path test and one error-case test, using Vitest, in a .test.ts file in the same directory as the source file” generates what you actually want.

3. Never do

This section has the highest leverage per line of anything in CLAUDE.md. Every time the agent does something that frustrates you — a pattern you always correct, a shortcut you always revert — write a rule here.

Over time, this list becomes your project’s permanent memory of what does not work.

Mine includes:

- Never modify committed migration files. Create a new migration instead.
- Never use `any` in TypeScript. If the type is genuinely unknown, use `unknown` and handle it.
- Never write inline styles. All styling goes through the design system tokens.
- Never hard-code environment-specific values. Use environment variables accessed through the config module.
- Never `console.log` in production code. Use the structured logger at `/src/lib/logger.ts`.
- Never declare a task done before running the test suite.

The last one is important. Without it, agents have a consistent bias toward reporting completion before verifying it.

4. Code style and conventions

This is where stack-specific rules live. Not a style guide — that belongs in ESLint or your linter config. This section covers the decisions that automated tools cannot enforce: naming patterns, module organization, error handling philosophy.

For a TypeScript API:

- Error handling: all errors should be typed. Throw typed errors, not plain Error objects.
- Repository functions return Result types (Ok/Err), not throw. Route handlers translate Results to HTTP responses.
- File naming: kebab-case for files, PascalCase for exported classes and types, camelCase for everything else.
- Module boundaries: `/services` contains business logic, `/repositories` contains data access, `/routes` contains HTTP handling. These do not import from each other laterally — data flows up through the stack.

5. Scope and tool permissions

What the agent is allowed to do, what requires approval, and what it must not do at all with its tool access.

- Shell commands that modify the database (migrations, seeds) require explicit approval before execution.
- Never delete files — move to a `.trash/` directory at the repo root instead.
- Changes to `/config/` files require explicit approval.
- Infrastructure changes (any file under `/terraform/`) are read-only — do not modify, only read to understand context.

The “never delete, move to trash” pattern is one I adopted after an agent deleted a file that turned out to be needed and was not in git. Moving to trash gives a one-step recovery.

6. Memory guidance

Instructions for what to write to persistent memory versus what should stay in CLAUDE.md.

- Write project-specific decisions and their rationale to `.claude/memory/project.md`.
- Write discovered patterns (API quirks, library behavior) to `.claude/memory/discoveries.md`.
- Do not write temporary task context to memory — only durable knowledge.

This prevents memory from becoming a graveyard of task notes while ensuring genuine discoveries persist across sessions.

Nested CLAUDE.md for monorepos

If you have a monorepo, a single root CLAUDE.md that tries to cover everything produces long, confused instructions. Claude Code supports nested files — a CLAUDE.md in a subdirectory extends or overrides the root for that scope.

My structure:

CLAUDE.md                    # Global conventions, security rules, commit format
apps/api/CLAUDE.md           # API-specific rules (error handling, test patterns)
apps/web/CLAUDE.md           # Frontend-specific rules (component conventions, CSS)
packages/shared/CLAUDE.md    # Library rules (public API discipline, no side effects)

Each child file is short and focused. The agent only loads what is relevant to its current working location.

The root file carries the rules that apply everywhere and that you never want to repeat — the security rules, the commit message format, the things that are never acceptable anywhere in the codebase.

Versioning and maintenance

Treat CLAUDE.md as code. Version it in git. Review changes in PRs. When you update a rule, add a comment explaining why — especially for “never do” items, where the reason is often a specific bad experience that is not obvious from the rule text.

I keep a # Changelog section at the bottom of each CLAUDE.md with the three most recent changes and their dates. It makes it easy to understand why a rule exists when you come back to it months later.

What I got wrong before getting this right

In roughly the order I learned them:

  1. Writing descriptions instead of policies
  2. One massive CLAUDE.md instead of a layered hierarchy
  3. Vague “always” instructions instead of specific ones
  4. Ignoring the “never do” section entirely
  5. Not versioning the file
  6. Forgetting to update it when I found a new agent anti-pattern
  7. Treating it as a one-time setup instead of a living document

The last one is the most important. The best CLAUDE.md files are the ones that get updated every time something goes wrong, until “going wrong” becomes rare.

Going deeper

This article covers Chapter 1 of a longer technical guide I published: Claude Code: The Production Guide. The full guide covers agent harnesses, parallelization with git worktrees, the auto-research framework, memory systems, MCP server security, and the complete security hardening model for running Claude Code with real shell and filesystem access.

If you are using Claude Code seriously and want the complete picture: Claude Code: The Production Guide ($29)

I also released a template pack with 15 ready-to-use CLAUDE.md configurations for specific stacks (Go, Python/FastAPI, Next.js, Terraform, dbt, and more) if you want a starting point rather than building from the structure above: CLAUDE.md Template Pack ($19)

What patterns have you found in CLAUDE.md that actually work? The comments are a good place for this — I find the design space genuinely interesting and there are almost certainly good patterns I have not tried.