ctrlship
ctrlship

CLAUDE.md - how to set up project instructions for Claude Code

| 11 min read

My CLAUDE.md is 216 lines long. It references 13 separate rule files. It has a section called “How to Work With Mateusz” that includes gems like “Do not soften truth” and “When he says change X, change exactly X. Not Y. Not X and also improved Z. Just X.”

I also have a file called lessons-learned.md where Claude automatically logs every mistake it makes. There are 20+ entries. One of them is about a hover color where Claude guessed #404040 instead of looking up the actual token. The real value was #737373. Darker instead of lighter. Claude was too lazy to check, and I was too trusting to notice until it looked wrong.

This is what happens when you take CLAUDE.md seriously. You end up building a relationship with a tool that forgets you exist every time you close the terminal.

The week the internet said to delete it

A study from ETH Zurich tested four coding agents on hundreds of real tasks. With instruction files and without.

/init makes things worse. Agents using auto-generated files solved fewer problems and cost 20% more. The generated content just repeated what the agent could already see by reading the code. You’re paying Claude to read the same information twice.

Human-written files improved results by about 4%, but also cost more tokens. That 4% only happens if the file contains things Claude wouldn’t figure out on its own.

Theo Browne tweeted “delete your CLAUDE.md” to half a million people. Addy Osmani wrote that you should stop using /init. Reddit piled on. For about 48 hours, instruction files were dead.

Nobody talked about what the study tested. They ran auto-generated boilerplate against coding benchmarks on unfamiliar projects. No lessons-learned file. No workflow rules. No “scan every hover state before writing CSS” born from Claude guessing a color wrong. They tested the worst version of these files and found it doesn’t work. That’s what happens when you write too much, too vague, and for problems you don’t have yet.

Vercel tested the opposite. They put documentation for APIs that didn’t exist during training into an instruction file. Pass rate went from 53% to 100%. When the file contains things the agent can’t discover on its own, it works.

If your CLAUDE.md repeats what’s in package.json and your folder structure, delete it. If it contains your preferences, your conventions, and the mistakes Claude made last week, keep it.

What is CLAUDE.md

It’s a text file in your project. Claude reads it at the start of every session. Whatever you write there becomes part of how it thinks about your code.

I have terrible memory. Names, conversations, where I left things. So when I say Claude forgets everything the moment you close the terminal, I mean I get it on a personal level. The difference is I can drink coffee. Claude just gets a fresh CLAUDE.md.

Without it, every session starts with Claude poking around your files trying to figure out what’s going on. With it, Claude starts with the right context: what matters, what to avoid, how you want things done. Instead of six rounds of “no, not like that,” you get two.

Where to put it

I didn’t know this at first, but Claude reads instructions from two places.

There’s a personal file at ~/.claude/CLAUDE.md. Whatever you put there applies to every project you open. I use mine for coding philosophy, commit conventions, which AI models to use for which tasks. The stuff that’s true regardless of what I’m building. Things like “no emojis in code” and “always use conventional commits” go here because I want them everywhere.

Then there’s the project file. Drop a CLAUDE.md in your main project folder (or inside .claude/ if you want a cleaner root). This one is for the team. Commit it. Everyone who opens the project gets the same instructions. This blog has one that says “Lighthouse target: 100/100/100/100. 99 is not acceptable.” My other projects have completely different rules.

The two layers stack. Personal preferences load first, project rules load on top. If they disagree, the project wins. Your preference is yours, but the project has the final word.

There’s also CLAUDE.local.md for stuff you don’t want to commit. Local dev URLs, test credentials, personal shortcuts. It gets gitignored automatically.

I discovered the layering by accident. I kept copy-pasting the same rules into every project’s CLAUDE.md. Then I realized the personal file existed and moved all the shared stuff there. Now I write my preferences once and they follow me everywhere. Project-specific stuff stays project-specific. No more duplicate rules drifting out of sync.

What to put in it

Start with whatever you keep repeating. If you’ve told Claude the same thing three sessions in a row, that thing belongs in CLAUDE.md.

The fastest way is just telling Claude what to remember:

add to CLAUDE.md that this is an Astro 5 blog with MDX,
Tailwind CSS 4, deployed on Vercel

Claude writes the entry for you. You don’t have to think about formatting or structure. Next time you correct something:

add to CLAUDE.md: always use conventional commits.
feat: for features, fix: for bugs, refactor: for cleanup

And when you catch Claude doing something wrong:

add a rule to CLAUDE.md: no emojis in code or content.
also add: Lighthouse target is 100/100/100/100, 99 is not acceptable

The file writes itself from your corrections. Every time Claude does something you don’t like, you tell it to remember. After a few sessions, you have a CLAUDE.md that reflects how you work. Not a template you copied from someone’s blog post.

You can also put in architecture notes, folder structure, how things connect. I’d wait. Add those when Claude gets confused about where something lives. If it never gets confused, you saved yourself the context cost.

Let Claude write the first draft

If you want a starting point, Claude Code has a command:

/init

It scans your project and generates a CLAUDE.md based on what it finds. As I mentioned above, the ETH Zurich study found this actually makes things worse. /init writes down things Claude can already see by reading your code. Your folder structure, your tech stack, your build commands. Claude reads the same facts twice and you pay for both.

If you use /init, gut it. Delete everything Claude can figure out on its own. What survives is probably two paragraphs. Or skip it entirely and build the file from corrections, like I described above.

