Claude Code · Series · Part 2 of 3

CLAUDE.md: the project memory that Claude reads every time

Khairul Alam·October 2026·~26 min read
Claude CodeCLAUDE.mdMemoryContext window

In Part 1 we installed Claude Code and had a first session. Now the important question: what happens tomorrow? Open a new session and Claude has no idea who you are, what the project is, or the three things you corrected yesterday. This part explains why that happens and how a plain text file solves it.

Part 2 in 60 seconds
  • A model has no memory between conversations. Everything it “knows” must be in the text it is given.
  • CLAUDE.md is a markdown file that Claude Code loads into that text at the start of every session.
  • It can live in your home folder (personal), the project root (shared), or sub-folders (specific).
  • Bigger projects split into a .claude/ folder of rules and skills.
  • /init drafts one for you; /memory opens it for editing.
  • Good files are short, specific and checkable. Bad ones are long, vague or out of date.

1. Why Claude forgets

If you read my earlier post on how AI chat works, this will sound familiar. A language model does not store your conversation. Every time you send a message, the app packs the conversation so far into one block of text, called the context, and hands it to the model. The model can only look at a fixed amount of text at once, the context window.

When you close the session, that block is gone. A new session starts with an empty window. The model is the same, but it is meeting you for the first time.

Without a memory file Monday: you explain build, style, folders… Tuesday: empty window. Explain it all again. With CLAUDE.md Monday: CLAUDE.md loaded, then your task Tuesday: CLAUDE.md loaded, then your task The idea Claude does not remember. The file is put back into the window for it, automatically, at the start of every session. You write it once.
The model forgets by design. CLAUDE.md is how the project's knowledge gets back in front of it.

2. What CLAUDE.md is

It is exactly what the name says: a file called CLAUDE.md, written in markdown (the same simple format as a README). When Claude Code starts, it finds these files and adds their text to the context, ahead of your first message. The model then treats the contents as standing instructions.

The sticky note on the monitor

Imagine a contractor with perfect skills but no memory, who forgets everything overnight. You would not re-brief them every morning. You would leave a note on the monitor: “Build with this command. Tests live here. We use tabs, not spaces. Never edit the generated folder.” CLAUDE.md is that note, and Claude reads it first every day.

It is different from your README.md. A README is written for humans who might contribute. A CLAUDE.md is written for the assistant, and contains the things you would otherwise keep repeating in chat.

3. All the ways Claude “remembers”

Before going further, one important fact: Claude does not remember anything on its own. The model is the same on day 10 as on day 1, and it forgets when a session ends. Everything that looks like memory is Claude Code putting saved text back into the context window. There are only a few ways that happens:

Kind of memoryHow it worksLasts how long
The conversationEverything said so far is re-sent with each message (the context window).One session. /clear or closing it empties the window, though the transcript is saved and can be reopened with /resume.
CLAUDE.md filesLoaded automatically at the start of every session.Permanent: they are files on disk.
Imports and linked notes@docs/file.md loads with CLAUDE.md; a feature or handoff note is read when Claude is told to.Permanent while the file exists.
Settings filesNot knowledge, but saved behaviour: permissions, default model.Permanent.
Resumed sessionsclaude --continue or --resume reloads an old conversation.Brings the old text back, which costs tokens. On Pro and Max, resuming a large session after a long break offers to continue from a summary instead.
Automatic memoryClaude can write notes for itself in a memory folder under ~/.claude/projects/ (see below). Per the official docs it is on by default in local sessions. A MEMORY.md index is loaded at the start of each session; the first 200 lines or 25 KB of it.Permanent until you or Claude edit or delete it. Still saved text read back in, not the model learning.
Files on disk (lasting) • CLAUDE.md (personal, project)• imports and linked notes• handoff and feature notes• settings.json loaded atsession start Context window (temporary) • the notes above• files Claude reads• this conversation• plans from plan mode When the session ends, the right side is wiped.Only what was saved to the left side comes back next time.
Files are the memory. The context window is just where they are shown to Claude.

This is also why plan mode (Part 1) does not remember anything: a plan lives in the conversation, so it is gone with it unless it is saved to a file.

Where Claude Code stores things on your computer

If you open ~/.claude/projects/ in Finder or a file manager, you will find one folder per project. According to the official Manage sessions page, each transcript is saved as ~/.claude/projects/<project>/<session-id>.jsonl, where <project> is your working folder's path with every non-alphanumeric character replaced by -. So /Users/me/work/my-app becomes -Users-me-work-my-app, and a session started in / shows up as a folder called just -. Auto memory lives in a memory folder inside the same project folder, and per the memory docs it is shared by all sub-folders and git worktrees of one repository.

