Claude Code Memory: How Persistent Context Actually Works

Priya SharmaPriya SharmaAI Engineering Lead
Chen WeiChen WeiAI Research Writer
10 min read

I came back to a side project after two weeks away and watched claude code memory do its thing for the first time. A small Django app, nothing serious, half a migration script and a half-formed auth flow. I typed claude into the terminal the way you flip on a kitchen light at 2am, not really expecting much.

The first thing the agent said was, "Picking up where we left off. Remember, we agreed not to mock the database in integration tests. Want me to finish the pytest-postgresql fixture for the new user model, or look at the auth flow first?"

I actually sat back in my chair. I had not told it anything. No pasted notes, no re-uploaded CLAUDE.md, no "here is what we were working on." It just knew. Or rather, something on disk knew, and it had whispered the right sentence into the agent's ear before I even hit enter. That something is the memory system, and once you understand how it works under the hood, it stops feeling like magic and starts feeling like what it actually is: a notebook kept by someone who has been quietly listening the whole time.

This post is the deep dive I wish I had read six months ago. Claude code memory is the persistent context system that makes the agent feel less like a stateless chatbot and more like a colleague who remembers. We will show you where the files live, what gets saved, what does not, how the # shortcut works, and how the system goes stale. Priya wrote the parts that feel like a relationship. Chen wrote the parts where we stare at file trees.

What the Memory System Actually Is

The mental model most people start with is wrong. They think of claude code memory as a bigger context window, or a RAG database, or a secret conversation log that Anthropic is keeping somewhere. It is none of those.

It is a folder of markdown files on your laptop.

That is the whole thing. When a Claude Code session starts, the client looks for a memory directory scoped to your current project, reads the index, and injects relevant entries into the agent's context before your first prompt runs. No server round-trip for retrieval. No vector search. Just files, read in order, pasted in at the top of the conversation like a barista glancing at the regulars board before the morning rush.

This is philosophically aligned with how the rest of Claude Code works. The official Claude Code documentation describes memory as "persistent project knowledge Claude can reference across sessions." Anthropic's engineering posts on Claude Code are pretty clear that the tool prefers dumb, legible, grep-able storage over clever indexes. Your memory is yours. Read it, edit it, delete it, commit it to git, or rm -rf the whole directory if the agent gets weird.

The thing that makes it feel like more than a folder of files is the auto-save behavior. Claude watches the conversation for durable facts, patterns, preferences, rules, and offers to save them. Sometimes it just saves them. We will get to when that is helpful and when it is annoying.