Splitting rules into separate files

One file works fine until it doesn’t. My CLAUDE.md hit maybe 400 lines and I noticed Claude was ignoring stuff near the bottom. Rules I’d written carefully were having zero effect. So I told it:

split CLAUDE.md into separate rule files under .claude/rules/

My personal ~/.claude/rules/ directory has 13 files. There’s bulletproof.md with platform-specific coding rules for React, React Native, and Swift. There’s security.md that blocks Claude from committing API keys (I wrote about why that matters). There’s anti-drift.md that prevents AI agents from going off-script during complex tasks. And there’s lessons-learned.md, which is basically a diary of Claude’s failures.

You don’t need 13 files. You probably need two or three. But knowing you can split things up means you won’t end up with one massive file that’s impossible to maintain.

Claude doesn’t read like you do. It pays the most attention to the beginning and end of its context, and the least to the middle. Long files mean rules get lost in that dead zone. Shorter, focused files mean every rule lands.

The lessons-learned file

I have a file called lessons-learned.md with a simple rule at the top: every time Claude makes a mistake, it has to log it. Automatically. No asking. The format is strict: date, project, what went wrong, why, what was fixed, and a new rule to prevent it from happening again.

A real entry from the file:

### [2026-02-19] NEVER guess CSS values - scan tokens from Design System
- Project: Shift
- Error: Hover on Button/Secondary set to #404040 (darker than default #525252).
Correct hover is #737373 (lighter). I guessed instead of reading tokens
- Cause: Laziness. Didn't query Figma API for hover variant. Made up #404040
- Fix: Read tokens. Applied correct values
- Rule: BEFORE writing ANY CSS value for a DS component, scan ALL states.
NEVER guess hover/focus/active values. ALWAYS scan

Claude literally wrote “Cause: Laziness” about itself. And that new rule at the bottom? It gets checked before every coding session. So the next time Claude works with design tokens, it reads that entry first and knows not to guess.

After 20 entries, I review them. Patterns that keep repeating get promoted to permanent rules in bulletproof.md. The rest stay as a reference. So it gets better over time. Not on its own. Because I built the system that forces it. The downside? All those rules and self-checks mean Claude burns through tokens the way I burn through creatine. Every session starts with it reading its own diary before it even looks at my code.

Other things worth knowing

There’s a feature where rules only apply to specific files. You put a paths field in the frontmatter and the rule kicks in only when Claude touches matching files. I don’t use it much, but if your frontend and backend have different conventions, it’s useful. CLAUDE.md also supports @ imports for pulling content from other files into your instructions. The official docs cover the syntax for both.

Auto memory is separate. You can just say:

remember that we use pnpm, not npm

Claude saves it to a memory file that loads every session. For this blog, Claude’s auto memory includes things like the background color change from pure black to #121212, why it was changed, and that every heading uses a specific CSS variable. Small things I’d otherwise repeat every session. Run /memory to see what it’s saved and fix anything wrong or outdated.

All of this eats context. Everything in CLAUDE.md, every rule file, every memory entry takes up space in the same window Claude needs for reading your code, thinking, and generating a response. My setup loads around 200 lines of CLAUDE.md plus 13 rule files. That’s a lot of tokens burned before Claude even looks at what I’m building. On a Max plan, I notice it. Sessions with large files get slower. Complex refactors hit the ceiling faster.

A hundred well-chosen lines beat three hundred generic ones. If a rule doesn’t save you time at least once a week, it probably doesn’t belong there. I’ve pruned mine twice already and I’ll do it again. The official documentation covers the technical details if you want the full picture.

The mistakes I made

Too much too soon. My first CLAUDE.md was 400 lines of everything I could think of. Claude read all of it but most of it was noise. Whatever you keep repeating to Claude belongs in CLAUDE.md. Everything else was wasting context.

Too vague. “Use 2-space indentation, no semicolons, prefer const over let” is useful. “Write clean code” is not. Be specific or don’t bother.

Never updating it. Projects change. CLAUDE.md doesn’t update itself. And then you sit there debugging rules you wrote two months ago wondering why Claude keeps doing something you stopped wanting.

Putting secrets in it. Don’t. No API keys, no passwords, no connection strings. CLAUDE.md is a text file. If you commit it (and you should for team projects), everyone sees it. Use environment variables for secrets.

Writing rules for problems I didn’t have yet. I added error handling rules before I’d encountered a single error. Accessibility rules before I had a single clickable button on the page. Those rules sat there eating context for weeks before they became relevant. Write rules when you need them, not when you think you might need them someday. This is the hardest one to follow because it feels productive to write rules. It’s not.

Start small, then let it grow from pain

Create a CLAUDE.md in your main project folder. Put in your stack, your main commands, and three conventions you care about.

Then pay attention. The next time you catch yourself explaining the same thing to Claude twice, stop explaining and write it down instead. Your CLAUDE.md grows from actual pain, not from anticipating pain.

My setup is extreme. Yours doesn’t have to be. But somewhere between “no CLAUDE.md” and “13 rule files with a self-improvement loop,” there’s a version that fits how you work. You’ll find it by using Claude, getting annoyed, and writing one more rule.

The annoying part is that it works. Claude with good instructions wastes less of your time. And the only way to write them is to watch Claude guess a hover color wrong and think, I should have told it.