The CLAUDE.md in this repository is 35 lines long. I know the exact number because I checked before writing this, and because the length is most of the point: it is the shortest file I could write that stops me re-explaining the same handful of things at the start of every session.
A CLAUDE.md file
is a markdown file that Claude Code loads into context at the start of every session, giving an agent the project facts it would otherwise have to rediscover, guess at, or ask you for.
The most useful sentence I have read about these files is in Anthropic's own memory documentation, current as of July 2026: CLAUDE.md content is delivered as a user message after the system prompt rather than as part of it, and Claude treats it as "context, not enforced configuration." That explains most of the disappointment people have with these files. You are not writing config. You are writing the top of a conversation, and every line you add competes for attention with the actual task.
Here is the whole file, then the reasoning behind it.
What is a CLAUDE.md file?
A CLAUDE.md file is a plain markdown file that Claude Code reads at the start of every session to learn project-specific facts: build commands, conventions, layout, and traps. It loads automatically from a small set of known locations, and it shapes behaviour rather than enforcing it.
Four locations matter, and they load from broadest scope to most specific. Everything discovered gets concatenated into context rather than overriding what came before, so a project instruction lands after a user instruction and gets read last.
| Scope | Where it lives | What belongs in it | Who sees it |
|---|---|---|---|
| Managed policy | /Library/Application Support/ClaudeCode/CLAUDE.md on macOS, /etc/claude-code/CLAUDE.md on Linux | Org-wide standards, compliance reminders | Every session on the machine |
| User | ~/.claude/CLAUDE.md | Your personal preferences across every project | Just you, everywhere |
| Project | ./CLAUDE.md or ./.claude/CLAUDE.md | Build commands, layout, conventions, invariants | Your team, via source control |
| Local | ./CLAUDE.local.md | Sandbox URLs, preferred test data. Add it to .gitignore | Just you, in this repo |
Files in directories above your working directory load in full at launch. Files in subdirectories below it load on demand, when Claude actually reads something in that directory.
The part people skip is the enforcement gap. If a rule genuinely must hold every time, prose will not get you there — a PreToolUse hook will, because it runs as a shell command regardless of what the model decides. This is the same lesson I learned the hard way getting schema-valid JSON out of a language model: you stop asking nicely and start constraining.
The whole file I ship, copy it if you want
Below is this repository's CLAUDE.md in full — four sections, 35 lines, 387 words, nothing redacted. It governs the site you are reading right now.
Why does every line have to earn its context?
Because a line in a CLAUDE.md costs tokens in every session forever and buys you only the corrections it prevents. A line earns its place when it changes what an agent would otherwise do by default. A line that merely describes the repository does not, because the repository is right there.
Sort the file by that test and five categories fall out.
| Line, abbreviated | Category | What it prevents |
|---|---|---|
| eslint src; next lint no longer exists in Next 16 | A tool default that changed | An agent reaching for next lint, which Next.js removed in version 16, then "fixing" the lint script back to something that cannot run |
| content.js is the CMS — edit copy here, not in pages | A single source of truth | Copy edited directly in a page component, which looks correct in the diff and silently diverges from the file that actually feeds every other surface |
| New UI should use the theme-scoped CSS vars so both themes work with one rule | A naming and token convention | Hard-coded hex values that look right in dark mode and unreadable in light, discovered by a visitor rather than by me |
| RouteGuard must stay synchronous | A load-bearing invariant | A refactor that reintroduces a bug already fixed once |
| Both lockfiles exist; npm is what's been used and tested | An environment ambiguity | An install with the wrong package manager, producing a lockfile nobody wanted in a diff nobody asked for |
| Borrow playwright-core from a sibling repo; scroll in small steps when screenshotting | An undiscoverable local workflow | Twenty minutes of an agent installing a browser stack, then screenshots of sections sitting at opacity 0 |
Notice what is absent. There is no code style section, no "always write tests", no instructions about tone or thoroughness. ESLint, Prettier and tsc already enforce the first, npm run quality runs all of them in one command, and the rest is decoration that dilutes the lines around it.
The one line that came from a real regression
One line in my CLAUDE.md exists because of a specific bug in this repository, and it is the line I would keep if I could keep only one:
RouteGuard must stay synchronous. It used to gate children behind a mount effect — caused a spinner flash on every load and an empty DOM for sibling effects at hydration.
RouteGuard decides whether a route is allowed to render. Gating its children behind a mount effect is a reasonable-looking pattern that produces two symptoms no diff will show you: a spinner flashes on every single page load, and the effects mounted alongside it hydrate against a DOM with nothing in it. Scroll reveal and the cursor spotlight both need elements to exist at hydration time.
Left undocumented, the tidiest available refactor is exactly the one that broke it. That is the shape of a line worth writing — a fix whose reason is invisible in the code that contains it.
Most repositories have several of these and write down none of them. The layout engine on Codelit's architecture canvas went through six rewrites, and the config that finally shipped looks arbitrary until you know which five layouts it replaced. A fresh session has none of that history, and neither does a new hire, which is a decent test for whether a line belongs.
The other line worth writing is a pointer that saves a search. Mine says the ⌘K palette lives in CommandPalette.tsx and that new destinations go in its ACTIONS array. I have argued before that a command palette is the cheapest retention feature you can ship; it is also the feature an agent will cheerfully rebuild from scratch if you never say where it already lives.
What does not belong in a CLAUDE.md file?
Anything an agent can work out by reading the repository. Anthropic ships this heuristic itself: the doctor checkup, from Claude Code v2.1.206 onward, proposes trims to a checked-in CLAUDE.md by cutting directory layouts, dependency lists and architecture overviews, while keeping pitfalls, rationale, and conventions that differ from tool defaults.
Apply that honestly to my own file and the weakest section is the design language one. A capable agent could open custom.css and infer the palette without my help. What keeps those bullets alive is the second half of each: the event name that opens the palette, the class that gates hiding so no-JS visitors still see content, the detail that a bento grid staggers its children instead of fading the container. Those are decisions. The sentences around them are descriptions, and the descriptions are what would go if I had to halve the file.
The architecture section sits on the same line. A list of file paths is derivable. "Edit copy here, not in pages" is not.
Four things I keep out of the file entirely:
- Anything ESLint, Prettier or the type checker already enforces. The tool is the enforcement; prose is a second copy that will drift.
- Tone, persona, and encouragement. It reads as instruction and behaves as noise.
- Secrets, sandbox URLs, personal test data. Those go in a gitignored CLAUDE.local.md, which loads alongside the main file and stays out of the repo.
- Anything you find yourself writing as ALWAYS or NEVER in capitals. If you are shouting at a context file, you have found a hook.
Should you use CLAUDE.md or AGENTS.md?
Claude Code reads CLAUDE.md and not AGENTS.md — Anthropic's memory docs state that plainly as of July 2026. If your repository already has an AGENTS.md, the documented bridge is a CLAUDE.md whose first line imports it, with any Claude-specific instructions written underneath.
The context worth having: AGENTS.md is an open format for guiding coding agents, stewarded by the Agentic AI Foundation under the Linux Foundation, and as of July 2026 its site reports use by over 60,000 open-source projects, with native support listed from Codex, Cursor, GitHub Copilot's coding agent, Gemini CLI, Zed, Aider, Windsurf and Jules among others. It is the closest thing the space has to a shared convention, and Claude Code is the conspicuous holdout.
| Setup | Who reads what | Best for | Where it breaks |
|---|---|---|---|
| CLAUDE.md only | Claude Code | Solo repos, or teams standardised on one agent | Every other agent starts from zero |
| AGENTS.md only | Everything except Claude Code | Repos where Claude Code is not in the toolchain | Claude Code ignores the file entirely |
| AGENTS.md plus a CLAUDE.md that imports it | Both, from one source | Mixed toolchains, most open source | Two files to keep honest, and the import path has to resolve |
| CLAUDE.md symlinked to AGENTS.md | Both, identically | Teams with nothing Claude-specific to say | Symlinks on Windows need Administrator or Developer Mode, and there is nowhere to put Claude-only rules |
Full disclosure on my own setup: this repo has a CLAUDE.md and no AGENTS.md, which is the least defensible row in that table for anyone but me. I am the only person in the repository, the same solo constraint that shaped how Codelit got built, and Claude Code is the only agent I run against it. The day a second agent touches this codebase, the import row is the one I move to.
What happens when two agents read different instruction files?
Two agents reading two different instruction files drift apart, and the repository quietly acquires two versions of the truth: the file you updated and the file you forgot. The failure is never dramatic. One agent knows the lint command changed, the other keeps reaching for the old one, and you spend a week wondering why results vary by tool.
The import fixes that by making one file canonical and the other a wrapper. Put the shared content in AGENTS.md, make CLAUDE.md a single import line plus whatever is genuinely Claude-specific, and there is only one place a stale instruction can live. When you want to know what actually loaded rather than what should have, run the context command and read the memory files list; there is also an InstructionsLoaded hook that logs which instruction files loaded and when.
How long should a CLAUDE.md be before it starts hurting?
Anthropic's documented target is under 200 lines per CLAUDE.md file, on the stated grounds that longer files consume more context and reduce adherence. Mine is 35. I would not defend 35 as an optimum, but I would defend the direction, because the failure mode of a long file is invisible.
What drives files past 200 lines is fear. Something dumb happens, you add a line to prevent it, and the line is free in isolation and expensive in aggregate. Adherence is not additive: every rule guarding against an unlikely mistake makes the rules guarding against likely ones a little quieter.
The tempting escape hatch does not work. Imports organise a file without shrinking its footprint, because imported content still loads at launch, recursing up to four hops deep. The mechanism that genuinely defers is path-scoped rules — markdown files in .claude/rules/ with a paths glob in their frontmatter, which only enter context when Claude reads a matching file. That is the right home for a rule that only applies to your API handlers or your migrations.
What belongs in the file, and what belongs in a skill or a hook?
A fact that is true in every session belongs in CLAUDE.md, a procedure that only matters occasionally belongs in a skill, a rule scoped to one part of the tree belongs in a path-scoped rule, and anything that must happen whether or not the model agrees belongs in a hook. One question sorts them: when does this need to be true?
| Mechanism | When it loads | Can the model skip it | Best for |
|---|---|---|---|
| CLAUDE.md | Every session, in full | Yes — it is context | Build commands, conventions, invariants, traps |
| Rules with a paths glob | When Claude reads a matching file | Yes | Rules that only apply to one area of the codebase |
| Skill | On demand, when invoked or judged relevant | Yes | Multi-step procedures that do not belong in every session |
| Hook | At a fixed lifecycle event | No — it is a shell command | Format gates, test gates, blocking a path outright |
The rule of thumb I use: if I am writing a sentence that assumes the agent will remember it under pressure, I am writing the wrong artifact. Instructions shape behaviour. Hooks decide it.
What I would write first if I started again
Run the init command, then delete most of what it produces. The generated file is a good description of the repository, and a description is precisely what the agent can already get for itself. What you want is the difference between your repository and an agent's defaults: the commands that are not standard, the conventions that are not obvious, the fixes whose reasons are not in the code.
After that, add exactly one line each time you correct the same thing twice. Once is noise. Twice is a pattern, and a pattern is cheaper as a line in a file than as a correction you retype forever.
The side effect is that the file turns into a changelog of your own repeated mistakes, which makes deletion easy — when a correction stops being necessary, the line goes. Mine is 35 lines because I keep asking which of them I would still bet money on. Most files I see have never been asked that question once.
Questions people actually ask
- What is a CLAUDE.md file and when does Claude Code read it?
- A CLAUDE.md file is a markdown file of project instructions that Claude Code loads at the start of every session. It can live at the project root, under a .claude directory, in your home directory for personal preferences, or at a managed policy location for an entire organisation. Anthropic's memory documentation is explicit that the content arrives as a user message after the system prompt, and that it is treated as context rather than as enforced configuration.
- Does Claude Code read AGENTS.md?
- No. Anthropic's memory documentation states plainly that Claude Code reads CLAUDE.md and not AGENTS.md. If your repository already uses AGENTS.md for other coding agents, the documented bridge is to create a CLAUDE.md whose first line imports it with an at-sign path reference, then add any Claude-specific instructions underneath. A symlink from CLAUDE.md to AGENTS.md also works, although on Windows that requires Administrator privileges or Developer Mode.
- How long should a CLAUDE.md file be?
- Anthropic's guidance is to target under 200 lines per file, because longer files consume more context and reduce how consistently the instructions get followed. The file I ship in my portfolio repository is 35 lines. Splitting a long file into imports organises it but does not shrink it, since imported files still load at launch. Path-scoped rules are the only mechanism that genuinely defers loading until a matching file is opened.
- What should you not put in a CLAUDE.md file?
- Anything the agent can work out by reading the repository. Anthropic's own trim heuristic, shipped in the doctor checkup, cuts directory layouts, dependency lists and architecture overviews, and keeps pitfalls, rationale and conventions that differ from tool defaults. Also leave out anything your linter or type checker already enforces, tone and persona instructions, and private values such as sandbox URLs, which belong in a gitignored CLAUDE.local.md instead.
- Why is Claude ignoring my CLAUDE.md?
- Usually one of three reasons. The file is not in a location that loads for your session, which you can check by running the context command and reading the memory files list. The instruction is vague enough to be interpreted away, so write something verifiable instead. Or two instruction files contradict each other, in which case the model may pick one arbitrarily. Anything that must run every time belongs in a hook rather than in prose.
- Should a rule go in CLAUDE.md, a skill, or a hook?
- Put it in CLAUDE.md if it is a fact that is true in every session, such as a build command or a naming convention. Put it in a skill if it is a multi-step procedure that only matters occasionally, because skills load on demand instead of every session. Put it in a hook if it must happen regardless of what the model decides, because hooks run as shell commands at fixed lifecycle events.