Claude Code reads instruction files before it does anything. These files are its "memory" — the standing rules and facts it carries into every session. Get them right and Claude follows your conventions without being told twice. Get them wrong and you either leak personal setup into a shared repo, or you keep repeating yourself in every chat.
I use a few places to store these rules: a user file, a project file, and a local file — plus a separate memory store that Claude writes to on its own. Each has a different job, and only some of them belong in git. This post is how I split them, with real examples from my own machine.
The layers, from broad to narrow
Claude Code loads memory from several files and stacks them together. From the widest reach to the most specific:
- Managed policy — placed by an organization admin. On Linux it lives at
/etc/claude-code/CLAUDE.md; on a Mac, under/Library/Application Support/ClaudeCode/CLAUDE.md. It applies to everyone in the company and cannot be overridden. If you work solo, you'll never see it. - User memory —
~/.claude/CLAUDE.md. Rules for you, across every project on your machine. - Project memory —
./CLAUDE.mdat the repo root. Rules for this codebase, shared with the whole team. - Project-local memory —
./CLAUDE.local.md. Rules for you, on this project only. Not shared.
Think of it as a stack. Broad rules go in first, and narrower files layer on top. When two files disagree, the more specific one usually wins — a project rule overrides a personal habit, and managed policy overrides everything.
How Claude finds these files
Loading isn't limited to your current folder. Two things happen:
It walks up the tree. When you launch Claude Code, it reads CLAUDE.md from
your working folder and every parent folder above it, up to the filesystem root.
So in a monorepo, a CLAUDE.md at the repo root applies even when you start Claude
inside apps/web.
It reads down on demand. When Claude later opens a file in a subfolder, it
picks up any CLAUDE.md sitting there too — but only when it actually goes into
that folder. This keeps the startup context small: a rule that only matters for
src/payments/ doesn't cost you anything until Claude touches that code.
my-monorepo/
├── CLAUDE.md # loaded always (repo-wide rules)
├── apps/
│ └── web/
│ └── CLAUDE.md # loaded when you work in apps/web
└── packages/
└── ui/
└── CLAUDE.md # loaded only when Claude opens packages/ui files
In a big monorepo you sometimes want to skip an ancestor file — say, another
team's rules. You can list those paths under claudeMdExcludes in
.claude/settings.local.json so they don't load.
Which file goes in git?
This is the part people get wrong. Here is the simple rule:
| File | Commit it? | Who it's for |
|---|---|---|
~/.claude/CLAUDE.md |
No — it lives outside the repo | You, everywhere |
~/.claude/settings.json |
No — outside the repo | You, everywhere |
./CLAUDE.md |
Yes | The whole team |
./CLAUDE.local.md |
No — gitignore it | You, this repo only |
.claude/settings.json |
Yes | The whole team |
.claude/settings.local.json |
No — gitignore it | You, this repo only |
The pattern is easy to remember: anything with .local. in the name, or
anything under your home folder, stays off git. Everything else is shared.
The reason to commit ./CLAUDE.md is the same reason you commit a README: a new
teammate — or you, on a fresh laptop — should pick up the project's conventions for
free. The reason to keep the local file out is that it holds your quirks: a
scratch URL, a path that only exists on your machine. Those would only confuse
everyone else.
What kind of rule goes in each file
The files aren't just about where rules live. Each one holds a different kind of rule. Mixing them up is what makes a memory file bloated and hard to trust.
User file — how you like to work
My ~/.claude/CLAUDE.md is pure behavior. It says nothing about any one project.
A few of the rules I keep there:
## Response Style
- Use plain language. Keep sentences short — one idea each.
- No jargon. If a technical term is needed, explain it in plain words.
## Git Commits and Pushes
- Never auto-commit or auto-push. Only when the current message asks for it.
- Approval does not carry forward from a previous message.
## Commit Message Format
- Always prefix the commit message with the ticket number from the branch name.
These follow me into every repo. They're about my taste and my safety habits, not about any codebase. That's exactly what the user file is for:
- Writing style and tone you always want.
- Safety rules — "never push unless I ask."
- Tools you use everywhere —
pnpmovernpm, say. - Your personal git conventions.
A word of warning: keep this file lean. It loads into every session for every project, so a wall of niche rules here is pure tax on your context. If a rule only matters for one repo, it doesn't belong in the user file.
Project file — how this codebase works
The CLAUDE.md in this blog's repo is the opposite. It says nothing about me and
everything about the project:
## Commands
npm run dev # dev server on http://localhost:3000
npm run seed # wipe + load sample data — REQUIRES MONGODB_URI
## Architecture — the important parts
- SSR pages call lib/articles.js and hit Mongo directly.
- Client components call the same-origin /api/* route handlers.
- Auth is a JWT in an httpOnly cookie.
This is the stuff every developer needs and nobody should have to rediscover:
- Build, run, and test commands.
- The architecture and folder layout.
- Team coding standards and naming rules.
- Gotchas unique to the codebase — the "don't do X, it breaks Y" traps.
A good test: if the rule would still be true for a teammate on a fresh clone, it belongs here. If it depends on your laptop, it doesn't.
Local file — your setup for this repo
The local file is for rules that are tied to this project but only make sense on your machine:
# CLAUDE.local.md — not committed
- My local API runs on port 4000, not the default 3000.
- Use the seed data in ~/scratch/blog-fixtures.json for testing.
- Skip the Docker step — I run Mongo natively.
Sandbox URLs, machine-specific paths, personal test data, a debugging trick you use but the team doesn't need. None of it belongs in the shared file, but all of it is worth remembering between sessions.
CLAUDE.local.md vs @import: two ways to keep it personal
There's a second way to hold personal rules, and it's worth knowing because people assume it replaces the local file. It doesn't — they solve slightly different problems.
Any CLAUDE.md can pull in another file with an @ reference:
# Personal setup for this repo
@~/.claude/my-blog-preferences.md
That line tells Claude to load your home-folder preferences file as part of the
project memory. Because the file lives in your home folder, it's shared across
every git worktree of the same repo — handy if you keep several checkouts of one
project side by side. Imports can nest a few levels deep before Claude stops
following them, and the @ syntax is ignored inside code blocks (which is why the
examples in this very article don't trigger anything).
One safety note: if a shared CLAUDE.md imports a file from outside the repo,
Claude Code asks you to approve it the first time. That stops a committed file from
quietly pulling in something off your machine.
So which do you use?
CLAUDE.local.md— self-contained personal rules for one repo. Simplest choice. Just remember to gitignore it.@~/.claude/...import — personal rules you want shared across every worktree, or reused across several projects.
Neither is deprecated. Pick by whether the rules stay in one place or travel with you.
Splitting big files with imports and rules folders
Two more tools help once a single CLAUDE.md gets unwieldy.
Imports for structure. You can break one long file into topic files and pull them in:
# Project memory
@docs/architecture.md
@docs/testing.md
@docs/deploy.md
This is about readability, not saving context — imported files still load at startup. Keeping the main file to a short table of contents just makes it easier to maintain.
Rules folders for scope. For a large codebase you can put topic rules under
.claude/rules/ and tag each with the paths it applies to. A rule with a paths
header only loads when Claude touches a matching file:
---
paths:
- "src/api/**/*.ts"
---
# API rules
- Every endpoint validates its input.
- Use the shared error-response helper.
Now the API rules stay out of the way until Claude actually opens an API file. It's
the same "load it only when it's relevant" idea as subfolder CLAUDE.md files,
just more deliberate.
Memory is not the same as settings
One distinction trips people up constantly. CLAUDE.md and .claude/settings.json
look similar and sit near each other, but they do different jobs.
CLAUDE.mdis guidance. It's handed to Claude as context. Claude tries to follow it, but it's advice, not a fence. A distracted model can miss it.settings.jsonis enforcement. It's read by the tool itself. Permissions, hooks, and environment variables here apply no matter what Claude decides.
A neat example from my own setup: my user file says "never add a Claude Code
attribution footer to commits." That's the guidance. My settings.json backs it
up with hard config:
{
"attribution": { "commit": "", "pr": "" },
"includeCoAuthoredBy": false
}
The rule in CLAUDE.md covers the free text Claude writes by hand; the setting
strips the footer the tool would add on its own. Belt and suspenders. Use
CLAUDE.md for "how we work" and settings.json for "what's allowed."
Settings follow the same commit rule as memory, and they resolve in a clear order when they conflict — managed policy first, then command-line flags, then your local project file, then the shared project file, then your user settings:
managed > --settings flag > .claude/settings.local.json
> .claude/settings.json > ~/.claude/settings.json
So a settings.local.json on your machine beats the committed team file, but
nothing beats managed policy.
The memory Claude writes for itself
There's one more store worth knowing about, and it's not a file you author. Claude Code can keep its own notes about a project — things it learned that aren't in the code: a preference you stated, a correction you made, a deadline you mentioned.
These live outside the repo, under your Claude config folder, one short file per fact with a little header describing it, plus an index file. Claude writes and reads them on its own; you don't commit them, and they don't sync between machines.
How this differs from CLAUDE.md:
CLAUDE.mdis you telling Claude the rules, up front, on purpose.- Auto memory is Claude recording what it picked up, as it goes.
They complement each other. If you find yourself giving the same correction twice,
that's a sign it should graduate from a passing comment into an explicit line in
CLAUDE.md, where you control it and your team sees it.
Three ways to add memory
You don't have to hand-write every file:
/init— run it in a new repo and Claude reads the codebase, then drafts a startingCLAUDE.mdwith the build commands and conventions it found. A solid first draft to trim down.- The
#shortcut — start any message with#and Claude saves that line to memory, asking which file to put it in. Good for capturing a rule the moment you think of it. /memory— opens the memory files in your editor for a direct edit when you want to reorganize.
The gotcha that bit me: gitignore your local files
Here's the catch. Creating a CLAUDE.local.md or a .claude/settings.local.json
does not automatically keep it out of git. The .local. in the name is a
convention Claude Code understands — it means nothing to git.
I checked this repo's .gitignore while writing this post and found it mentions no
Claude files at all. So if I dropped a CLAUDE.local.md full of local paths in
there, git add . would happily stage it, and my machine's quirks would land in
everyone's clone.
Fix it once, up front. Add these lines to .gitignore:
CLAUDE.local.md
.claude/settings.local.json
Then your personal files stay personal and your shared files stay shared, exactly
as intended. Note that you do still commit .claude/settings.json and any
.claude/rules/ — only the .local. file is ignored.
My layout, in one picture
Here's how it all sits together on my machine:
~/.claude/CLAUDE.md # user rules — my style, my git habits (not in any repo)
~/.claude/settings.json # user config — permissions, hooks, attribution off
repo/CLAUDE.md # project rules — commands, architecture (committed)
repo/.claude/settings.json # project config — shared (committed)
repo/.claude/rules/ # path-scoped rules — shared (committed)
repo/CLAUDE.local.md # my rules for this repo (gitignored)
repo/.claude/settings.local.json # my config for this repo (gitignored)
# and, outside the repo, Claude's own per-project notes it writes and reads itself
A checklist to steal
- Put your personal style and safety rules in
~/.claude/CLAUDE.md. Never commit it. - Put shared build commands, architecture, and conventions in
./CLAUDE.md. Commit it. - Put machine-specific paths and scratch data in
./CLAUDE.local.md. Gitignore it. - Use
@~/.claude/...imports when you want personal rules shared across worktrees. - Split a bloated file with
@imports, or scope rules with.claude/rules/. - Reach for
settings.json, notCLAUDE.md, when you need real enforcement. - Add
CLAUDE.local.mdand.claude/settings.local.jsonto.gitignoretoday.
Keep each file short and specific. Say why, not just what — a rule with a reason survives longer than a bare command. Review them now and then and delete rules that have gone stale. Do that, and Claude shows up to every session already knowing how you work.
Comments
Be the first to comment.