~/.claude/projects/ -Users-me-work-my-app/ 12068aba-….jsonl (one session) d110d31a-….jsonl (another session) memory/ (automatic notes) -Users-me-work-other-app/ … same layout for each project - (sessions started in /) Transcriptsused by --resume Saved memoryreloaded next session
One folder per project. Inside: a transcript per session, and a memory folder of notes.
What you seeWhat it is
-Users-…-my-app/One folder per project path where you ran claude.
<long-id>.jsonlA session transcript: every message and tool call of one conversation, one JSON entry per line. This is what --resume and --continue reopen.
memory/Automatic memory for that project: a MEMORY.md index (one line per memory) plus one small topic file per memory. Only the start of the index is loaded each session; Claude reads the topic files on demand.
Treat these folders as private
  • Transcripts can contain everything you pasted or showed Claude, including code, tokens and customer data. Do not share or commit the folder.
  • You can open memory/ and edit or delete a note. That is how you correct something Claude remembered wrongly.
  • Deleting a project folder removes that project's history and memory, and nothing else. You lose --resume and the saved notes for it.
  • Old transcripts are deleted automatically after a retention period, 30 days by default (the cleanupPeriodDays setting). Memory files are excluded from that clean-up, so they stay until you or Claude edit or delete them.
  • Auto memory is machine-local. It is not shared with teammates or other computers, so anything the team needs belongs in CLAUDE.md, which you commit.

What auto memory saves, and how to control it

The official docs describe four kinds of notes Claude may save for itself:

TypeWhat it holds
userYour role, expertise and working preferences.
feedbackCorrections you gave and approaches you confirmed.
projectOngoing work, deadlines and decisions that cannot be derived from the code or git history.
referenceWhere to find things outside the project, such as a tracker or dashboard.

It is still just text. It helps only if the notes are correct and short, and it takes up context like any other file, so review it from time to time just as you would CLAUDE.md. Details: auto memory in the official docs.

What happens if you say “remember this session”?

By itself, nothing is guaranteed. Saying it does not change the model. Whether anything survives depends on whether Claude actually writes a file:

To check, look for the file write in the session or run git diff. Part 3 shows a better way, an explicit handoff note.

4. Where the file can live

You can have several, and Claude Code combines them. Each location has a different audience:

LocationWho it is forTypical content
~/.claude/CLAUDE.mdYou, in every projectPersonal preferences: “explain simply”, preferred language, how you like commit messages.
./CLAUDE.md or ./.claude/CLAUDE.mdEveryone on the project (commit it to git)Build and test commands, architecture, conventions.
./CLAUDE.local.mdYou, in this project only. Add it to .gitignore.Your sandbox URLs, preferred test data, personal habits for this repo.
CLAUDE.md in a sub-folderWork inside that folderRules for one module, such as server/ or app/. Loaded once Claude reads or edits a file in that folder.
Managed policy fileEveryone in an organisationCompany standards set by IT, in a system folder (for example /Library/Application Support/ClaudeCode/CLAUDE.md on macOS).
~/ (your computer) .claude/CLAUDE.md (personal) projects/my-app/CLAUDE.md (team) app/CLAUDE.md (module) server/CLAUDE.md (module) Context windowpersonal + team + module notesthen your first message
Broad notes load for every session. Narrow notes load only when Claude is working in that part of the project.

A good split is: personal taste at the user level, how this project works at the root, and how this one area works in the sub-folder. Commit the root file so your whole team gets the same assistant behaviour.

How they combine, per the official docs: Claude Code loads the CLAUDE.md in your working folder and in every folder above it at launch, and files in sub-folders on demand. They are added together, not overridden, ordered from the filesystem root down, so the instructions closest to where you started are read last. If your repository already has an AGENTS.md for other coding tools, Claude Code can read that when there is no CLAUDE.md, or you can import it from one. See How Claude remembers your project.

5. Creating one with /init

You do not have to start from a blank page. Run /init and Claude reads your project (build files, folders, existing docs) and drafts a CLAUDE.md.

/init
> /init

● Analyzing your codebase…
  Read(build.gradle.kts)
  Read(README.md)
  List(app/src)

● Created CLAUDE.md
  Build commands, module layout, and conventions found in the code.
  Review it, then trim anything that is not useful.
Illustrative mockup. Treat the generated file as a first draft that you review, not a finished product.

The draft is usually decent about commands and structure. It cannot know your team's unwritten rules or what has gone wrong before, so that part is yours to add. If a CLAUDE.md already exists, /init suggests improvements instead of overwriting it. To confirm a file really loaded, run /context and look under Memory files.

