Cream-colored illustration of a CLAUDE.md file card with a folded corner and neat annotation lines beside it

CLAUDE.md Best Practices (With a Full Annotated Example)

July 7, 2026 · SkillProof test team · 13 min read

CLAUDE.md is the most expensive file in your repository. Not in bytes. In repetition. Whatever you put there gets read into Claude’s context at the start of every session and rides along with every single message you send afterward. A bloated CLAUDE.md is a tax you pay hundreds of times a day without noticing.

We test Claude setups for a living at SkillProof, and we’ve now read more CLAUDE.md files than we’d like to admit. Most of them are doing it wrong in the same way: they treat the file as a dumping ground for everything the author ever wanted Claude to know. Coding philosophy, tone preferences, a full style guide, three paragraphs about being careful. All of it loaded, all the time, for every request including the one where you asked Claude to rename a variable.

This guide is our attempt to fix that. What CLAUDE.md is actually for, a complete annotated example you can adapt, what to move out into skills, and the arithmetic that explains why any of this matters.

What CLAUDE.md is and when it loads

CLAUDE.md is Claude Code’s project memory file. When you start a session in a directory, Claude Code walks up from your working directory looking for CLAUDE.md files and loads what it finds into the system context. Your global file at ~/.claude/CLAUDE.md loads too, in every project. Files in subdirectories load when Claude works with files in those subdirectories.

The part people miss: this isn’t a one-time read. Language models are stateless, so the full context, including your CLAUDE.md, is sent with every request in the conversation. Ask forty questions in a session and the file gets transmitted forty times. Prompt caching softens the dollar cost, and we’ll get to the actual numbers, but nothing softens the attention cost. Every token in CLAUDE.md is a token competing with your actual code for the model’s focus.

That’s the price. It buys you something real: Claude starts every session already knowing your build commands, your architecture quirks, and the landmines in your codebase. Nobody has to paste “we use pnpm, not npm” for the ninth time. The question is never whether to have a CLAUDE.md. It’s what earns a spot in it.

The golden rule: facts in CLAUDE.md, behavior in skills

Here’s the thesis of this whole article, and the single filter that fixes most bad files:

CLAUDE.md is for facts about this project. Skills are for reusable behavior.

A fact about this project: “prices are stored as integer cents.” Claude cannot guess that. No amount of general intelligence recovers it from thin air, and getting it wrong corrupts data. It belongs in CLAUDE.md, permanently loaded, because it’s relevant to almost any change touching money.

Reusable behavior: “when reviewing code, check error handling first, then security, then naming.” That’s not a fact about your project. It’s a procedure, it applies to every project you’ll ever touch, and it’s only relevant when you’re actually reviewing code. Put it in CLAUDE.md and you pay for it during every commit message, every CSS tweak, every “what does this function do.” Put it in a skill and it costs you one line of metadata until the moment a review actually happens, at which point the full instructions load on demand.

Most people stuff everything into CLAUDE.md because it’s the file they know about. It’s right there, it obviously works, and the cost is invisible because no invoice ever says “you paid for your style guide 4,000 times this month.” But the cost is real, and the split is almost mechanical once you ask two questions about any instruction. Is it true only of this project? Is it relevant to most requests? Two yeses: CLAUDE.md. Anything else: a skill, or the trash.

There’s a second reason for the split that has nothing to do with tokens. Instructions in an always-loaded file compete with each other. We’ve watched a 2,000-word CLAUDE.md where the genuinely important rule (“never run db:push against staging”) sat in paragraph eleven, below a lecture about clean code. Claude follows long instruction lists the way people do: the sharp items get diluted by the filler around them. Short files get obeyed. Long files get skimmed.

An annotated CLAUDE.md example for a mid-size web app

Here’s a complete example for a fictional but realistic Next.js storefront. The annotations explain why each section earns its permanent seat in context. It comes to about 350 words, roughly 500 tokens, and we’d argue nothing in it is cuttable.

# Acme Storefront

Next.js 14 (App Router) + TypeScript. Postgres via Prisma. Deployed on Vercel.
<!-- One line of stack. Claude infers most of this from package.json anyway;
     this just saves it the lookup. Do not write paragraphs here. -->

## Commands
- `pnpm dev` — local server on :3000 (needs `docker compose up db` first)
- `pnpm test` — unit tests (Vitest). Integration: `pnpm test:int` (slow, needs db)
- `pnpm lint && pnpm typecheck` — run both before calling any task done
- Migrations go through `pnpm db:migrate`. NEVER `db:push` outside local.
<!-- Commands earn their place because Claude runs them dozens of times per
     session, and a wrong guess (npm vs pnpm) wastes a round-trip every time.
     The db:push line is here because the failure is irreversible. -->

## Architecture facts you can't guess
- `src/app` is routes only. All logic lives in `src/modules/<domain>`.
- API routes are thin wrappers over `src/modules/*/service.ts`. No logic in routes.
- Auth is Clerk, but `userId` in services is OUR internal id, not Clerk's.
  Map with `getInternalUser()`.
