teachyou.ai academy
← All posts
Claude CodeCLAUDE.mdAI coding agentsdeveloper toolingprompt engineering

CLAUDE.md and Memory Files in Claude Code

Pramod Dutta · Jul 6, 2026 · 14 min read

If you have used Claude Code for more than a week, you have probably typed /init, watched it generate a CLAUDE.md file, and moved on without thinking much about it. That file is Claude Code memory: the persistent context an agent reads at the start of every session so it does not have to relearn your build commands, your folder layout, or your team's git conventions every single time. Get it right and the agent stops asking questions you already answered last week. Get it wrong and you end up with a 40KB file the model skims past, or worse, one that actively confuses it. This article covers how the memory hierarchy actually works, how to write a CLAUDE.md that earns its place in context, and how to split memory across nested files and imports without turning it into unmaintainable sprawl.

What Claude Code Memory Actually Is

Claude Code memory is plain markdown, not a database and not a vector store. When a session starts, Claude Code walks a known set of locations, reads whatever markdown files it finds there, and concatenates them into the system context before your first message is even processed. There is no embedding step, no retrieval ranking, no "relevant chunk" selection. Everything in a loaded memory file is in context for the entire session, which is exactly why size discipline matters more than people expect.

This matters because it changes how you should think about writing these files. A CLAUDE.md is not documentation you write for a future engineer to skim. It is a system prompt fragment you write for a model that will re-read it, in full, every single turn for the rest of the conversation. Verbose prose, restated obvious facts, and stale instructions all cost tokens on every request, not once at read time.

The practical upshot: treat CLAUDE.md like you would treat any other expensive, always-loaded resource. Write it dense, keep it current, and delete anything that stopped being true.

The Memory File Hierarchy: Project, User, and Local

Claude Code memory resolves across three tiers, and understanding the order matters because later files can override or add to earlier ones rather than replace them wholesale.

Project memory lives at CLAUDE.md in your repository root. This is checked into git, shared with your team, and describes things that are true for anyone working in that codebase: the stack, the test command, the deploy process, naming conventions, gotchas specific to that repo. If you run Claude Code from a subdirectory, it walks upward looking for CLAUDE.md files at each parent level too, so a monorepo can have a root-level file plus package-level files that layer in.

User memory lives at ~/.claude/CLAUDE.md on your machine. This applies across every project you touch and is where personal preferences belong: your commit message style, whether you want em dashes banned from your writing, how you like PRs formatted, standing rules about not adding co-author trailers. Nothing project-specific belongs here because it will load into contexts where it is irrelevant.

Local memory is the third tier: an optional file (commonly named with a .local.md suffix and gitignored) for machine-specific or personal notes on a shared project that you do not want checked into version control. Think local port numbers, personal shortcuts, or scratch notes about a branch you are debugging.

Here is the mental model: user memory sets who you are as an engineer across all your work, project memory sets what this particular codebase needs everyone to know, and local memory sets what only makes sense on your machine right now. When all three exist, Claude Code loads all of them, and more specific, more local information should win in practice because it is more relevant to the immediate task, not because of some formal precedence rule you need to memorize.

A quick way to see what is actually being loaded for a given session:

ls ~/.claude/CLAUDE.md
find . -maxdepth 3 -name "CLAUDE.md"

Run that from your project root before you assume the agent knows something. If the file is not there, it was never in context, no matter how confident the agent sounds.

Writing an Effective CLAUDE.md

The single biggest mistake in Claude Code memory files is writing them like a README. A README explains a project to a human meeting it for the first time, with prose, motivation, and background. A CLAUDE.md should read more like a terse ops runbook: commands, constraints, and exceptions, nothing else.

A structure that works well in practice:

## Stack
Next.js 15, Postgres via Neon, Clerk for auth, deployed on Vercel.

## Commands
- Dev server: npm run dev (port 3000)
- Tests: npm test
- Typecheck: npm run typecheck
- Lint: npm run lint -- --fix

## Conventions
- Server actions go in app/actions/, never in API routes.
- All Postgres queries go through db/queries.ts, no raw SQL in components.
- Commit messages: no Co-Authored-By trailers, no emoji.

## Gotchas
- The staging DB has a different schema version than prod; check migrations/ before assuming a column exists.
- Stripe webhooks are gated behind a feature flag until keys are rotated.