6. What to put in it

The test for every line is simple: would Claude get this wrong, or waste time finding it, without being told? If not, leave it out.

Put in ✓ How to build, run and test ✓ Folder map: what lives where ✓ Conventions that differ from defaults ✓ Gotchas and past mistakes ✓ Definition of done (tests, lint) ✓ Where to find deeper docs Leave out ✗ What the code already shows clearly ✗ Whole manuals pasted in ✗ Passwords, keys, tokens ✗ Style a formatter already enforces ✗ Vague wishes like “write good code” ✗ Anything you will not keep updated
A short list of useful, true, specific lines beats a long list of everything.

Specific beats vague. Compare these two lines:

VagueSpecific
Format code properly.Run ./gradlew ktlintFormat before committing.
Keep tests passing.Run ./gradlew testDebugUnitTest; all tests must pass before you say a task is done.
Follow our architecture.UI calls a ViewModel, the ViewModel calls a Repository. Never touch the database from a screen.

7. A complete example

Here is a realistic root file for the Android chat app from my earlier post. It is deliberately short.

CLAUDE.md
# Private AI Chat (Android)

Kotlin + Jetpack Compose app that chats with an on-device model
(llama.cpp) or an online model, with fallback between them.

## Commands
- Build debug:   ./gradlew assembleDebug
- Unit tests:    ./gradlew testDebugUnitTest
- Lint/format:   ./gradlew ktlintFormat
- Release bundle: ./gradlew :app:bundleRelease

## Structure
- app/ui/        Compose screens (no business logic)
- app/chat/      ViewModels, builds the prompt
- app/engine/    LlmEngine interface + local / online / rules impls
- app/data/      Room database (chats, memories, settings)
- native/        llama.cpp + JNI bridge (C++)

## Conventions
- Screens never call an engine directly: go through the ViewModel.
- Every engine returns Flow<String> of text pieces.
- All native calls run on the single dedicated native thread.
- Use https only for online models; never log prompts or API keys.

## Gotchas
- The native build must target plain armv8-a, or older phones crash (SIGILL).
- Test release builds on a real device: R8 can strip JNI classes.

## Definition of done
- Unit tests pass and ktlint is clean.
- New user-visible strings are in strings.xml.

It is about thirty lines. It answers the questions Claude would otherwise spend time on: how to build, where things live, and the two traps that already cost me a day each.

8. Splitting it up with imports

A CLAUDE.md can pull in other files by writing @ followed by a path. That keeps the main file small while still pointing at the detail.

CLAUDE.md
See @README.md for the project overview.

Architecture details: @docs/architecture.md
Release checklist:    @docs/release.md

Imported files are expanded and loaded at launch along with the main file, so they count toward its size: imports help you organise a long file but do not make it cheaper. A few rules from the official docs:

Use imports to organise, not to hide an enormous document. In Part 3 we will look at the cost side of this.

9. Growing up: the .claude folder

One CLAUDE.md is enough for a small project. As a project grows, a single file turns into a long list that is expensive to load and hard to maintain. Claude Code lets you spread the knowledge across a .claude/ folder in the project, so each piece has a clear job and loads only when it is useful. A typical layout looks like this (the official reference for all of it is the memory documentation):

my-project/.claude/ CLAUDE.md rules/ testing.md build.md modules/ (one file per module) skills/create-release-notes/ SKILL.md reference.md (extra detail) settings.json · settings.local.json What each part is for Always-on notes for the whole project Topic rules, split by subject or area Skills: a recipe, loaded when relevant Settings: permissions, model Why split it up? Smaller always-loaded file,detail loaded only when needed,and each file has one owner.
A generic example layout. Names are only examples; use whatever fits your project.

The pieces, one by one