- Prices are integer cents everywhere. Formatting lives in `lib/money.ts`.
- Feature flags come from `flags.ts`, not env vars.
<!-- The test for this section: would a skilled new hire get it wrong on
     day one? The Clerk id mismatch has caused real bugs; that is exactly
     the kind of fact worth paying for on every request. -->

## Conventions that differ from defaults
- Named exports only. Legacy violations exist in `src/legacy`; don't add new ones.
- Server components by default. `"use client"` needs a comment saying why.
- Zod schemas live next to services; infer types from them, never hand-write both.
<!-- Only conventions that DIFFER from what Claude would do anyway.
     "Use TypeScript strict mode" when tsconfig already says so is a wasted line. -->

## Gotchas
- `src/legacy/*` is frozen. Don't refactor it or import from it in new code.
- CI runs Node 20. `pnpm test:int` fails on 22; use `nvm use` first.
<!-- Each line here represents an hour someone actually lost. -->

## Definition of done
Lint, typecheck, and unit tests pass, and you state which of them you ran.
<!-- One sentence. Not a philosophy of quality. -->

Notice what’s absent. No “write clean, maintainable code.” No tone instructions. No explanation of what Next.js is. No lint rules that the linter already enforces mechanically. Every line is either a command Claude will execute, a fact it cannot infer, or a boundary where crossing it costs real money or real hours.

What to move out into skills

If your current CLAUDE.md is 1,500 words, the excess usually falls into a few recognizable buckets, and each bucket has a skill-shaped home. Some examples from our own catalog, all installed and tested on clean machines before we listed them:

Git ceremony. Branch naming, commit message format, PR description templates, when to squash. This is behavior, it’s identical across your projects, and it’s only relevant when you’re actually committing. The git-workflow skill we tested handles exactly this scope, and moving it out of CLAUDE.md typically saves 200 to 400 words of always-loaded text.

Review checklists. “When I ask for a review, check X then Y” instructions are the classic CLAUDE.md squatter. They’re long, they’re procedural, and they’re dormant 95% of the time. code-review-checklist loads its checklist only when a review is happening, which is the entire point of the skill format.

Debugging discipline. Instructions like “reproduce before fixing, state your hypothesis, verify after” describe a methodology, not your project. systematic-debugging packages that methodology and stays out of context while you’re doing anything other than chasing a bug.

The general rule: if you could copy the paragraph into a different project’s CLAUDE.md without editing it, it isn’t a fact about your project and it shouldn’t live there. Extract it. Writing your own skill takes about twenty minutes with our guide, and it’s the highest-leverage twenty minutes available to anyone whose CLAUDE.md has scrolled past one screen.

FREE STARTER PACK

We bundled five tested skills that absorb the most common CLAUDE.md bloat: git workflow, review checklists, debugging discipline, and more. Install them, then delete the matching paragraphs from your file.

Get the free starter pack

Anti-patterns we keep seeing

We review a lot of setups, and the same five failures show up on repeat.

The novel. A 3,000-word CLAUDE.md that reads like an engineering handbook. Someone had a bad experience, added a paragraph, had another, added another, and never deleted anything. The file only grows. Beyond the token cost, files like this bury the two rules that actually matter under twenty that don’t, and Claude’s compliance with all of them drops together.

Stale commands. The file says npm run test:unit, the script was renamed to pnpm test eight months ago, and now Claude confidently runs a command that fails, reads the error, pokes around package.json, and recovers. Every session. You’re paying for the wrong documentation and then paying again for Claude to route around it. A wrong CLAUDE.md is worse than no CLAUDE.md, because Claude trusts it.

Duplicated lint rules. “Use single quotes. No unused variables. Max line length 100.” Your ESLint config already enforces this mechanically, with better coverage than an LLM’s attention ever will. Restating machine-enforced rules in prose is pure waste. The one exception: rules the tooling can’t catch, like “don’t import from src/legacy,” which genuinely need to be written down.

The “be helpful” section. “Write clean code. Think carefully before making changes. Be thorough but concise.” We see some variation of this in maybe half the files we review, and it does nothing. Claude is already trying. Vibes-based instructions with no testable content are the first thing to delete, and their only measurable effect is the tokens they burn.

Autobiography. Long descriptions of what the product does, who the users are, the company mission. Occasionally one product fact matters for code decisions (“our users are on slow rural connections, bundle size is a real constraint”). Keep that one sentence. Cut the pitch deck.

The token math

Let’s do the arithmetic people skip, because the numbers are what turned us into minimalists.

A 3,000-word CLAUDE.md is roughly 4,000 tokens. It rides with every request, so a working session of 40 messages transmits it 40 times: 160,000 tokens of input in one session that are your memory file, not your code or your conversation. Work five sessions a day and you’re moving 800,000 CLAUDE.md tokens daily. Over a 22-day month, about 17.6 million.

Prompt caching rescues most of the dollar cost, and we should be honest about that. Cached input on Sonnet runs a tenth of the base price, so those 17.6M tokens cost a couple of dollars a month if the cache stays warm, more like $50 if it doesn’t. Annoying, not ruinous.

