Project Boundaries and CLAUDE.md
CLAUDE.md is the contract between you and the AI. Plan mode lets you review before acting. Reference files, nested CLAUDE.md in monorepos — all the tools for shaping what the AI knows and does.
The single most important thing you can do to improve Claude Code quality is get CLAUDE.md right.
It's the contract. Every session reads it first. What's in it shapes every response. What's missing causes every surprise.
CLAUDE.md: Beyond the Generated Version
/init gives you a starting point. The generated CLAUDE.md captures what the AI can infer from your files — the stack, the structure, the commands. What it can't infer:
- Which patterns are intentional vs. historical accidents
- What decisions were made deliberately and shouldn't be reversed
- What the team tried and decided was wrong
- Performance or security requirements specific to your domain
- The "why" behind unconventional choices
You add those manually.
The Four Sections of a Good CLAUDE.md
1. Project context — what is this and who uses it?
## Project Context
B2B SaaS application for accounting firms.
Users are accountants managing client portfolios.
Performance is critical — users are in the app all day.
Any response > 200ms will generate support tickets.
Data isolation between clients is a hard security requirement.
A bug that leaks Client A's data to Client B is a P0 incident.2. Tech stack with specific versions
## Stack
- Next.js 15.1.0 — App Router only, not Pages Router
- TypeScript 5.4 — strict mode, no exceptions
- PostgreSQL 16 via Prisma 5.11
- Vitest 1.6 + React Testing Library 15
- Tailwind CSS 3.4
- Auth.js v5 for authenticationVersions matter. The AI has seen code from every version of Next.js. Specifying 15.1.0, App Router prevents it from generating patterns from Next.js 13 or Pages Router code.
3. Explicit conventions
## Conventions
- Components: PascalCase, one per file, in src/components/
- Use "import type" for all type-only imports
- Never use `any` — use `unknown` and narrow properly
- Server components by default; add "use client" only when needed
- Forms use react-hook-form + Zod — never uncontrolled inputs
- API responses use the Result type from src/types/result.ts
- All DB queries go through the query functions in src/lib/db/ — never raw Prisma calls in components4. Hard constraints — what NOT to do
## Do Not
- Add console.log (use the logger at src/lib/logger.ts)
- Import from react directly — use the re-exports in src/lib/react.ts
- Modify Prisma migrations after they've been applied to any environment
- Use class components
- Use var
- Add new npm packages without noting them in this file
- Touch src/lib/auth.ts without discussing it first — it's extremely sensitiveThe "do not" section is often the most valuable part. It captures decisions made after learning things the hard way. The AI doesn't know your history — you have to tell it.
Plan Mode (Shift+Tab)
Plan mode is one of the most useful features in Claude Code. Without it:
You describe a task → Claude Code immediately starts acting on itWith plan mode:
You describe a task → Claude Code describes what it plans to do
→ You review and respond → Claude Code actsHow to activate:
Shift+Tab → toggle plan mode on/offOr prefix your message:
> [plan mode] add Google OAuth to the login flowWhat you get in response:
I'll need to:
1. Install @auth/core and @auth/prisma-adapter
2. Create src/auth.ts with the Auth.js v5 configuration
3. Create src/app/api/auth/[...nextauth]/route.ts
4. Update src/app/providers.tsx to include the SessionProvider
5. Modify the login page at src/app/(auth)/login/page.tsx
Before I start, I need to confirm:
- Should I use the existing User model or create a separate Account model?
- Which OAuth providers beyond Google? (GitHub is listed in the requirements)
- Should the session strategy be database (JWT + DB sessions) or JWT only?This is a plan review, not code review. You can catch wrong assumptions before they become wrong implementations. You can answer questions that would otherwise become wrong guesses.
When to use plan mode:
- Any task touching more than 3 files
- Anything involving database schema or migrations
- Features you haven't fully specified in your head yet
- Anything where getting it wrong is expensive to undo
Reference Files
Instead of re-explaining context in every prompt, tell CLAUDE.md to always load certain files:
## Always Load
@src/types/index.ts — all shared TypeScript interfaces and types
@src/lib/db/index.ts — database client and base query patterns
@src/lib/result.ts — the Result<T, E> type used in all API responsesThese files get loaded with every session. The AI always has the type definitions and core patterns in context, without you having to @-reference them each time.
What to include as reference files:
- Shared type definitions
- Core utilities with patterns the AI should follow
- A file that exemplifies the "right way" to do something
What not to include:
- Large files (they eat context on every session)
- Files that change frequently
- Files with secrets
Nested CLAUDE.md in Monorepos
In a monorepo or project with distinct domains, you can have CLAUDE.md files at multiple levels:
project/
├── CLAUDE.md ← global: repo structure, cross-cutting rules
├── apps/
│ ├── web/
│ │ └── CLAUDE.md ← frontend-specific conventions
│ └── api/
│ └── CLAUDE.md ← API-specific constraints
└── packages/
└── shared/
└── CLAUDE.md ← shared package rulesWhen you're working in apps/web/, Claude Code reads both the root CLAUDE.md and apps/web/CLAUDE.md. Global rules apply everywhere; local rules apply within their directory.
Use this when:
- Frontend and backend have fundamentally different conventions
- Some packages have stricter constraints than others
- You want domain-specific "do not" rules without polluting the global file
The root CLAUDE.md should cover: repo structure, cross-package imports, deployment, shared patterns.
Domain CLAUDE.md files should cover: the patterns specific to that app or package, local conventions, local constraints.
The Living Document
CLAUDE.md is never done. Every mistake the AI makes that a rule would have prevented is a potential update.
Update it when:
- The AI does something wrong a clear rule would have caught
- You add a new library or change a core pattern
- You make a decision that shouldn't be reversed
- You onboard a new project area
A single line — "Never import Prisma client directly in components — use the query functions in src/lib/db/" — might prevent the same mistake from appearing in every session for the next year.
That's the compounding return on CLAUDE.md investment.
Keep reading
Enjoyed this? Get more like it.
Deep dives on system design, React, web development, and personal finance — straight to your inbox. Free, always.