DEV Community

Cover image for A Concise Guide to Writing CLAUDE.md
code plato
code plato

Posted on

A Concise Guide to Writing CLAUDE.md

This guide applies equally to AGENTS.md

What CLAUDE.md Is For

First, let's understand what CLAUDE.md is for. A model is like a newborn. Every time you talk to it, it has no idea who you are, who it is, or what you've said before. It learns all of this from the context you pass it. Of course, Claude Code comes with some preset context, so it knows it's Claude Code, a tool that helps you write code.
So at the start of every new session, it knows nothing about your project. The reason it still works reasonably well is that Claude Code's own preset context teaches it what to do to understand a project before touching it.
That's why it can seem to understand your project quite well, yet still frequently reinvents the wheel. It has a methodology for quickly getting up to speed, but it still can't know every detail of your project. And you wouldn't want it to try — that would burn far too many tokens.
This is where someone needs to use short, plain language to tell it about the project's background, architecture, and so on. Doing this has two benefits:

  1. It won't need to traverse your entire project, saving tokens
  2. It won't do things that obviously go against your codebase's intent, or reinvent the wheel This short text is what goes into CLAUDE.md, or AGENTS.md. This guide applies equally to AGENTS.md.

Writing Principles

A good CLAUDE.md should read like:
The shortest onboarding guide for a new hire: Imagine you're onboarding a new hire for a task. Try to convey just enough knowledge, in the fewest words possible, for them to start working without wrecking your codebase.
Wording as tight as a résumé: A while back I was job hunting, and I learned from an HR contact how to write a résumé. I racked my brain compressing my 4-page résumé down to 2 pages, cutting almost every redundant word, tightening every sentence until it couldn't be trimmed further. You should write CLAUDE.md the same way.
Highly abstract knowledge: You don't need to tell Claude Code or Codex CLI how many spaces to indent, or which line of which file has the database connection. Leave a lot of that to hooks — file names and line numbers change all the time. What you should tell the model are highly abstract design principles.
Generally speaking, a good CLAUDE.md should be under 200 lines.

Types

Including the CLAUDE.md at your project root, there are actually 3 kinds of CLAUDE.md.

  1. ~/.claude/CLAUDE.md — global scope. Used by all of your projects.
  2. ./CLAUDE.md — project scope.
  3. ./subdirectory/CLAUDE.md — subdirectories can have their own CLAUDE.md too.

Framework

There's no strict, perfect framework for CLAUDE.md. But drawing on some good examples of CLAUDE.md and related articles, I've distilled a reasonably sound structure:

  1. One-line introduction
  2. Architecture
  3. Tech stack
  4. Commands
  5. Conventions
  6. Boundaries
  7. Domain doc map

One-Line Introduction

A brief description of what the project is and roughly what it does. Example:

This is a multi-agent orchestration framework for coordinating parallel Claude Code sub-agents, automating dev/QA workflows on a FastAPI + React codebase.
Enter fullscreen mode Exit fullscreen mode

Architecture

A brief description of the project's architecture, for example:

## Architecture
- Keep controllers thin — business logic lives in `app/Services/`
- Database access only goes through `app/Repositories/`. No direct Eloquent usage in controllers.
- API resources under `app/Http/Resources/` are responsible for shaping every JSON response.
Enter fullscreen mode Exit fullscreen mode

Tech Stack

A brief description of the project's tech stack, like:

## Tech Stack
- FastAPI, Python 3.11
- PostgreSQL 15 (async SQLAlchemy 2.0)
- Celery + Redis for background jobs
- Poetry for dependency management
Enter fullscreen mode Exit fullscreen mode

Commands

Common commands for the project, for example:

## Commands
- Dev server: `uvicorn app.main:app --reload`
- Run tests: `pytest -x -v`
- DB migrations: `alembic upgrade head`
Enter fullscreen mode Exit fullscreen mode

Conventions

Abstract conventions that a linter can't capture, like:

## Conventions
- Boolean variables/properties are prefixed with `is`, `has`, or `should`
- All datetimes are stored and passed as UTC
- Event names follow the `domain.action` naming format
Enter fullscreen mode Exit fullscreen mode

Boundaries

Boundaries on which files can be modified:

## Boundaries
- `legacy/` — old payment system, emergency bug fixes only, no new patterns or refactors
- `src/generated/` — auto-generated by Prisma/GraphQL
- `vendor/`, `third_party/` — third-party code, fix issues by upgrading the dependency version, don't edit directly
Enter fullscreen mode Exit fullscreen mode

Domain Doc Map

A mapping between domain terms and documentation:

## Domain Doc Map
| Mentions | Read |
|---|---|
| billing, stripe, payment, subscription, invoice | docs/billing.md |
| auth, login, session, oauth, jwt | docs/auth.md |
| migration, schema, drizzle, kysely | docs/db-migrations.md |
| feature flag, rollout, kill switch | docs/feature-flags.md |
Enter fullscreen mode Exit fullscreen mode

Putting these examples together, a good CLAUDE.md should look something like this:

This is a multi-agent orchestration framework for coordinating parallel Claude Code sub-agents, automating dev/QA workflows on a FastAPI + React codebase.

