Back to guides

How Do I Write a CLAUDE.md That Actually Changes Claude's Behavior?

Jake McCluskeyBeginner20 min read
How Do I Write a CLAUDE.md That Actually Changes Claude's Behavior?

Every Claude Code user eventually writes a CLAUDE.md file and most of them are bad. They're either a wall of preferences Claude ignores, or a terse "be concise" that doesn't change anything. A good CLAUDE.md is the difference between "Claude keeps suggesting patterns we don't use" and "Claude already knows how we write code here." Here's how to write one that actually changes behavior on your next run.

Why this matters

CLAUDE.md is the file Claude Code reads on every session start. It gets prepended to Claude's context as an instruction, not a reference document. Which means: every line costs tokens, every line is evaluated, and every line needs to be doing work.

Most CLAUDE.md files are where good intentions go to die. They accumulate like a junk drawer — "also don't do X", "prefer Y", "we discussed Z in Slack last week" — until the file is 400 lines of noise and Claude follows none of it. The trick is treating it like production code: small, tested, revised.

Before you start

You need:

  • Claude Code installed. If you don't have it yet, see How Do I Set Up Claude Code on macOS?.
  • A project you're actively working in. CLAUDE.md without a codebase to constrain is useless.
  • About 30 minutes of honest reflection on which Claude behaviors frustrate you.

Step 1: Find your project's CLAUDE.md

Claude Code looks for CLAUDE.md in three places, in order:

  1. Project root./CLAUDE.md (versioned with your code, shared with your team).
  2. Home directory~/.claude/CLAUDE.md (personal, across all projects).
  3. Parent directories up to the repo root — useful for monorepos.

Create one in your project root if it doesn't exist:

bash
touch CLAUDE.md

Project-root is where 80% of your instructions should live. Home-dir is for your personal preferences that span every project (e.g., "always use U.S. spelling").

Step 2: Start with a list of things Claude keeps getting wrong

Not what you want Claude to do in some idealized sense. What it keeps getting wrong right now.

Open the last three or four Claude Code sessions and scan for corrections you had to make. Things like:

  • "No, we use Tailwind, not CSS modules."
  • "Import from @/lib/foo, not relative paths."
  • "Don't add comments like // fetch user data above fetchUser()."
  • "We never use any — if you can't type it, say so."
  • "Server Components by default; 'use client' only when there's an interactive reason."

Each of those is a CLAUDE.md candidate. Write them down raw.

Step 3: Rewrite each one as a short imperative

Bad: "I would prefer if you could generally lean toward using TypeScript generics when we're dealing with collection operations because it improves type inference..."

Good: "Use generics on collection helpers. Never return any[]."

The rules of thumb:

  • Imperative voice. "Use X." "Don't use Y." Not "I think maybe we should."
  • One rule per line. Compound rules get ignored.
  • Concrete examples. "Use cn() from @/lib/utils" beats "use our class utility."
  • Negative rules beat positive ones. "Never add try/catch unless the error is recoverable" is clearer than "handle errors thoughtfully."

Step 4: Structure the file so Claude can scan it

Claude reads top to bottom and weights earlier content slightly more. Structure:

markdown
# Project: [Name]

One-line description of what this is.

## Stack
- Next.js 16, Tailwind 4, Prisma 7, Postgres
- Deployed on Railway
- Tests: Vitest

## Conventions

### Imports
- Use `@/` path alias, never relative `../`.
- Group: external libs, then `@/`, then same-folder.

### Components
- Server Components by default.
- `'use client'` only for interactivity (state, effects, browser APIs).

### Types
- Never `any`. Use `unknown` and narrow, or generic.
- Prefer interfaces over types for object shapes.

## Running the app
- `npm run dev` — local with Turbopack.
- `npm run build` — production build; must pass before every PR.

## Don't
- Don't add "helpful" comments that restate the code.
- Don't create new files unless they're needed; extend existing ones.
- Don't run destructive git commands without asking.

