I gave Claude Code a simple task: add a logout button to the user profile screen.
Three minutes later, Claude Code delivered. The code was clean. It compiled. It technically worked.
It was also completely wrong for our project.
The logout button was in the wrong component — it should have been in the navigation bar, not the profile screen. It used Material Design 2 when our entire app uses Material 3. It called the auth service directly instead of going through our ViewModel layer. And it hardcoded the navigation path instead of using our centralized routing constants.
None of this is Claude Code’s fault. It doesn’t know our conventions. It doesn’t know our architecture. It doesn’t remember that yesterday we decided all auth flows must go through ViewModels. Because Claude Code has no memory between sessions. Every conversation starts from zero.
This is the problem nobody talks about. And there’s a solution that takes five minutes to set up and transforms output quality forever: CLAUDE.md.
What Claude Code Doesn’t Know About Your Project
Every time you start a new Claude Code session, it’s like hiring a contractor who’s never seen your codebase. It can read files you point it to. It can infer patterns from what it sees. But it doesn’t know:
- Your architectural decisions — why you chose Repository pattern over direct database access, why all network calls go through a singleton ApiClient, why you have that weird middleware chain
- Your team conventions — PascalCase for components, camelCase for functions, kebab-case for file names, all test files end in
.test.tsnot.spec.ts - Your tech stack preferences — Zustand for state management (not Redux), TailwindCSS utility classes (not CSS modules), async/await everywhere (no
.then()chains) - Your folder structure logic — why
lib/is for shared utilities butservices/is for API clients, why there are three differentcomponents/directories at different levels - Things you learned the hard way — that one library has a memory leak so we don’t use it, that API endpoint returns stale data so we cache-bust it, that Firebase config must never be committed
Without this context, Claude Code generates code that works but doesn’t fit. You end up spending 20-40 minutes every session fixing patterns, renaming things, and restructuring code to match your conventions.
Here’s what this looks like across different stacks:
Next.js Example: Wrong Pattern
What you asked for:
“Add a contact form that saves to the database”
What Claude Code generated (without CLAUDE.md):
'use client';
import { useState } from 'react';
export default function Contact() { const [email, setEmail] = useState('');
const handleSubmit = async () => { const res = await fetch('/api/contact', { method: 'POST', body: JSON.stringify({ email }) }); // ... };
return <form onSubmit={handleSubmit}>...</form>;}What’s wrong:
- Client Component when it should be Server Component + Server Action
- Direct fetch to API route when you use Server Actions for mutations
- No validation (you use Zod schemas everywhere)
- Inline styles when you use TailwindCSS + shadcn/ui
Python/FastAPI Example: Wrong Architecture
What you asked for:
“Add an endpoint to fetch user orders”
What Claude Code generated (without CLAUDE.md):
from fastapi import APIRouterfrom app.database import db
router = APIRouter()
@router.get("/orders/{user_id}")def get_orders(user_id: int): orders = db.query("SELECT * FROM orders WHERE user_id = ?", user_id) return {"orders": orders}What’s wrong:
- Direct database query when you use Repository pattern
- Missing Service layer (Router → Service → Repository is mandatory)
- Synchronous function when everything is async
- No type hints for return value
- Raw SQL when you use SQLAlchemy everywhere
Mobile (KMP) Example: Wrong Platform Split
What you asked for:
“Add biometric authentication”
What Claude Code generated (without CLAUDE.md):
class BiometricAuth { fun authenticate() { // Android BiometricPrompt implementation }}
// iosApp/Sources/BiometricAuth.swiftclass BiometricAuth { func authenticate() { // iOS LocalAuthentication implementation }}What’s wrong:
- Duplicated logic in platform-specific code when it should be in
shared/commonMain/ - Should use
expect/actualpattern for platform APIs - Missing ViewModel layer (you never put auth logic directly in platform code)
- No sealed class for auth state (Loading, Success, BiometricFailed, etc.)
This happens because Claude Code can’t read your mind. And it shouldn’t have to. This is what CLAUDE.md fixes.
What CLAUDE.md Actually Is
CLAUDE.md is a single Markdown file in your project root that Claude Code automatically reads at the start of every session. It’s the first file Claude Code sees — before it reads any code, before you give it a prompt.
Think of it as permanent instructions that persist across every conversation, every session, every week, every month.
Here’s the mental model:
Without CLAUDE.md:Session 1: You explain conventions → Claude Code generates codeSession 2: You explain conventions AGAIN → Claude Code generates codeSession 3: You explain conventions AGAIN → Claude Code generates code
With CLAUDE.md:Setup: You write conventions ONCE in CLAUDE.mdSession 1: Claude Code reads CLAUDE.md → generates correct codeSession 2: Claude Code reads CLAUDE.md → generates correct codeSession 3: Claude Code reads CLAUDE.md → generates correct codeThe return on investment is absurd. Five minutes of writing saves hours every week.
What Goes In a CLAUDE.md
Every CLAUDE.md should answer five questions:
- What is this project? (1-2 sentences — what it does, who uses it)
- What’s the tech stack? (frameworks, libraries, tools)
- What are the architectural patterns? (layers, data flow, state management)
- What are the conventions? (naming, formatting, folder structure)
- What should never be done? (dangerous operations, banned patterns, sensitive files)
Let me show you real examples across different project types.
Before vs After: Output Quality Comparison
Example 1: Next.js E-commerce Dashboard
Prompt:
“Add a product search feature with filters for category and price range”
Without CLAUDE.md
// app/search/page.tsx (generated without CLAUDE.md)'use client';
import { useState, useEffect } from 'react';import axios from 'axios';
export default function Search() { const [products, setProducts] = useState([]); const [category, setCategory] = useState(''); const [minPrice, setMinPrice] = useState(0); const [maxPrice, setMaxPrice] = useState(1000);
useEffect(() => { axios.get('/api/products', { params: { category, minPrice, maxPrice } }).then(res => setProducts(res.data)); }, [category, minPrice, maxPrice]);
return ( <div className="search-container"> <select value={category} onChange={e => setCategory(e.target.value)}> <option value="">All</option> <option value="electronics">Electronics</option> </select> <input type="number" value={minPrice} onChange={e => setMinPrice(e.target.value)} /> <input type="number" value={maxPrice} onChange={e => setMaxPrice(e.target.value)} /> <div> {products.map(p => <div key={p.id}>{p.name}</div>)} </div> </div> );}Problems:
- Client Component when it should be Server Component
- Using
axioswhen project usesfetch - Using
.then()when project usesasync/await - Custom CSS class names when project uses TailwindCSS
- Inline JSX when project uses shadcn/ui components
- No TypeScript types
- No error handling
- No loading state
Time to fix: 25 minutes
With CLAUDE.md
## Project: E-commerce DashboardAdmin dashboard for managing products, orders, and customers.
## Tech Stack- Next.js 14 + TypeScript + App Router- React Server Components + Server Actions- Prisma + PostgreSQL- TailwindCSS + shadcn/ui- Zod for validation
## Architectureapp/ ├── (auth)/ # Auth routes ├── api/ # API routes (minimal — prefer Server Actions) └── dashboard/ # Protected routes lib/ ├── actions/ # Server Actions (all mutations) ├── db/ # Prisma client └── utils/ # Shared utilities
## Conventions- Server Components by default, "use client" ONLY when absolutely necessary- All database access via Prisma (never raw SQL)- Server Actions for mutations, not API routes- TailwindCSS utility classes, never custom CSS- shadcn/ui components for all UI elements- Zod schemas for all forms- async/await everywhere (no .then() chains)
## Rules- NEVER commit secrets (.env.local, docker-compose.yml)- NEVER access process.env outside of lib/config- NEVER use CSS modules or styled-componentsGenerated code (with CLAUDE.md):
import { Suspense } from 'react';import { ProductSearchFilters } from '@/components/products/search-filters';import { ProductGrid } from '@/components/products/product-grid';import { searchProducts } from '@/lib/actions/products';
interface SearchPageProps { searchParams: { category?: string; minPrice?: string; maxPrice?: string; };}
export default async function SearchPage({ searchParams }: SearchPageProps) { const products = await searchProducts({ category: searchParams.category, minPrice: searchParams.minPrice ? parseInt(searchParams.minPrice) : undefined, maxPrice: searchParams.maxPrice ? parseInt(searchParams.maxPrice) : undefined, });
return ( <div className="container mx-auto px-4 py-8"> <h1 className="text-2xl font-bold mb-6">Product Search</h1> <ProductSearchFilters /> <Suspense fallback={<div className="text-muted-foreground">Loading...</div>}> <ProductGrid products={products} /> </Suspense> </div> );}What’s correct:
- Server Component (async function, no useState)
- Uses searchParams from URL (Next.js 14 pattern)
- Uses Server Action from lib/actions/
- TailwindCSS utility classes
- Suspense boundary for loading state
- TypeScript types for props
- No direct database access in component
Time to fix: 0 minutes. It’s correct on first generation.
Result: 25 minutes saved per feature. Over a month, that’s 10+ hours saved.
Example 2: Python FastAPI Analytics API
Prompt:
“Add an endpoint to get user analytics: total revenue, order count, average order value”
Without CLAUDE.md
from fastapi import APIRouterfrom app.database import get_db
router = APIRouter()
@router.get("/analytics/{user_id}")def get_analytics(user_id: int): db = get_db() orders = db.execute("SELECT * FROM orders WHERE user_id = ?", user_id).fetchall()
total_revenue = sum(o['amount'] for o in orders) order_count = len(orders) avg_order = total_revenue / order_count if order_count > 0 else 0
return { "total_revenue": total_revenue, "order_count": order_count, "average_order_value": avg_order }Problems:
- Synchronous function (project is async everywhere)
- Direct database query (project uses Repository pattern)
- No Service layer
- Raw SQL (project uses SQLAlchemy)
- No type hints for return value
- No Pydantic schema for response
- No error handling
Time to fix: 20 minutes
With CLAUDE.md
## Project: Analytics APIReal-time analytics API for e-commerce platform.
## Tech Stack- FastAPI + Python 3.11- SQLAlchemy + Alembic- PostgreSQL + Redis- Pydantic for validation
## Architectureapp/ ├── api/ # Routers (thin layer, just endpoints) ├── core/ # Config, dependencies ├── models/ # SQLAlchemy models ├── schemas/ # Pydantic schemas ├── services/ # Business logic └── repositories/ # Data access
## Conventions- Router → Service → Repository pattern (NEVER skip layers)- All config via Settings (Pydantic BaseSettings), never direct os.getenv()- Async everywhere (async def, await)- Type hints mandatory (mypy strict mode)- Pydantic schemas for all request/response- SQLAlchemy ORM (never raw SQL)
## Rules- NEVER commit .env file- NEVER use synchronous database queries- NEVER put business logic in routersGenerated code (with CLAUDE.md):
from pydantic import BaseModelfrom decimal import Decimal
class UserAnalytics(BaseModel): user_id: int total_revenue: Decimal order_count: int average_order_value: Decimal
# app/repositories/analytics_repository.pyfrom sqlalchemy import select, funcfrom sqlalchemy.ext.asyncio import AsyncSessionfrom app.models import Orderfrom decimal import Decimal
class AnalyticsRepository: def __init__(self, db: AsyncSession): self.db = db
async def get_user_order_stats(self, user_id: int) -> dict: query = select( func.sum(Order.amount).label('total_revenue'), func.count(Order.id).label('order_count') ).where(Order.user_id == user_id)
result = await self.db.execute(query) row = result.first()
total_revenue = row.total_revenue or Decimal('0') order_count = row.order_count or 0
return { 'total_revenue': total_revenue, 'order_count': order_count }
# app/services/analytics_service.pyfrom decimal import Decimalfrom app.repositories.analytics_repository import AnalyticsRepositoryfrom app.schemas.analytics import UserAnalytics
class AnalyticsService: def __init__(self, analytics_repo: AnalyticsRepository): self.analytics_repo = analytics_repo
async def get_user_analytics(self, user_id: int) -> UserAnalytics: stats = await self.analytics_repo.get_user_order_stats(user_id)
total_revenue = stats['total_revenue'] order_count = stats['order_count'] avg_order_value = ( total_revenue / order_count if order_count > 0 else Decimal('0') )
return UserAnalytics( user_id=user_id, total_revenue=total_revenue, order_count=order_count, average_order_value=avg_order_value )
# app/api/analytics.pyfrom fastapi import APIRouter, Dependsfrom sqlalchemy.ext.asyncio import AsyncSessionfrom app.core.dependencies import get_dbfrom app.repositories.analytics_repository import AnalyticsRepositoryfrom app.services.analytics_service import AnalyticsServicefrom app.schemas.analytics import UserAnalytics
router = APIRouter(prefix="/analytics", tags=["analytics"])
@router.get("/{user_id}", response_model=UserAnalytics)async def get_user_analytics( user_id: int, db: AsyncSession = Depends(get_db)) -> UserAnalytics: """Get analytics for a specific user.""" analytics_repo = AnalyticsRepository(db) analytics_service = AnalyticsService(analytics_repo) return await analytics_service.get_user_analytics(user_id)What’s correct:
- Async everywhere
- Proper layering (Router → Service → Repository)
- SQLAlchemy ORM (no raw SQL)
- Type hints on everything
- Pydantic schema for response
- Clean separation of concerns
Time to fix: 0 minutes.
Result: 20 minutes saved per endpoint. Over a month, that’s 8+ hours saved.
The 5 Sections Every CLAUDE.md Needs
Based on three months of iteration across Next.js, Python, Go, and mobile projects, here’s the template that works universally:
## 1. Project Overview[2-3 sentences: What is this? Who uses it? What's the core purpose?]
## 2. Tech Stack[List frameworks, languages, key libraries. Be specific with versions if it matters.]
## 3. Architecture[Show folder structure. Explain data flow. Name the patterns you use.]
## 4. Conventions[Naming rules, code style, testing patterns, formatting preferences.]
## 5. Rules (What Never To Do)[Sensitive files, banned patterns, security requirements.]Let me show you this template filled out for different project types.
CLAUDE.md for Different Project Types
Next.js + React + TypeScript
## Project: SaaS DashboardMulti-tenant SaaS dashboard for project management.
## Tech Stack- Next.js 14.2.x + TypeScript 5.4+- React Server Components + Server Actions- Prisma + PostgreSQL- TailwindCSS + shadcn/ui- NextAuth.js for authentication- Zustand for client state- React Query for server state- Zod for validation
## Architectureapp/ ├── (auth)/ # Public auth routes │ ├── login/ │ └── register/ ├── (dashboard)/ # Protected dashboard routes │ ├── projects/ │ ├── tasks/ │ └── settings/ ├── api/ # API routes (minimal) │ └── auth/ # NextAuth routes only lib/ ├── actions/ # Server Actions (all mutations) ├── db/ # Prisma client + utils ├── auth/ # NextAuth config └── utils/ # Shared utilities components/ ├── ui/ # shadcn/ui components ├── forms/ # Form components └── features/ # Feature-specific components
**Data Flow:**- Server Components fetch data directly via Prisma- Client Components use React Query for data fetching- All mutations via Server Actions (in lib/actions/)- Client state via Zustand stores (in lib/stores/)
## Conventions
**File Naming:**- Components: `PascalCase.tsx` (UserProfile.tsx)- Utils/Actions: `kebab-case.ts` (format-date.ts)- Types: `PascalCase.types.ts` (User.types.ts)
**Component Structure:**- Server Components by default- Only use "use client" when: - Need useState, useEffect, or other React hooks - Need event handlers (onClick, onChange) - Need browser APIs (window, localStorage)
**State Management:**- Server state: React Query (fetching/caching)- Client state: Zustand (UI state, modals, forms)- URL state: searchParams (for filters, pagination)
**Forms:**- React Hook Form + Zod validation- shadcn/ui Form component- Server Actions for submission
**Styling:**- TailwindCSS utility classes only- Never use CSS modules or styled-components- Use cn() helper from lib/utils for conditional classes
**Testing:**- Unit tests: Vitest- Integration tests: Playwright- Test files: `ComponentName.test.tsx`
## Rules
**NEVER:**- Commit .env.local or .env.production- Access process.env outside of lib/config- Use CSS modules or inline styles- Put business logic in components (use Server Actions or services)- Use API routes for mutations (use Server Actions)- Hardcode URLs or API endpoints- Skip error handling on Server Actions
**SENSITIVE FILES (never access):**- `.env.local` - DATABASE_URL, NEXTAUTH_SECRET, API keys- `docker-compose.yml` - Database passwords- `.env.production` - Production secrets
**WHEN WORKING ON:**- Auth: Reference lib/auth/config.ts as the canonical pattern- Forms: Reference components/forms/LoginForm.tsx as example- Database queries: Always use Prisma, check lib/db/queries.ts for patternsPython + FastAPI + SQLAlchemy
## Project: E-commerce APIRESTful API for e-commerce platform with inventory, orders, payments.
## Tech Stack- Python 3.11.7- FastAPI 0.109+- SQLAlchemy 2.0+ + Alembic- PostgreSQL 15+- Redis (caching + sessions)- Pydantic v2- pytest + pytest-asyncio
## Architectureapp/ ├── api/ │ ├── v1/ │ │ ├── endpoints/ # Route handlers │ │ └── dependencies.py # Endpoint dependencies │ └── router.py # Router aggregation ├── core/ │ ├── config.py # Settings (Pydantic BaseSettings) │ ├── database.py # DB session management │ └── security.py # Auth utilities ├── models/ # SQLAlchemy models ├── schemas/ # Pydantic schemas ├── services/ # Business logic ├── repositories/ # Data access layer └── main.py # FastAPI app tests/ ├── unit/ ├── integration/ └── conftest.py
**Data Flow:**Request → Router → Service → Repository → Database Response ← Router ← Service ← Repository ← Database
## Conventions
**File Naming:**- Models: singular (user.py, order.py)- Schemas: plural (users.py, orders.py)- Services: `{domain}_service.py`- Repositories: `{domain}_repository.py`
**Async Everywhere:**- All database operations: async def- All external API calls: async def- Use `await` for all I/O operations
**Type Hints:**- Mandatory on all functions- Use Pydantic models for complex types- Run mypy in strict mode
**Error Handling:**- Raise HTTPException in routers- Return Result types in services (Success | Error)- Log all errors with context
**Database:**- SQLAlchemy ORM only (never raw SQL)- Alembic for migrations- Use async session everywhere- Repositories handle all queries
**API Design:**- REST conventions (GET, POST, PUT, DELETE)- Plural resource names (/users, /orders)- Use Pydantic schemas for request/response- Versioned APIs (/api/v1/)
**Testing:**- pytest for all tests- Fixtures in conftest.py- Mock external services- Test files: `test_{module}.py`
## Rules
**NEVER:**- Use synchronous database calls- Put business logic in routers- Access config directly (always inject Settings)- Commit .env file- Use raw SQL (use SQLAlchemy ORM)- Skip type hints- Use print() for logging (use loguru)
**SENSITIVE FILES (never access):**- `.env` - DATABASE_URL, REDIS_URL, SECRET_KEY, STRIPE_KEY- `docker-compose.yml` - Service passwords- `credentials/` directory - Service account keys
**WHEN WORKING ON:**- Auth: Reference app/services/auth_service.py- Database: Reference app/repositories/user_repository.py for patterns- Testing: Reference tests/integration/test_users.py for examplesMobile (Kotlin Multiplatform)
## Project: Banking App (KMP)Mobile banking app for iOS and Android with shared business logic.
## Tech Stack- Kotlin Multiplatform 1.9.22- Compose Multiplatform for UI- Ktor for networking- SQLDelight for local database- Koin for dependency injection- Kotlinx.serialization for JSON- Kotlinx.coroutines for async
## Architectureshared/ ├── commonMain/ # Shared logic (business logic, network, DB) │ ├── data/ │ │ ├── api/ # API clients │ │ ├── database/ # SQLDelight │ │ └── repository/ # Repository implementations │ ├── domain/ # Business logic, use cases │ └── presentation/ # ViewModels (shared) ├── androidMain/ # Android-specific implementations │ └── kotlin/ │ └── expect/ # actual implementations ├── iosMain/ # iOS-specific implementations │ └── kotlin/ │ └── expect/ # actual implementations androidApp/ # Android UI (Compose) iosApp/ # iOS UI (SwiftUI or Compose)
**Data Flow:**UI → ViewModel → UseCase → Repository → (API | Database)
## Conventions
**Code Organization:**- Business logic in commonMain (always)- Platform code only when absolutely necessary (camera, biometrics, notifications)- Use expect/actual for platform APIs
**State Management:**- Sealed classes for all UI state ```kotlin sealed class UiState<out T> { data object Loading : UiState<Nothing>() data class Success<T>(val data: T) : UiState<T>() data class Error(val message: String) : UiState<Nothing>() }- ViewModels emit StateFlow<UiState
> - UI observes state and reacts
Networking:
- Ktor HttpClient configured in commonMain
- All API responses as sealed classes (Success | Error)
- Use kotlinx.serialization for JSON parsing
Database:
- SQLDelight for local storage
- Repository pattern for data access
- Cache-first strategy (check DB, then network)
Dependency Injection:
- Koin modules in commonMain
- Inject dependencies, never instantiate directly
Async:
- Coroutines everywhere
- Use suspend functions for async operations
- Launch coroutines in ViewModel scope
Testing:
- Unit tests in commonTest
- Use Turbine for Flow testing
- Mock repositories for ViewModel tests
Rules
NEVER:
- Put platform-specific code in commonMain
- Use GlobalScope (always use ViewModel or component scope)
- Expose mutable state (use StateFlow, never MutableStateFlow)
- Hardcode platform checks (use expect/actual)
- Skip sealed class for state (always use UiState pattern)
SENSITIVE FILES (never access):
- Android:
local.properties,google-services.json,keystore.jks - iOS:
GoogleService-Info.plist,Config.xcconfig - Shared:
secrets.properties
WHEN WORKING ON:
- Network layer: Reference shared/commonMain/data/api/ApiClient.kt
- Repository pattern: Reference shared/commonMain/data/repository/UserRepository.kt
- ViewModel: Reference shared/commonMain/presentation/auth/LoginViewModel.kt
---
### Go + Chi + PostgreSQL
```markdown# CLAUDE.md
## Project: Task Management APIRESTful API for task management with teams and projects.
## Tech Stack- Go 1.22- Chi router- sqlc for SQL generation- PostgreSQL 15- golang-migrate for migrations- go-chi/jwtauth for auth
## Architecturecmd/ └── server/ # Main application internal/ ├── api/ # HTTP handlers ├── service/ # Business logic ├── repository/ # Data access ├── auth/ # Auth middleware └── config/ # Configuration pkg/ # Public packages migrations/ # SQL migrations sqlc/ # Generated SQL code
**Data Flow:**Request → Handler → Service → Repository → DB
## Conventions
**File Naming:**- `snake_case.go` for all files
**Error Handling:**- Return errors, never panic- Wrap errors with context: `fmt.Errorf("failed to get user: %w", err)`- Use sentinel errors for expected errors
**HTTP Responses:**- JSON responses only- Use consistent envelope: ```go type Response struct { Data interface{} `json:"data,omitempty"` Error string `json:"error,omitempty"` }Database:
- Use sqlc for type-safe SQL
- Use sqlc.arg() for parameterized queries
- Transactions in service layer
- Repositories return domain models
Testing:
- Table-driven tests
- Use testify for assertions
- Mock interfaces, not structs
Rules
NEVER:
- Use
panic()in production code - Ignore errors (
_ = err) - Use
interface{}without good reason - Commit
.envfile - Use raw SQL strings (use sqlc)
SENSITIVE FILES:
.env- DATABASE_URL, JWT_SECRETdocker-compose.yml- Passwords
---
## Common Mistakes People Make with CLAUDE.md
After reviewing 50+ CLAUDE.md files from newsletter subscribers, here are the top mistakes:
### Mistake #1: Too Generic
❌ **Bad:**```markdown## Tech Stack- React- Node.js- Database✅ Good:
## Tech Stack- Next.js 14.2.x + TypeScript 5.4+- React Server Components + Server Actions- Prisma + PostgreSQL 15- TailwindCSS + shadcn/uiWhy: Generic statements don’t help. Claude Code needs specifics.
Mistake #2: No Architecture Explanation
❌ Bad:
## Conventions- Use TypeScript- Write tests✅ Good:
## Architectureapp/ ├── (auth)/ # Public routes ├── (dashboard)/ # Protected routes lib/ ├── actions/ # Server Actions (all mutations) ├── db/ # Prisma client
**Data Flow:**- Server Components fetch directly via Prisma- All mutations via Server Actions- Client state via ZustandWhy: Architecture is how pieces fit together. This prevents Claude Code from inventing its own structure.
Mistake #3: Listing Every Library
❌ Bad:
## Tech Stack- React, React-DOM, React-Router, React-Query, React-Hook-Form, React-Select, React-Datepicker, React-Icons, React-Toastify, React-Modal, React-Tooltip...✅ Good:
## Tech Stack- Next.js 14 + TypeScript- TailwindCSS + shadcn/ui- React Query (server state)- Zustand (client state)Why: List only the libraries that affect architectural decisions. Claude Code will discover utility libraries from your package.json.
Mistake #4: No “Rules” Section
❌ Bad:
[No rules section at all]✅ Good:
## Rules
**NEVER:**- Commit .env files- Use CSS modules (TailwindCSS only)- Put business logic in components- Access process.env outside lib/config
**SENSITIVE FILES:**- .env.local - DATABASE_URL, STRIPE_KEYWhy: The “Rules” section prevents mistakes that require manual cleanup. This is the highest ROI section.
Mistake #5: Documentation Instead of Instructions
❌ Bad:
## ProjectThis project was created in 2023 to solve the problem of task management.We initially considered using Redux but decided on Zustand because of bundle size.The team discussed this in meeting notes from Q2 2023.✅ Good:
## ProjectTask management app for remote teams.
## State Management- Zustand for client state (not Redux)- React Query for server stateWhy: CLAUDE.md is instructions for Claude Code, not project history. Be directive, not narrative.
Mistake #6: Putting Implementation Details
❌ Bad:
## API IntegrationThe user endpoint is at /api/users and returns JSON with fields:{ id: number, name: string, email: string, createdAt: string }✅ Good:
## Conventions- All API calls via lib/api/client.ts- API responses must be validated with Zod schemasWhy: Claude Code will read your actual API code. CLAUDE.md is for conventions and patterns, not implementation details.
The Compound Effect: How CLAUDE.md Grows
Here’s what nobody tells you: CLAUDE.md is a living document. It compounds over time.
Week 1 (Setup):
- Write initial CLAUDE.md: 20 lines
- Covers tech stack, basic conventions
- Time invested: 10 minutes
Week 2 (First corrections):
- Claude Code uses wrong naming pattern → add naming rules
- Claude Code puts logic in wrong layer → add architecture section
- Lines: 30
- Time invested: 5 minutes
Week 4 (Refinement):
- Claude Code tries to use banned library → add to Rules section
- Claude Code generates tests in wrong format → add testing conventions
- Lines: 45
- Time invested: 5 minutes
Week 8 (Mature):
- CLAUDE.md now covers 80% of common scenarios
- Claude Code generates correct code on first try most of the time
- Lines: 60
- Time invested: 0 minutes (no corrections needed)
Month 3 (Stable):
- CLAUDE.md is comprehensive
- New team members copy it for their projects
- Claude Code output quality is excellent
- Lines: 80
- Time invested: 0-2 minutes per month (rare additions)
Total time invested: ~30 minutes Time saved per month: 15-20 hours (from reduced corrections) ROI: 30x+
Real Story: KMP Banking App
Let me show you a real example from a project I worked on.
Project: Banking app for Vietnamese bank, built with Kotlin Multiplatform.
Initial state (no CLAUDE.md):
Claude Code kept generating code with these issues:
- Platform-specific code in
commonMain/(wrong) - Direct network calls in ViewModels (should go through UseCases)
- Mutable state exposed (
MutableStateFlowinstead ofStateFlow) - Wrong testing pattern (wasn’t using Turbine for Flow tests)
- Hardcoded platform checks (
if (Platform.isAndroid)) instead of expect/actual
Every feature took 3-4 revision cycles:
- Claude Code generates code
- I review and spot architectural issues
- I explain corrections
- Claude Code fixes
Each revision: 15-20 minutes. Per feature: ~1 hour of corrections.
After adding CLAUDE.md:
## Project: BankApp (KMP)Banking app with biometric auth, transfers, bill payments.
## Architecture- Shared business logic in commonMain- Platform code ONLY for: biometrics, notifications, camera- Use expect/actual for platform APIs (never Platform.isAndroid checks)
## State Pattern```kotlinsealed class UiState<out T> { data object Loading : UiState<Nothing>() data class Success<T>(val data: T) : UiState<T>() data class Error(val message: String) : UiState<Nothing>()}Conventions
- UI → ViewModel → UseCase → Repository → (API | DB)
- ViewModels emit StateFlow (never expose MutableStateFlow)
- All async via suspend functions
- Dependency injection via Koin
Testing
- Use Turbine for Flow testing
- Example:
viewModel.uiState.test { assertThat(awaitItem()).isInstanceOf<Loading>() }
Rules
- NEVER put platform-specific code in commonMain
- NEVER use GlobalScope
- NEVER hardcode platform checks (use expect/actual)
**Result:**
After adding this (took 12 minutes), the revision cycles dropped to 1:1. Claude Code generates code → correct on first try
Features that took 1 hour now take 15 minutes.
**Numbers:**- Before CLAUDE.md: 3.5 revisions per feature, 1 hour corrections- After CLAUDE.md: 1.2 revisions per feature, 10 minutes corrections- Time saved per feature: 50 minutes- Features per week: ~5- **Time saved per week: ~4 hours**- Total time to write CLAUDE.md: 12 minutes
ROI: 20x in first week, 80x over the month.
---
## Advanced: Team CLAUDE.md
If you work on a team, CLAUDE.md becomes even more valuable. It's not just instructions for Claude Code — it's shared team conventions in a machine-readable format.
**Team workflow:**
1. **Start with a base template** — the senior dev writes the initial CLAUDE.md2. **Everyone uses it** — all team members rely on the same CLAUDE.md3. **Update together** — when someone discovers a better pattern, they update CLAUDE.md4. **Version control it** — CLAUDE.md is committed to git, reviewed in PRs
This creates a feedback loop:- New patterns are documented immediately- Everyone's AI assistant follows the same conventions- Code reviews focus on logic, not style (because Claude Code follows style automatically)- New team members get up to speed faster (Claude Code teaches them the patterns)
**Real example from a 6-person team:**
Before CLAUDE.md:- Code reviews had constant style debates- Each developer's Claude Code sessions generated different patterns- PRs required 2-3 rounds of style fixes
After shared CLAUDE.md:- Style is consistent across all Claude Code output- Code reviews focus on logic and architecture- PRs merge faster (1-2 rounds max)
**Time saved per person per week:** ~3 hours**Time saved for team per week:** 18 hours**Time to set up:** 30 minutes
---
## How to Start Right Now
Don't overthink this. You can start in 5 minutes.
**Step 1:** Create `CLAUDE.md` in your project root
**Step 2:** Copy this minimal template:
```markdown# CLAUDE.md
## Project: [Name][One sentence: what does this project do?]
## Tech Stack- [Framework] + [Language]- [Key library #1]- [Key library #2]- [Key library #3]
## Architecture[Show folder structure OR explain data flow in 2-3 sentences]
## Conventions- [Top convention #1]- [Top convention #2]- [Top convention #3]
## Rules**NEVER:**- [Dangerous thing #1]- [Dangerous thing #2]
**SENSITIVE FILES:**- [File with secrets #1]- [File with secrets #2]Step 3: Fill in the brackets (5 minutes)
Step 4: Start a Claude Code session
Claude Code will read it automatically. You’ll see the difference immediately.
When CLAUDE.md Compounds
Here’s the timeline of returns:
| Time | State | Benefit |
|---|---|---|
| Day 1 | Basic CLAUDE.md (20 lines) | 20% fewer corrections |
| Week 1 | Added 3 corrections | 40% fewer corrections |
| Week 2 | Added 5 more corrections | 60% fewer corrections |
| Month 1 | Comprehensive (60+ lines) | 80% fewer corrections |
| Month 3 | Stable, rarely changes | 90% fewer corrections |
This is why CLAUDE.md is the most important file. It’s a one-time investment that pays dividends forever.
CLAUDE.md vs Other Documentation
You might be thinking: “Isn’t this just README.md?”
No. Here’s the difference:
| File | Audience | Purpose | Format |
|---|---|---|---|
| README.md | Humans | Project overview, setup instructions | Prose |
| CONTRIBUTING.md | Humans | How to contribute | Prose |
| ARCHITECTURE.md | Humans | Design decisions, diagrams | Prose |
| CLAUDE.md | AI | Instructions for code generation | Directives |
README.md explains what the project is. CLAUDE.md tells Claude Code what to do.
Example:
README.md (for humans):
This project uses Next.js with TypeScript. We chose Server Components because they improve performance and reduce client bundle size.
CLAUDE.md (for AI):
Conventions
- Server Components by default, “use client” ONLY when necessary
- All mutations via Server Actions (never API routes)
See the difference? CLAUDE.md is imperative. README.md is descriptive.
The Bottom Line
Claude Code has no memory between sessions. Every conversation starts from zero. Without CLAUDE.md, you waste 20-40 minutes every session explaining the same conventions, fixing the same patterns, correcting the same mistakes.
CLAUDE.md solves this. Write it once. Five minutes. Claude Code reads it automatically at the start of every session. Your output quality jumps 3-5x. Your correction time drops 80%. Over a month, this saves 15-20 hours.
Three numbers:
- Time to write: 5-10 minutes
- Time saved per week: 4-8 hours
- ROI: 20x+ in first month
This is the highest-leverage thing you can do for AI-assisted development.
Start with the minimal template. Add to it as you go. In three months, you’ll have a CLAUDE.md that makes your AI assistant as knowledgeable about your project as any senior team member.
What Goes in YOUR CLAUDE.md?
Every project is different. But every CLAUDE.md should answer five questions:
- What is this project? (1 sentence)
- What’s the tech stack? (frameworks, libraries)
- What’s the architecture? (layers, data flow, patterns)
- What are the conventions? (naming, structure, style)
- What should never be done? (security, banned patterns)
Answer those five questions and you have a CLAUDE.md that will save you dozens of hours.
Verdict: Project Type Recommendations
| Project Type | Key Sections | Priority Details |
|---|---|---|
| Next.js/React | Architecture, State Management, Server Components | ⭐⭐⭐⭐⭐ Critical to specify Server vs Client |
| Python/FastAPI | Layering (Router→Service→Repo), Async, Type Hints | ⭐⭐⭐⭐⭐ Layering prevents most mistakes |
| Mobile (KMP) | commonMain rules, expect/actual, State pattern | ⭐⭐⭐⭐⭐ Platform boundaries are easy to cross wrong |
| Go | Error handling, Interface patterns | ⭐⭐⭐⭐ Go conventions are strict |
| Backend API | Repository pattern, Auth flow | ⭐⭐⭐⭐ Prevents security mistakes |
Bonus: Free CLAUDE.md Templates
I’ve created 12 production-ready CLAUDE.md templates for the most common project types:
- Next.js 14 + App Router + TypeScript
- Next.js + Prisma + tRPC
- Python + FastAPI + SQLAlchemy
- Python + Django + DRF
- Kotlin Multiplatform Mobile
- React Native + Expo
- Go + Chi + PostgreSQL
- Node.js + Express + TypeScript
- Monorepo (Turborepo + pnpm)
- Chrome Extension (Plasmo)
- Electron + React
- Flutter
These templates are based on real production projects. Each one is 60-100 lines of battle-tested conventions.
They’re part of the Template Pack (coming in Phase 15 of the course). But if you join the newsletter now, I’ll send you 3 templates immediately:
- Next.js 14 full-stack
- Python FastAPI
- KMP Mobile
Plus the Claude Code Cheat Sheet — 50+ commands in one PDF.
Get the free templates + cheat sheet →
Go Deeper
This is just the beginning. CLAUDE.md is Phase 4, Module 2 in the Claude Code Mastery course.
Phase 4 covers:
- Module 1: Prompt Engineering Techniques
- Module 2: CLAUDE.md — Project Memory (this article)
- Module 3: Slash Commands
- Module 4: Memory System
The course has 16 phases, 55 modules, 200+ topics. It’s the most comprehensive Claude Code training available. Phases 1-3 are completely free.
Want the cheat sheet? Join 2,000+ developers getting weekly Claude Code tips. Get the free Claude Code Cheat Sheet (50+ commands) when you subscribe.