## Architecture
- Keep controllers thin — business logic lives in `app/Services/`
- Database access only goes through `app/Repositories/`. No direct Eloquent usage in controllers.
- API resources under `app/Http/Resources/` are responsible for shaping every JSON response.

## Tech Stack
- FastAPI, Python 3.11
- PostgreSQL 15 (async SQLAlchemy 2.0)
- Celery + Redis for background jobs
- Poetry for dependency management

## Commands
- Dev server: `uvicorn app.main:app --reload`
- Run tests: `pytest -x -v`
- DB migrations: `alembic upgrade head`

## Conventions
- Boolean variables/properties are prefixed with `is`, `has`, or `should`
- All datetimes are stored and passed as UTC
- Event names follow the `domain.action` naming format

## Boundaries
- `legacy/` — old payment system, emergency bug fixes only, no new patterns or refactors
- `src/generated/` — auto-generated by Prisma/GraphQL
- `vendor/`, `third_party/` — third-party code, fix issues by upgrading the dependency version, don't edit directly

## Domain Doc Map
| Mentions | Read |
|---|---|
| billing, stripe, payment, subscription, invoice | docs/billing.md |
| auth, login, session, oauth, jwt | docs/auth.md |
| migration, schema, drizzle, kysely | docs/db-migrations.md |
| feature flag, rollout, kill switch | docs/feature-flags.md |

Enter fullscreen mode Exit fullscreen mode

Should You Write a "Never" Section?

You may have seen a section in other guides called "Never," used to record past mistakes. Sounds good in theory. But I don't recommend adding this section. That's because a "Never" section has only the motivation to add, never the motivation to remove. It just keeps growing — even Claude Code itself will add to it, and no one ever trims it. Over time it turns into a graveyard of historical incidents.
What I'd suggest instead: when a problem comes up, flip the failure mode around and write it as a positive rule. For instance, instead of "Never do synchronous database writes in webhook/stripe.ts," write "Database operations only happen inside repository/."

Trimming

Even if you follow the framework above, for a large project, a long-lived one, or one that already has a CLAUDE.md, you'll still find it hard to keep the file under 200 lines. That's when you move to the trimming step.
Trimming means moving things out of CLAUDE.md. Here are the destinations and methods, in order:

  1. Custom sub-agents: .claude/agents
  2. Rules folder: .claude/rules
  3. Subdirectory CLAUDE.md: ./subdirectory/CLAUDE.md
  4. Fixed workflows: .claude/skills/
  5. Other docs: docs/ Notice these are numbered. That's because extraction has a priority order — from specific to abstract, from precise to general.

Custom Sub-agents

Move all guidance related to sub-agents into .claude/agents. For example:
You could define an agent dedicated to running integration tests, named integration-tester.md, with content like:

---
name: integration-tester
description: Runs integration tests
tools: Bash
model: sonnet
---

You run and diagnose integration tests. You should follow these steps.....
Enter fullscreen mode Exit fullscreen mode

Rules Folder

Specific rule files can live in .claude/rules. There are two kinds: path-scoped and non-path-scoped. Non-path-scoped rules carry the same priority as CLAUDE.md, for example:

# Security Rules

- All user input is validated at the API boundary
- Secrets and API keys are read from environment variables only
- SQL queries always use parameterized statements
Enter fullscreen mode Exit fullscreen mode

Path-scoped rules only load when the given path condition is met, for example:

---
paths:
  - "tests/**/*.py"
  - "**/*.spec.ts"
---

# Unit Testing Rules

- One assertion concept per test
- Test names describe behavior, not implementation
....
Enter fullscreen mode Exit fullscreen mode

Rules vs. the domain doc map: You might wonder: "Don't rule files and the domain doc map end up defining the same thing twice?" Yes, that overlap can happen. For instance, you might specify that files under billings/ should follow the billing docs, and also have a row in the domain doc map like | billings | docs/billings.md | — which is indeed redundant.
The fix is to split documentation into rules and background. Rule files are highly enforceable and belong in .claude/rules. Background files are comparatively looser and belong in docs/.

Subdirectory CLAUDE.md

Rules specific to certain subdirectories can move into those directories — common cases are sql/, domains/, adapters/ folders. A CLAUDE.md in these directories doesn't need to follow the standard framework, but it should still stay short.

Fixed Workflows

If there are fixed, sequential operations, turn them into a project-level skill and put it under .claude/skills/.

Other Docs

Everything else goes into the docs/ directory. This directory holds more general documentation — things like a more detailed architecture.md, or ADRs (Architecture Decision Records) under docs/adr/, e.g. docs/adr/0001-migrate-to-drizzle.md.

Getting Started

If you don't yet have a CLAUDE.md or AGENTS.md, start with these steps:

  1. Run /init to generate a first draft
  2. Revise the draft following the methods above

Also remember to run /doctor periodically going forward to keep optimizing CLAUDE.md.

References


About the Author

I'm CodePlato.

I believe human creativity is the true tree of AI Coding. Code and models are only shadows projected onto the walls of the cave.

X: @codeplato2026
https://x.com/codeplato2026

Top comments (0)