The cost that caching does not rescue is context. Claude Code’s context window is finite, and long sessions already end in compaction, where the conversation gets summarized and detail gets lost. A 4,000-token permanent resident means you hit compaction earlier, every session, forever. It also degrades attention before you hit any hard limit: models demonstrably follow instructions better when there are fewer of them competing. The difference between a 500-token file and a 4,000-token file isn’t 3,500 tokens. It’s whether the model still cares about your “never touch staging” rule at message 35.

Run the same numbers on the trimmed example above: 500 tokens, 40 messages, five sessions, 22 days. About 2.2M tokens a month, an eighth of the bloated version, with the important rules standing in an uncrowded room. We wrote up the broader cost-cutting playbook in our token cost guide, and CLAUDE.md trimming is consistently the cheapest win in it.

Project vs global: the ~/.claude/CLAUDE.md split

You have two files, and things go wrong when content lands in the wrong one.

~/.claude/CLAUDE.md is global. It loads in every project, so it must contain only things true everywhere: “I use pnpm on all personal projects,” “answer in English even though my prompts are sometimes Russian,” “never commit without being asked.” Keep it under ten lines. Every line here is multiplied across all your work, so its budget should be the tightest.

<project>/CLAUDE.md is the project file, checked into git, shared with teammates and CI. Facts about this codebase, in the shape of the example above. Because it’s shared, personal preferences don’t belong in it; that’s what CLAUDE.local.md or the global file is for.

Subdirectory CLAUDE.md files are the underused middle tier. A packages/api/CLAUDE.md with API-specific facts loads only when Claude touches that package. If your monorepo’s root file has sections that apply to one workspace, pushing them down a level is free token savings with zero information loss.

One misconception worth killing: putting a thing in more than one of these files does not make Claude follow it harder. It makes the file longer. Pick one home per fact.

Keeping it fresh

CLAUDE.md rots faster than regular documentation because nothing breaks visibly when it’s wrong. Tests fail loudly. A stale memory file just quietly misdirects the model, which quietly recovers, and you quietly pay for both.

Our ritual is boring and takes ten minutes a month. Open the file. For every command, actually run it. For every stated fact, ask whether it’s still true and whether Claude got anything wrong this month because of it. Delete at least one line; there is always a line, and if you can’t find it, your standards for “earning a place” have slipped. Then check the length: one screen is the target, two screens is the ceiling, and past that you’re not editing anymore, you’re extracting into skills.

The best trigger for updates isn’t the calendar, though. It’s the moment Claude does something wrong because the file misled it. Fix the file in the same breath as the code. Treat CLAUDE.md bugs like bugs, because they have the same recurrence behavior: unfixed, they happen again next session.

SKILLPROOF PACK

The Optimizer Pack is our tested bundle for exactly this problem: skills that audit your context spend, slim down your setup, and keep sessions fast. Every skill in it passed our clean-machine tests, and the pack pays for itself in the first month of saved tokens.

Get the Optimizer Pack — $10

FAQ

How long should a CLAUDE.md be?

Under 500 words for most projects, under 1,000 for a genuinely complicated monorepo. Our working test: if it doesn’t fit on one screen, something in it is behavior masquerading as fact, and that something belongs in a skill. The example in this article is about 350 words and covers a real mid-size app.

What’s the difference between CLAUDE.md and a skill?

Loading behavior. CLAUDE.md is loaded in full, always, for every request. A skill exposes only a one-line description until a task matches it, then loads on demand. So CLAUDE.md should hold facts you need constantly, and skills should hold procedures you need occasionally. Our skills guide covers the mechanics.

Should I commit CLAUDE.md to git?

Yes. It’s shared documentation, and your teammates’ Claude sessions benefit from the same facts yours does. Keep personal preferences in CLAUDE.local.md (gitignored) or your global file instead of pushing them onto the team.

Can I use nested CLAUDE.md files in subdirectories?

Yes, and in monorepos you should. Claude Code loads a subdirectory’s file when it works with files there. Root file gets repo-wide facts, packages/api/CLAUDE.md gets API facts, and single-package projects can skip nesting entirely.

Does trimming CLAUDE.md actually change Claude’s behavior, or just cost?

Both, and behavior is the bigger win. Instruction-following degrades as instruction count grows; a short file with five sharp rules gets obeyed more reliably than a long one with thirty. The token savings are the measurable part, and our efficiency rankings track which skills help most there, but the compliance improvement is what you’ll notice first.

One last opinion: the best CLAUDE.md files we’ve seen were all edited by deletion. Nobody writes a great one on day one. You write a mediocre one, watch where Claude stumbles, add the fact that would have prevented it, and cut a line of filler to pay for it. Six weeks of that beats any template, including ours.

★ 9.6/10 × 3

The free starter pack

3 skills with our highest test scores plus the install checklist — the setup we'd put on a fresh machine. Free, by email.

One email with the pack + a short weekly digest of new test results. Unsubscribe anytime.