Sections let Claude grep; short bullets let it comply.

Step 5: Add one "forbidden patterns" section with examples

This is the highest-ROI block. Claude follows "don't do X" most reliably when you show the X.

markdown
## Forbidden patterns

BAD (useless comment):

ts // Get the user const user = await getUser();

text

GOOD:

ts const user = await getUser();

text

BAD (relative import):

ts import { foo } from '../../../lib/foo';

text

GOOD:

ts import { foo } from '@/lib/foo';

text

Three or four examples is enough. Each one saves you from correcting the same thing a hundred times.

Step 6: Test it with a small task

Restart your Claude Code session so it re-reads CLAUDE.md. Then ask Claude to do something that would have tripped your old rules — e.g., "add a helper function that fetches a user's posts" — and watch if it now uses the conventions you specified.

If it doesn't:

  • Your rule may be too vague. Tighten it.
  • Your rule may be buried under noise. Delete 30% of the file.
  • Your rule may contradict something else in the file. Resolve it.

Iterate. The file is code.

Verify it worked

1. Session picks it up. Start a new Claude Code session and type /context — Claude shows what's in its prompt. CLAUDE.md should be visible.

2. Behavior changed. Ask Claude to do three tasks that previously required correction. Count how many you had to correct. Target: zero.

3. File is short. Under 200 lines for most projects. If yours is longer, you're probably documenting things Claude can infer from the codebase. Delete those.

Where this breaks

  • Contradicting existing code. If your CLAUDE.md says "use Tailwind" but half the codebase is in CSS modules, Claude will follow the code it sees, not your file. Align the two before writing rules.
  • Over-specifying style. "Always use 2-space indent" is wasted tokens — Prettier or Biome handles this. Save CLAUDE.md for things formatters can't enforce.
  • Secrets in CLAUDE.md. The file often gets committed to Git. Never put API keys, DB URLs, or anything you wouldn't put in a README.
  • Rules that are actually team debates. "Prefer function declarations over arrow functions." If your team hasn't agreed, Claude is going to pick one side. Settle the debate human-to-human first, then encode it.
  • The "big list of don'ts" that grows forever. Review and prune every month. If a rule hasn't been violated in 60 days, delete it — you solved the problem.

What to try next

Want this built for you instead?

Let's talk about your AI + SEO stack

If you'd rather skip the how-to and have it shipped for you, that's what I do. Start a conversation and we'll figure out the fastest path to results.

Let's Talk
Questions from readers

Frequently asked

Where does Claude Code look for CLAUDE.md?

Project root first, then parent directories up to the repo root, then ~/.claude/CLAUDE.md globally. Keep 80% of your instructions in the project-root file — it's versioned with your code and shared with your team. Use the home-dir file for personal cross-project preferences.

How long should CLAUDE.md be?

Under 200 lines for most projects. Longer than that usually means you're documenting things Claude can infer from the codebase. If you find yourself writing 'we use Prettier with 2-space indent,' delete it — the formatter config already tells Claude that.

Should CLAUDE.md be committed to Git?

Yes. It's part of your project conventions, same as README or .eslintrc. The team benefits when everyone's Claude sessions follow the same rules. Just make sure there are no secrets or private URLs in it — the file is as public as the repo.

Claude keeps ignoring one of my rules. What do I do?

Three usual causes: the rule is too vague ('be concise' means nothing), the rule contradicts what Claude sees in the code, or the rule is buried under other noise. Rewrite as a short imperative with a concrete example, delete 30% of the surrounding file, and restart the session so Claude re-reads the file.

Can I have different CLAUDE.md files for different subprojects?

Yes — in a monorepo, Claude walks up from the file being edited and reads every CLAUDE.md along the way. So apps/web/CLAUDE.md gets merged with the repo-root CLAUDE.md when you're working in the web app. Keep shared rules at the root and app-specific rules in the sub-folder.