CLAUDE.md example for a SaaS codebase

A real CLAUDE.md for a multi-module TypeScript SaaS: what to include, how it loads with AGENTS.md, and which rules belong in lint and hooks instead.

MC
Martin Coll

A CLAUDE.md file is the project briefing Claude Code reads at the start of every session. Each session begins with an empty context window, so this file is how knowledge about your codebase survives from one session to the next: how to run it, how it is laid out, and what never to do.

Code for this guide: https://github.com/KonixDev/claude-code-guardrails — the open-source (MIT) claude-code-guardrails kit.

Most guides stop at "put your commands and conventions in it". This one is about a specific kind of project — a multi-module TypeScript SaaS — and about the part most CLAUDE.md files get wrong: they ask for things that should be enforced.

Where CLAUDE.md lives and when it loads

  • Project root (./CLAUDE.md or ./.claude/CLAUDE.md): loaded at session start. Commit it; it is for the whole team.
  • CLAUDE.local.md: your personal notes for this project. Keep it out of git.
  • Your user folder (~/.claude/CLAUDE.md): your defaults for every project.
  • Subdirectories: a CLAUDE.md inside apps/server/ loads when Claude reads files there, not at launch.
  • .claude/rules/: rule files that can be scoped to paths, so they load only when Claude works on matching files.
  • Imports: a line like @AGENTS.md pulls another file in. That organizes a long file; it does not make it cheaper — the docs are explicit that imported files load together with the file that imports them.

Anthropic's guidance is to keep each CLAUDE.md under about 200 lines: longer files cost more context and get followed less. To shrink it, move what only matters for one part of the repo into that folder's CLAUDE.md or a path-scoped rule in .claude/rules/.

CLAUDE.md or AGENTS.md?

AGENTS.md is the tool-neutral version of the same idea: Cursor and Codex read it, and recent Claude Code versions (2.1.277+) read it when there is no CLAUDE.md in the project. If you use more than one agent, keep the rules in AGENTS.md and make CLAUDE.md a thin wrapper:

@AGENTS.md

## Claude Code specifics

- Hooks in `.claude/hooks/` lint every edit and send you back once if you
  try to finish while `npm run guardrails` fails. Treat their output as a
  failing test.
- Use the `architecture-reviewer` subagent before reporting a non-trivial
  change as done.

One rule set, every agent reads it, and the Claude-only notes stay out of the other tools' way.

A CLAUDE.md for a multi-module SaaS

Here is a generalized version of the one in useDeploy (which is longer and uses Bun): one folder per bounded context under src/modules/ (billing, tenancy, identity…), each split into domain/, application/, infrastructure/ and interfaces/.

# Project briefing

## Run it
- `npm run dev` — app on :3000, API on :3005
- `npm run guardrails` — lint + typecheck. Must pass before you say "done".
- `npm test` — unit tests. Add one for every behavior change.

## Layout
One folder per bounded context in `src/modules/<context>/`:
domain/ (entities, no I/O) → application/ (use cases, ports)
→ infrastructure/ (adapters) → interfaces/ (HTTP).
Dependencies point inward. See AGENTS.md for the full rules.

## Contracts
Request/response schemas live in `packages/contracts`. The UI imports them;
never re-declare a server field on the client.

## When you change an endpoint
Regenerate the API types (`npm run generate:api`) and commit them in the
same change. CI fails if they are stale.

## Don't
- Don't add a dependency without saying why.
- Don't edit generated files (`openapi.json`, `*.generated.ts`).
- Don't disable a lint rule to get green — fix the cause or ask.

What makes it work:

  • Commands first. The single most useful thing in the file is how to verify a change. An agent that knows npm run guardrails exists will run it.
  • The layout in four lines. Enough for Claude to put new code in the right folder; the detail lives in AGENTS.md.
  • The traps, stated once. Generated files, stale API types, a client form that re-declares a server field. Each line is there because skipping it breaks something CI or users will notice.
  • No style guide. Formatting belongs to Prettier and ESLint, not to prose the model has to remember.

What CLAUDE.md can't do

Anthropic's documentation is direct: Claude treats CLAUDE.md as context, not as enforced configuration, and to block an action regardless of what Claude decides it points you to a PreToolUse hook. (Why that matters for architecture.) In practice, move each rule that must never break into something that fails:

Rule in proseWhere it should be enforced
"domain/ never imports a framework"ESLint no-restricted-imports (the exact config)
"modules only talk through types"@typescript-eslint/no-restricted-imports with allowTypeImports
"run the checks before you finish"a Claude Code Stop hook that sends Claude back once if they fail
"fix lint errors as you go"a PostToolUse hook that lints every edited file
"regenerate API types"a CI job that diffs the generated files

Keep the sentence in CLAUDE.md — it tells Claude why — and let the tooling say no. The next guide shows the two hooks: Claude Code hooks that enforce your architecture.

Get the files

Everything above — AGENTS.md, the CLAUDE.md wrapper, the ESLint boundary config, both hooks, a review subagent and a Cursor rule — is open source and copy-in: claude-code-guardrails on GitHub (MIT). It ships with tests that run each rule against real violations.

FAQ

How long should a CLAUDE.md be? Anthropic suggests under about 200 lines per file. Move folder-specific sections into that folder's CLAUDE.md or a path-scoped .claude/rules/ file; imports organize a file but don't reduce what gets loaded.

Does Claude Code read AGENTS.md? Yes (2.1.277+), when the project has no CLAUDE.md. If you have both, import it from CLAUDE.md with an @AGENTS.md line so Claude reads the same rules as Cursor and Codex.

Should I put coding style in CLAUDE.md? Only what a formatter or linter can't check. Everything they can check is cheaper and more reliable as config.

Can Claude ignore my CLAUDE.md? It is context, not a constraint, so yes — especially deep into a long task. That is why the rules that must never break belong in lint, hooks and CI.


useDeploy is a TypeScript SaaS boilerplate that ships this setup — AGENTS.md, the hooks and the ESLint boundaries — on top of auth, organizations and billing. If you'd rather start from a base where the guardrails already pass, see what's included.