Notice what is missing: no explanation of what Next.js is, no history of why the team picked Postgres, no paragraph about the company mission. An agent does not need onboarding narrative, it needs facts it can act on immediately. If a fact would not change what the agent does on its next tool call, it does not belong in memory.

Two more things that consistently pay off:

Point to skills and scripts instead of re-explaining them. If your team has a scripts/deploy.sh or a Claude Code skill that already encodes a workflow, reference it by path and one line of context rather than duplicating the steps in CLAUDE.md. Duplication drifts: the script changes, the memory file does not, and now the agent is following stale instructions with total confidence.

Say what not to do, not just what to do. Agents default to reasonable-sounding behavior that is sometimes wrong for your specific repo: adding a co-author trailer to commits, creating a new file when an existing one should be edited, running git push --force because it seemed efficient. A line like "never amend commits, always create new ones" prevents a whole category of unwanted actions more reliably than hoping the agent infers your preference.

Nested CLAUDE.md Files for Monorepos

In a monorepo, a single root CLAUDE.md forces every subproject's rules into a file that loads for all of them, even when someone is working entirely inside one package. Claude Code supports nested memory: a CLAUDE.md inside apps/web/ or packages/api/ loads in addition to the root file once you are working inside that directory, so you can keep root-level facts (workspace layout, shared tooling, monorepo-wide conventions) separate from package-specific facts (that package's test command, its internal module boundaries, its own gotchas).

A layout that scales:

/CLAUDE.md                  -> workspace overview, shared commands, cross-cutting rules
/apps/web/CLAUDE.md         -> web app specifics: routing, component conventions
/apps/api/CLAUDE.md         -> API specifics: auth middleware, request validation
/packages/ui/CLAUDE.md      -> design system rules, storybook commands

The root file should stay short and mostly point outward: "the web app lives in apps/web, read its CLAUDE.md before touching anything there" is a real pattern worth using, and it is exactly what a well-structured workspace root file should say. This keeps the root file a map rather than a merged dump of every subproject's rules, and it means someone working only in packages/ui never pays the token cost of loading API-specific gotchas they will never touch.

The /init and /memory Commands

Two built-in commands cover the everyday memory workflow.

/init scans the current project and generates a starting CLAUDE.md by inspecting the codebase: package manifests, existing config files, folder structure, common commands it can infer from scripts. It is a fast way to bootstrap a new project's memory file, but treat the output as a draft. Generated files tend to over-describe obvious things (restating what a package.json script does) and under-describe the tribal knowledge that actually matters (why staging behaves differently from prod, which files are auto-generated and should never be hand-edited). Run /init, then spend ten minutes cutting the boilerplate and adding the gotchas a generator cannot know.

/memory opens the relevant memory file directly in your editor so you can add or correct something mid-session without leaving your terminal flow. This is the command to reach for the moment you notice the agent doing something wrong that a one-line rule would fix, rather than correcting it manually every session going forward. If you catch yourself giving the same correction twice, that is the signal to open /memory and write it down once.

Memory Imports: Splitting Large CLAUDE.md Files

As a project grows, a single CLAUDE.md can outgrow what should reasonably load on every turn. Claude Code memory files support an import syntax so you can split content into separate files and pull them in by reference, using an @path/to/file style reference inside the main CLAUDE.md rather than pasting the whole thing inline.

This is useful for content that is large but only occasionally relevant: a full API reference, a detailed migration guide, an exhaustive list of environment variables. Keep the main CLAUDE.md dense with what is needed constantly, and import the long-tail reference material so it is discoverable without being loaded by default in every session.

A rough pattern:

## Reference material
See docs/api-conventions.md for the full endpoint naming reference.
See docs/env-vars.md for the complete list of required environment variables.

Whether these resolve as automatic imports or as pointers the agent reads on demand depends on your Claude Code version, so check your current documentation for the exact import syntax before relying on it. Either way, the design goal is the same: default-loaded memory should be small and load-bearing, and everything else should be one read away rather than always in context.

Auto-Memory vs CLAUDE.md: What's the Difference

Claude Code also supports agent-written memory: notes the agent itself accumulates across sessions about durable facts it has learned, separate from the CLAUDE.md files you author by hand. This typically lives under a memory directory keyed to the project, often organized as an index file plus topic files, so the agent can record something like "this project uses Neon for Postgres and Clerk for auth, checkout is gated until payment keys are live" once, and have it available in future sessions without you re-explaining it.

