Skip to content

How to write a CLAUDE.md that actually helps

By SunnyKumar Jonwal 10 min read

Every new Claude Code session starts with no memory of your project. It can read files, but it doesn't know that your team runs tests with a custom script, that the legacy/ folder is off limits, or that you hate default exports. A CLAUDE.md file fixes that. It's a plain Markdown file that the agent loads into context at the start of each session, a standing briefing you write once.

The difference between a useful one and a useless one is large, and it's mostly a matter of restraint. This post covers what to put in, what to keep out, and how to test whether the file is doing its job.

How the file gets used

Claude Code looks for CLAUDE.md in the directory where you start a session and reads it into context before you type anything. You can have more than one. A file in your home directory (~/.claude/CLAUDE.md) holds personal preferences that apply everywhere. A file in the project root holds team conventions, and it's normally committed to version control so everyone shares it. Files in subdirectories load when the agent works in those folders, which suits monorepos where the frontend and backend follow different rules. A file can also import others by path, so a long reference doc can live elsewhere and be pulled in by reference.

Because the content is loaded on every session, it's part of the window every time, and it pays the cost every time. That's the constraint that should shape everything you write. It's the same principle from context engineering: every token in the standing context should earn its place.

The test for every line

Here's a filter that works well. For each line, ask: if I deleted this, would Claude make a mistake it wouldn't otherwise make? If the agent would do the right thing anyway, the line is noise. If it's something it can't learn by reading the code, it belongs.

That rules out a lot of what people write. "Write clean, maintainable code" changes nothing, because the agent already tries. "Use meaningful variable names" is the same. "This is a Laravel application" is obvious from the composer.json it can read in two seconds.

What survives the filter is specific and non-obvious.

What to include

Commands. How to build, run, test, and lint, written exactly as you'd type them. This is the highest-value content. If the test command is composer test -- --filter=Invoice, or the app needs docker compose up -d first, say so. Agents waste many steps guessing at these.

Conventions that differ from defaults. Anything a competent developer wouldn't guess. "We use Pest, not PHPUnit style." "Form requests for all validation, never inline validate()." "Money is stored as integer cents." "Prefer early returns."

A map of the codebase. A few lines on where things live and how they connect, when it isn't obvious from folder names. "Business logic lives in app/Actions, controllers stay thin. Everything under app/Legacy is frozen; don't refactor it."

Gotchas and landmines. The things that have already burned someone. "The staging database is shared, so never run migrate:fresh against it." "Tests hit the real queue unless you use the fake." "The orders table has a trigger that updates total; don't recompute it in PHP."

Workflow rules. Branch naming, commit message format, whether to run the formatter before committing, what a pull request needs.

Environment quirks. Required environment variables (names only, never values), services that must be running, ports.

Boundaries. Files and folders that should never be edited, and actions that need a human first. These complement, and don't replace, real permission settings, which are covered in least privilege for AI agents.

What to leave out

Anything the code already says. Directory listings, dependency lists, and descriptions of what a well-named function does go stale and add nothing.

Long tutorials and style guides. If your style guide is twenty pages, link to it or import the essential page, and don't paste it in. Put the three rules people actually get wrong in CLAUDE.md.

Vague aspirations. "Be careful," "write good tests," "think about edge cases."

Rules a tool can enforce. If a linter or formatter handles it, let the tool do it, ideally through a hook that runs automatically. A line telling the agent to use tabs is weaker than a formatter that fixes it on save. See Claude Code skills, hooks, and subagents.

Secrets. Never put keys, tokens, or passwords in the file. It's committed, shared, and sent to the model.

Your whole README. Duplicating it creates two sources of truth that drift apart.

Keep it short

There's no magic length, but shorter is nearly always better. A file of 60 to 150 lines is plenty for most projects. As it grows, two things happen: it costs more on every session, and important rules get buried among unimportant ones, so the agent follows all of them less reliably.

If a topic needs a lot of detail, such as how the billing system works or the release process, write it up in a separate doc and point to it: "For billing internals, read docs/billing.md before changing anything in app/Billing." The agent will read it when it's relevant and skip it when it isn't, which is just-in-time loading applied to documentation.

Watch for the tendency to add a rule after every mistake. A file that only grows becomes a rule pile. Prune as you add.

Write like you're briefing a teammate

Specific beats general, and verifiable beats subjective. Compare:

  • Vague: "Format code properly."

  • Better: "Run ./vendor/bin/pint before committing; CI fails on unformatted files."

  • Vague: "Handle errors well."

  • Better: "Don't catch generic exceptions. Let domain exceptions bubble to the handler in app/Exceptions/Handler.php, which maps them to responses."

A good rule tells the agent what to do, and usually why. The reason lets it apply the rule to cases you didn't list. "Never run migrations against staging (shared with QA) without asking" generalizes better than "never run migrate on staging."

