Modern Angular
A CLAUDE.md for Modern Angular: Memory, Not Just Rules
Claude Code does not read your AGENTS.md. It reads CLAUDE.md, and it loads that file into every session automatically. ng generate ai-config --tool claude writes one to .claude/CLAUDE.md, a path Claude Code already treats as project memory. The win for an Angular repo is not a longer rules file. It is structure: a hierarchy, imports, and path-scoped rules that line up with how the workspace is already organized.
AGENTS.md is the what, CLAUDE.md is how Claude Code loads it
If you already wrote an AGENTS.md for your Angular repo, there is a detail that catches teams out: Claude Code does not read it. It reads CLAUDE.md, and it reads it on its own. At the start of a session it walks up the directory tree and loads every CLAUDE.md it finds, so the file is already in context before you type anything. An AGENTS.md sitting next to it is ignored unless you pull it in on purpose.
There are two project-level spots Claude Code accepts: ./CLAUDE.md at the repo root, or ./.claude/CLAUDE.md inside the .claude/ folder. They count the same. That second path is the one that matters for Angular, because ng generate ai-config --tool claude writes exactly there. The file the CLI scaffolds is already a memory file Claude Code will load, so you do not have to move it.
The constraints that go inside, the version pin, the Signals and RxJS boundary, the template rules, are the same ones I worked through for an AGENTS.md. That guide is the what. This one is about how Claude Code loads and organizes them, which is where the leverage is.
| File | When it loads | What it is for |
|---|---|---|
| ~/.claude/CLAUDE.md | Every session, every project | Your personal defaults, not the team's |
| .claude/CLAUDE.md or ./CLAUDE.md | At session start, top of the tree | The Angular rules the whole team shares |
| CLAUDE.local.md | At session start, after CLAUDE.md | Your machine-only notes, gitignored |
| src/app/feature/CLAUDE.md | On demand, when Claude opens files there | Rules for one feature or library |
Import AGENTS.md, do not copy it into CLAUDE.md
The wrong way to keep both files is to paste the Angular rules into each. Now you have two copies that drift, and ng generate ai-config will happily regenerate one of them out from under you. Claude Code has an import for this. A line that starts with @ pulls another file into CLAUDE.md when it loads, so one source of truth feeds both tools.
I keep the Angular constraints in AGENTS.md, the file the CLI maintains, and let CLAUDE.md import it and add only the Claude-specific part: when to use plan mode, which directories are off-limits, how this team runs tests. Imports resolve relative to the file and go up to four levels deep, which is more than enough for one @AGENTS.md.
# CLAUDE.md
@AGENTS.md
## Working in this repo
- Ask before adding a dependency; this repo stays lean.
- Use plan mode before touching anything under src/app/**/state/.
- Run tests with `npm test` (Vitest). Do not bring Karma back.
- Never edit files under src/generated/.Path-scoped rules line up with an Angular workspace
A single rules file tries to cover components, state, and tests at once, and the agent reads all of it whether it is writing a pipe or a spec. Claude Code can scope rules to paths instead. A file in .claude/rules/ with a paths glob in its frontmatter loads only when Claude opens a file that matches. An Angular workspace already has those boundaries: specs, components, state.
This is where the testing rules earn their own file. The advice that matters in a .spec.ts, use Vitest, the fakeAsync helpers need the zone patch, do not call detectChanges after every change, is noise in a file about components. It is also a decision in its own right. Scope those rules to the specs and they show up exactly when the agent is writing one.
---
paths:
- "src/app/**/*.spec.ts"
---
# Testing rules
Use Vitest. Do not reintroduce Karma or Jasmine.
fakeAsync, tick, and flush only work with the zone.js vitest-patch enabled.
Prefer fake timers or native async over fakeAsync in new specs.
Do not call detectChanges after every mutation; it hides missing notifications.Keep the root file short; let the tree carry the rest
Memory is not free. Everything in CLAUDE.md and everything it imports sits in context for the whole session, and a bloated file does not just cost tokens, it gets followed less reliably. The guidance is to keep a CLAUDE.md under about two hundred lines and push detail outward: imports for the shared rules, path-scoped files for the parts that only matter sometimes.
An Angular monorepo gives you the other axis for free. A CLAUDE.md inside a feature library loads only when Claude is working in that library, so per-app and per-lib rules do not weigh on every session. /init drafts a starter file from your codebase, and /memory lists what is actually loaded right now, which is the fastest way to catch a file you forgot was there.
| Rule | Where it goes | Why |
|---|---|---|
| Version pin, Signals/RxJS boundary, template syntax | AGENTS.md, imported into CLAUDE.md | One source the CLI keeps current |
| Plan mode, off-limits dirs, how to run tests | CLAUDE.md root | Claude-specific, always true |
| Vitest and fakeAsync rules | .claude/rules/testing.md, scoped to *.spec.ts | Loads only when writing a spec |
| State and store conventions | .claude/rules/state.md, scoped to the state dir | Loads only in state code |
| One library's quirks | lib/CLAUDE.md | Lazy-loaded when working in that library |
A stale CLAUDE.md misleads on every session
Every file here has the same failure mode as a hand-written rules file, with one twist: because CLAUDE.md loads on every session, a stale line is not a document someone might read wrong. It is an instruction the agent follows on every task, quietly, until someone notices the generated code is a version behind.
So the maintenance rule is the one I tie to AGENTS.md: when the team bumps an Angular major, the memory files get the same review as the dependencies. The version pin, the experimental-API list, the testing notes, all of it. A CLAUDE.md that still calls Signal Forms experimental will steer an agent away from an API that went stable in v22, with full confidence, on every run.
Reusable artifact
A CLAUDE.md setup for an Angular repo
- Memory file at .claude/CLAUDE.md or ./CLAUDE.md (both load); the ai-config CLI writes the first.
- AGENTS.md imported with @AGENTS.md, not copied, so the CLI-maintained rules stay the single source.
- Testing rules scoped to *.spec.ts in .claude/rules/, so Vitest and fakeAsync notes load only on specs.
- State and component rules path-scoped the same way; the root file holds only what is always true.
- Root CLAUDE.md under about 200 lines; a per-library CLAUDE.md for monorepo features.
- A review trigger tied to Angular majors, so the version pin and experimental-API list never go stale.
Sources checked
- https://code.claude.com/docs/en/memory
- https://code.claude.com/docs/en/claude-directory
- https://angular.dev/cli/generate/ai-config
- https://angular.dev/ai/develop-with-ai
- https://angular.dev/ai
- https://angular.dev/ai/mcp
- https://angular.dev/guide/testing
- https://angular.dev/style-guide
- https://blog.angular.dev/announcing-angular-v21-57946c34f14b
Modern Angular Playbook
This article is one play.
The Modern Angular Playbook collects the diagnostic, the adoption matrix, eleven plays, and the 30-day plan. Free, in English and Portuguese.
You get both PDFs by email, through the Dojo IA list.
André Ramosavailable for remote roles, UTC−3Get in touch →