The File Structure (Chen's Turn)

Here is what the tree actually looks like on disk. This is the memory folder for a real project I worked on last month, sanitized.

~/.claude/projects/
└── -Users-chen-work-payments-service/
    └── memory/
        ├── MEMORY.md
        ├── database-testing-rule.md
        ├── stripe-webhook-idempotency.md
        ├── user-prefers-pnpm.md
        ├── deploy-runbook-reference.md
        └── pr-review-style-feedback.md

A few things worth noticing.

The project slug is the absolute path with slashes replaced by hyphens. That is how Claude Code disambiguates memory between your forty side projects without a registry. Move the project, you start fresh. Symlink it, same story. Annoying if you rename folders often. Great because memory never leaks across unrelated repos.

Every memory is a single .md file. No database. No binary blobs. No encryption. On a shared machine, anything sensitive the agent has memorized is readable by anyone with access to your home directory. This matters, and we will come back to it.

MEMORY.md is the index. It is the first thing the agent reads at session start, and it is the map that decides which other memory files get pulled in. Here is an excerpt from the same project.

markdown
# Payments Service Memory
 
## User Preferences
- [pnpm not npm](user-prefers-pnpm.md) - always use pnpm for installs
 
## Feedback
- [database-testing-rule](database-testing-rule.md) - no mocking the DB in integration tests
- [pr-review-style](pr-review-style-feedback.md) - review comments should be suggestions, not assertions
 
## Project Rules
- [stripe-webhook-idempotency](stripe-webhook-idempotency.md) - all webhook handlers must be idempotent
 
## Reference
- [deploy-runbook](deploy-runbook-reference.md) - link to internal runbook for prod deploys

The index is itself a markdown file the agent can update. It is not magic metadata. It is just a list with links, which is exactly the kind of structure both humans and LLMs parse well. When you open MEMORY.md in your editor and delete a line, that memory stops getting loaded. When you add a line pointing to a file that does not exist yet, the agent will notice and offer to create it.

Loading order matters a little. MEMORY.md is read first, and its section headers act as soft priors: memories under "Non-negotiable Rules" get more weight than ones under "Reference." Invent your own categories if you want, but write them in the imperative voice the agent already understands.

The Four Memory Types

Every memory file has YAML frontmatter with three fields: name, description, and type. The type is a small enum that tells the agent what kind of thing it is reading. There are four.

User memories capture personal preferences that apply across the project. Tooling choices, shortcuts, ergonomic habits. A real one from my machine:

markdown
---
name: user-prefers-pnpm
description: User uses pnpm exclusively, never npm or yarn
type: user
---
 
Chen uses pnpm for all package management in this project. Do not suggest
npm install. Do not run npm commands. Lockfile is pnpm-lock.yaml and it is
checked in. If you need to add a dependency, use pnpm add.

User memories are the least controversial to save. They are stable. They reflect how I work, not what the project needs.

Feedback memories are the most interesting. They are the lessons the agent learned from being wrong, or from being corrected. They have the shape of a note-to-self written in the voice of someone who just got yelled at. The one Priya's agent remembered in the opening of this post looked like this:

markdown
---
name: database-testing-rule
description: User prefers integration tests that hit real DB over mocks
type: feedback
---
 
User corrected me in the auth migration PR: do not mock the database
in integration tests. Reason: prior incident where mock/prod divergence
masked a broken migration. Use pytest-postgresql fixtures instead.

Notice the prose is reflective, not prescriptive. This is on purpose. Feedback memories work best when they carry the why, not just the what. The agent can reason about "avoid mocks because a mock once hid a real migration bug" in more situations than it can reason about "no mocks."

Project memories describe invariants of the codebase itself. Not opinions. Facts.

markdown
---
name: stripe-webhook-idempotency
description: All webhook handlers must be idempotent
type: project
---
 
All Stripe webhook handlers in src/webhooks/ must be idempotent. They key
off stripe_event_id which is stored in the processed_events table. Duplicate
events are logged and dropped. Never add a webhook handler without this check.

If a rule would still be true if a different developer inherited the repo, it is probably a project memory.

Reference memories are pointers outward. Links to internal docs, runbooks, third-party APIs, architecture diagrams. The agent does not try to memorize the content; it memorizes where to look.

markdown
---
name: deploy-runbook-reference
description: Internal deploy runbook lives in Notion, not repo
type: reference
---
 
Production deploys: see Notion page "Payments Deploy Runbook" under
Engineering > Runbooks. Do not suggest deploy steps from memory. Always
ask user to consult the runbook, or paste its contents into the session.

Types are not validator-enforced. Mis-type a memory and nothing breaks. But the agent reads the type and behaves differently. A feedback memory becomes a lesson learned. A project memory becomes a rule. Get this wrong and you will wonder why the agent keeps second-guessing something you thought was locked in.

The # Shortcut and the Auto-Save Flow

The single most important keyboard combination in Claude Code, in my opinion, is #.

Type # at the start of a message, followed by a fact, and the agent will save it as a memory. Like so.

# always run prettier before committing, config is in .prettierrc.cjs

The agent responds with "Saved as user memory: prettier-before-commit," and you move on. No modal. No file picker. No confirm dialog. The friction is deliberately near zero, because memory that requires ceremony to save is memory you will not save.

Underneath, the # shortcut does three things. It classifies the note into one of the four types, generates a slug for the filename, and appends the entry to MEMORY.md under the right heading. You can watch it happen by tailing the directory with ls -la ~/.claude/projects/<slug>/memory/.

There is also an automatic save path. When the agent notices a pattern in conversation, a repeated correction, a strong preference expressed more than once, or an explicit "remember this," it will offer to save a memory. Sometimes it just saves one and tells you. You can configure this behavior in ~/.claude/settings.json under the memory key, and you can turn auto-save off entirely if you find it noisy.

For teams, memory is per-user and lives in the home directory. It does not sync across machines by default. Some people dotfile-sync the ~/.claude/projects/ tree; some people keep memory intentionally local. There is no wrong answer, but a lot of the power of memory comes from it being idiosyncratic to how you work. If we covered more keyboard and workflow shortcuts like this, we did it in 50 Claude Code tips. Go read that after this if you want the full productivity surface.

One more note. The # shortcut does not create duplicates. Save the same fact twice and it updates the existing file rather than making a new one. I find this reassuring. I save things I have already saved all the time.

What Not to Save, and How Memories Go Stale

This is the part where we admit the system has teeth.

A memory is a tax you pay every session forever. Every file loaded at session start eats context window. If you save thirty memories, you are paying thirty memories worth of tokens on every turn, whether they are relevant or not. Napkin math: assume each memory is 150 tokens, thirty memories is 4,500 tokens injected at session start, plus whatever the index contributes. On a 200k context model that is fine. On a long session with heavy file reads, it adds up.

So the first rule of not-saving: do not save anything the agent would have figured out anyway. "Use TypeScript strict mode" is already in your tsconfig. The agent will read tsconfig. You do not need a memory for it.

The second rule: do not save secrets. Ever. API keys, DB passwords, bearer tokens, internal URLs that are meant to be private. The memory directory is plaintext markdown. If your laptop gets compromised, your memory directory is a gift to the attacker. Anthropic has written about this explicitly in their security docs: treat the memory directory like you would treat your shell history.

The third rule is subtler. Do not save anything volatile. "The billing API is currently broken on staging" is a true fact today and a lie next week. Memories do not come with expiration dates. The agent will dutifully remind you that the billing API is broken six months after it was fixed, and you will waste twenty minutes arguing with it about something that was never real. This is how memories go stale.

Staleness is the memory system's quiet failure mode. The agent cannot know that a rule you saved in December is no longer true in March. It trusts the notebook. This is why you want to treat ~/.claude/projects/<project>/memory/ as a living garden, not a permanent archive. Every few weeks, I open MEMORY.md and delete anything that does not still feel load-bearing. Five minutes of weeding beats an hour of debugging a rule that no longer applies.

The Anthropic engineering team's guidance on long-running agents puts it well: "The cost of wrong memory is higher than the cost of missing memory." A forgotten fact you can re-teach. A wrong fact that the agent acts on confidently is harder to catch.

If you are already using CLAUDE.md heavily, there is some overlap to think through. CLAUDE.md is for rules that apply to every contributor to the project. Memory is for rules that apply to you working on the project. We drew the line between these two in the definitive CLAUDE.md guide, and the short version is: if a new hire would need it, put it in CLAUDE.md; if only you need it, put it in memory.

A Real Session Walkthrough

Back to that Django project. Here is what actually happened in the first ten minutes of the session.

The agent opened with the pytest-postgresql suggestion. I said yes, do the migration test. It wrote the test. The test failed, because a model field had changed since the memory was saved. The agent noticed, said "The User model has a new locale column that was not in the fixture. Should I update the fixture, or add a default?"

I said, add a default. The agent paused. Then it asked, "Do you want me to save a memory that new User fields should have DB-level defaults where possible?"

This is the moment memory either becomes useful or becomes noise. I said yes, because that is genuinely how I want this project to evolve, and the rule would apply to future fields too. The agent wrote a new memory file, updated MEMORY.md, and kept going.

Ten minutes later, I was editing a webhook handler. The agent, unprompted, said "Reminder: webhook handlers in this project are expected to be idempotent via the processed_events table. I will add the check." That was a project memory from two weeks ago, loaded silently at session start, firing at exactly the right moment.

I did not have to prompt it. I did not have to remind it. I did not have to paste context. The notebook remembered. It remembered because at some point in the past I had said "remember this" and the agent had written it down in a place both of us could find again.

What I want you to notice in this little story is how unremarkable the memory system feels when it works. There is no UI flourish, no "here is what I remembered" banner. The memory just shows up in the agent's behavior, the way a good assistant's memory shows up in theirs. You only notice the system when it is wrong.

Closing

Memory is an evolving artifact. It is not a setup step; it is a conversation that happens in the margins of every session, written down so the next version of you does not have to start from scratch.

The trick, if there is one, is to treat claude code memory like a garden and not like a filing cabinet. Plant things. Pull weeds. Rearrange. Delete the things that no longer bloom. The file structure is right there, in plain markdown, in a folder you already have access to. Nobody is going to manage it for you, and honestly, nobody should.

I am still figuring out my own pattern. Chen swears by a monthly pruning ritual. Priya mostly just uses # a lot and occasionally panics and deletes half the directory. Both of us keep coming back to the same quiet observation: the best memories are the ones that came from a moment where we got something wrong, wrote down why, and never had to have the same argument again.

Open your memory folder. See what is in there. Tell us what surprised you.

Stay in the loop. Get weekly tutorials on building software with AI coding agents. Speak to the community of Builders worldwide.

Free forever, no spam. Tutorials, tool reviews, and strategies for founders, PMs, and builders shipping with AI.

Learn More