PieceWhat it isLoadedGuaranteed?
CLAUDE.mdCore notes: commands, structure, key conventions.Every sessionNo, it is guidance
rules/*.mdTopic files such as testing, build or CI. Easier to review than one giant file. In recent versions a rule can be limited to certain paths, so it applies only there.Session start, or when the matching files are in playNo, it is guidance
skills/<name>/SKILL.mdA reusable recipe for one job, with optional extra files beside it.Only its short description is always visible; the body loads when the task matchesNo, Claude decides when to use it
settings.jsonTeam settings: permissions, default model. Committed.Session startYes, enforced by the tool
settings.local.jsonYour personal overrides for this project. Keep it out of git.Session startYes, enforced by the tool

The most useful distinction is the last column. CLAUDE.md, rules and skills ask Claude to behave a certain way. Settings make it happen. If Claude must never read a file, do not rely on a sentence in CLAUDE.md. Use a deny rule in settings.json.

Rules: split by topic

Move a long section out of CLAUDE.md into its own file and keep one line behind:

.claude/rules/testing.md
# Testing rules
- Unit tests live next to the code in src/test/.
- Name tests: methodName_condition_expectedResult.
- Run ./gradlew testDebugUnitTest before saying a task is done.
- Do not mock the database; use the in-memory one.

Each file stays short, has an obvious owner, and a pull request that changes testing rules touches only testing.md. A modules/ sub-folder works well for one rule file per module. Official reference: Organize rules with .claude/rules/.

A rule can also be limited to certain files with a paths list at the top. It then loads only when Claude works on a matching file, so it costs nothing elsewhere:

.claude/rules/api.md
---
paths:
  - "src/api/**/*.ts"
---

# API rules
- Every endpoint validates its input.
- Use the standard error response format.

A rule without paths loads every session, like CLAUDE.md.

Skills: a recipe that loads on demand

A skill packages a procedure you would otherwise paste into chat again and again, such as “how we write release notes”. Claude sees only a short description until the task matches, then reads the full instructions. That is the token saving: long how-to text costs nothing on unrelated tasks.

.claude/skills/create-release-notes/SKILL.md
---
name: create-release-notes
description: Write release notes from merged changes. Use when asked to
  prepare or draft release notes for a version.
---

1. List merged pull requests since the last tag.
2. Group them: Features, Fixes, Internal.
3. Write one plain-language line per change; no ticket numbers.
4. Follow the layout in reference.md.

Extra files beside SKILL.md (like reference.md) are read only if the skill points to them. Official reference: Extend Claude with skills.

A fuller settings.json: a deny list

Here is a more complete project .claude/settings.json with the thing I find most useful on a real project: a list of files Claude must not read. The file names are examples; replace them with your own.

.claude/settings.json
{
  "permissions": {
    "deny": [
      "Read(./.env)",
      "Read(./secrets.properties)",
      "Read(./**/*.keystore)",
      "Read(./**/*.pem)",
      "Read(./node_modules/**)",
      "Read(./package-lock.json)",
      "Read(./build/**)",
      "Read(./.idea/**)"
    ]
  }
}

The deny list does two jobs

Treat the list as a guardrail, not a vault. The safest secret is one that is not in the project folder at all.

Exact rule syntax changes between versions, so confirm it against the official settings and permissions pages before copying it.

Official documentation for this section

These pages move and grow as Claude Code changes. If a name or option here does not match your version, the docs are right and this post is out of date.

What goes in git, and what does not

Start small. Begin with one CLAUDE.md. Split out a rules file only when a section gets long, and add a skill when you paste the same instructions a third time.

10. Keeping it up to date

Three easy ways to change the file:

  1. Type /memory to list your CLAUDE.md and memory files, open one in your editor, and toggle auto memory.
  2. Tell Claude in plain words: “Add to CLAUDE.md that we always run ktlint before commits.” It will edit the file for you.
  3. Edit it like any other file and review the change in a pull request.
updating memory
> Remember: the debug build needs JAVA_HOME set to JDK 17.
  Add it to CLAUDE.md.

● Update(CLAUDE.md)
  + - Needs JDK 17: set JAVA_HOME before running Gradle.

● Added under ## Commands.
Illustrative mockup. When you correct Claude the same way twice, that is a signal to write the rule down.

Make this a habit: if you correct Claude twice, put the correction in the file. Equally, when a line stops being true, delete it. A wrong note is worse than none, because Claude will follow it with confidence.

Two more tips from the official docs. First, /doctor prompt-audit asks Claude to check your instruction files for outdated or conflicting content and proposes edits (it changes nothing until you agree, and it needs a recent version). Second, block-level HTML comments such as <!-- maintainer notes --> are stripped before CLAUDE.md is sent to Claude, so you can leave notes for people without spending tokens on them.

11. What it cannot do

That second point, the cost of space, is where Part 3 begins: how to make the file earn its tokens, and how it helps when you join a new project or start a new feature.

TermIn plain words
Context windowThe fixed amount of text the model can read at once.
CLAUDE.mdA markdown file loaded into the context at the start of each session.
/initCommand that drafts a CLAUDE.md from your project.
/memoryCommand that opens your memory files for editing.
RulesTopic files in .claude/rules/, split out of CLAUDE.md.
SkillA reusable recipe whose full text loads only when the task matches.
Import (@path)Pulls another file's contents into CLAUDE.md.
Standing instructionA rule present at the start of every session.
← Back to all writing