The distinction that matters in practice: CLAUDE.md is memory you curate, auto-memory is memory the agent curates. Use CLAUDE.md for standing rules and conventions you want enforced consistently, the kind of thing you would put in a style guide. Use auto-memory as a running record of facts discovered during actual work: what the staging environment looks like today, what got fixed last Tuesday, what a particular flaky test actually needed. Auto-memory drifts less into stale instruction and more into stale fact, so periodically skim it the same way you would periodically clean out CLAUDE.md, since an agent will trust a written note over its own uncertainty even when the note is a year old and no longer accurate.

Common Mistakes That Bloat Context

A few patterns show up repeatedly in memory files that have grown unwieldy:

Restating what tooling already reports. If package.json already lists the dev command, and your CLAUDE.md restates it verbatim, that is one more thing to keep in sync and it added nothing the agent could not get from reading the manifest itself when it needs to.

Narrative history instead of current state. "We used to use X but migrated to Y because Z" is interesting to a human onboarding, and mostly noise to an agent that only needs to know Y is what exists now. If the history matters for a specific decision (do not re-add a dependency that was deliberately removed, for instance), state the constraint, not the story.

Instructions that stopped being true. A gotcha about a bug that got fixed six months ago is worse than no gotcha at all, because the agent will now avoid a workaround that is no longer necessary, or worse, apply a fix for a problem that does not exist. Memory files need the same maintenance discipline as code: when behavior changes, the note describing the old behavior needs to be deleted, not just left to accumulate.

One giant file instead of nested or imported ones. As covered above, a root CLAUDE.md that tries to cover every subproject's rules loads all of it into every session, everywhere in the repo. Split it.

Vague preferences instead of checkable rules. "Write clean code" and "follow best practices" cost tokens and change no behavior, because there is nothing concrete for the agent to act on. "Functions over 50 lines get flagged for a split" or "no default exports" are rules an agent can actually apply.

A Practical CLAUDE.md Template

If you are starting from nothing, this is a reasonable skeleton to fill in and then trim:

# <Project name>

One sentence: what this project is and where it runs.

## Stack
Language/framework, database, auth provider, hosting.

## Commands
- Install:
- Dev server:
- Tests:
- Lint/typecheck:
- Build:

## Structure
- Where business logic lives
- Where UI/components live
- What is generated and should never be hand-edited

## Conventions
- Commit style
- Branch naming
- Testing expectations for new code

## Gotchas
- Anything a new contributor would get wrong on day one

## Do not
- List of specific actions that are off-limits in this repo

Fill in only what you actually know to be true and current. An empty section is better than a guessed one, because a guessed fact in memory is worse than no fact at all: the agent will act on it with full confidence.

FAQ

Does Claude Code memory get sent on every single message, or just once at the start of a session? It loads once at session start and stays in context for the rest of that session, so its token cost is paid up front and then persists across every subsequent turn. This is exactly why keeping it lean matters more than keeping it complete.

Should I commit CLAUDE.md to git? Yes, for project-level memory. It is shared context for anyone working in the repo, human or agent, and should evolve with pull requests like any other project file. Keep genuinely personal or machine-specific notes in a separate local file that is gitignored instead.

What happens if I have conflicting instructions across user, project, and local memory? Treat more specific, more local information as the one that should win in practice: a project's CLAUDE.md speaks with more authority about that project than your general user-level preferences do, and a local override should reflect the most current, most specific truth about your immediate situation. If you notice an actual conflict, resolve it explicitly rather than leaving both statements in place for the agent to reconcile on its own.

How big is too big for a CLAUDE.md file? There is no hard limit enforced by the tool, but the useful question is not "how big" but "how much of this loads value on every single turn." If large sections are only occasionally relevant, split them into separate files and reference them rather than inlining everything into the file that loads by default every session.

Can I use CLAUDE.md to store secrets or API keys? No. Memory files are plain text, frequently committed to git, and read directly into model context, which makes them one of the worst possible places to put anything sensitive. Keep secrets in environment variables or a secrets manager, and at most reference their existence ("Stripe keys are in the deploy environment, never printed or logged") in memory.

Does writing a good CLAUDE.md replace the need for skills or custom commands? No, they solve different problems. CLAUDE.md is passive context the agent always has. Skills and custom commands are active workflows the agent invokes deliberately for a specific recurring job, often with their own tool access and multi-step logic. A CLAUDE.md line like "for blog articles, use the blog-ingest skill" is the right way to connect the two: memory tells the agent a skill exists and when to reach for it, the skill does the actual work.