On emphasis: use it sparingly. If every line is IMPORTANT, none is. Reserve stronger wording for the couple of rules where a violation is costly, and expect that to work better than shouting throughout.

An example for a Laravel project

Here's a compact CLAUDE.md in the spirit of the above. Adapt it, and don't copy it verbatim.

# Project notes

Laravel 12 app for invoicing. PHP 8.3, MySQL, Pest for tests.

## Commands
- Run app: `composer dev`
- Tests: `php artisan test` (single file: `php artisan test tests/Feature/InvoiceTest.php`)
- Format: `./vendor/bin/pint` (CI rejects unformatted code)
- Static analysis: `./vendor/bin/phpstan analyse`

## Structure
- Business logic in `app/Actions/*` (one class, one `handle()` method). Controllers stay thin.
- Validation in Form Requests, never inline.
- `app/Legacy/*` is frozen. Don't refactor it; add adapters instead.

## Conventions
- Money is stored as integer cents. Use the `Money` value object, not floats.
- Every new endpoint needs a Feature test and a policy check.
- Prefer early returns over nested conditionals.

## Gotchas
- The staging DB is shared with QA. Never run `migrate:fresh` or seeders against it.
- Tests use the `sync` queue; don't rely on job timing.
- `orders.total` is maintained by a DB trigger. Don't recompute it in PHP.

## Workflow
- Branch: `feature/<ticket>-short-name`. Commit messages: imperative, under 72 chars.
- Run tests and Pint before every commit.
- For billing internals, read `docs/billing.md` before touching `app/Billing`.

Notice what's missing: no description of Laravel itself, no dependency list, no generic advice. Every line prevents a specific mistake.

Layering and scope

Use each location for the right kind of information.

Your home-directory file is for personal habits: your preferred explanation style, editor quirks, tools you always want used. Keep it short, since it applies to every project.

The project-root file holds shared team knowledge and is reviewed like code. Treat changes to it like changes to any other shared contract, and mention them in pull requests.

Subdirectory files hold rules that only apply locally: "in frontend/, use pnpm, not npm," or "everything in services/payments/ requires two-person review." They keep the root file lean.

If you use several AI coding tools, note that other tools have their own convention files, and a cross-tool AGENTS.md convention has emerged. Check what each tool reads, and avoid maintaining the same text in three places if you can point them at one.

Test it

A CLAUDE.md is a prompt, so treat it like one. After writing or changing it, run a few real tasks and watch what happens. Does the agent use the right test command on the first try? Does it follow the naming convention you specified? Does it avoid the frozen directory?

When it ignores a rule, look at why. Sometimes the file is too long and the rule is lost. Sometimes two rules conflict. Sometimes the rule is vague enough to be read two ways. Sometimes it's phrased as a preference when you meant a requirement. Fix the file, and try again.

You can also ask the agent to review the file itself: "Read CLAUDE.md and tell me which instructions are unclear, redundant, or contradictory." It won't catch everything, and it's a cheap sanity check.

Keep it alive

The most common failure isn't a bad first draft. It's staleness. The test command changes, a folder moves, a convention is dropped, and the file keeps insisting on the old way. Then the agent follows outdated instructions with full confidence.

A few habits prevent that:

  • When the agent makes the same mistake twice, add a line. When a line stops mattering, remove it.
  • Review the file when you review related changes: renamed scripts, moved directories, new tooling.
  • Once a quarter, read it top to bottom and delete anything you can't justify.
  • Keep it in version control, and let history show why a rule exists.

Some people keep a short "learned" section at the bottom for lessons from recent sessions, and periodically fold the good ones into the main body.

Which mechanism for which job

CLAUDE.md isn't the only way to shape the agent, and it isn't always the right one. Use it for standing knowledge that applies to most sessions. Use a skill for a procedure that's needed only sometimes, such as your release process, so it doesn't sit in context otherwise. Use a hook when something must happen every time without exception, like formatting. Use a subagent to isolate noisy work. And use real permissions, not prose, for anything security-sensitive.

Getting that split right keeps CLAUDE.md short, which is what makes it work.

Mistakes that come up again and again

Three patterns are worth calling out by name. The first is the generated dump: /init produces a reasonable draft, and people commit it untouched. It tends to be long and full of things the agent could have discovered on its own. Treat it as raw material and cut it down by half. The second is the contradiction: one line says "always write tests first" and another, added months later, says "skip tests for small fixes." The agent can't satisfy both, so it picks one at random. The third is the wish list, a section of aspirations nobody enforces. If a rule isn't real, delete it, because a file that's only half true teaches the agent to treat the whole thing loosely.

A quick review before you commit it

Read your file once more with a skeptical eye and check that it's short enough to skim in a minute, that every command runs as written, that nothing in it is obvious from the code, that there are no secrets, and that the two or three highest-stakes rules are impossible to miss. Then start a fresh session and give the agent a real task. What it does next will tell you more than any amount of polishing.