Examples: Bad vs Good CLAUDE.md
Learn what makes a CLAUDE.md effective by seeing real examples of bad vs good patterns.
Example 1: The Vague Project Description
❌ Bad
# My App
This is a cool web app I'm building. It does some stuff with users and data.
Problems:
- No tech stack mentioned
- "Cool" and "stuff" are useless descriptors
- Claude has no idea what this actually does
✅ Good
# TaskFlow - Team Task Management
A SaaS task management tool for remote teams. Users create projects, assign tasks, and track progress in real-time.
## Tech Stack
- Backend: Node.js 20 + Express + PostgreSQL via Prisma
- Frontend: React 18 + TypeScript + TailwindCSS
- Real-time: WebSockets (Socket.io)
- Auth: JWT tokens via Supabase
- Deployment: Docker containers on AWS ECS
Why it's good:
- Clear one-sentence description
- Specific tech stack with versions
- Claude knows exactly what kind of application this is
Example 2: The Wishful Thinking Rules
❌ Bad
## Code Style
- Write clean code
- Use good variable names
- Make it performant
- Follow best practices
Problems:
- "Clean," "good," "best practices" are subjective
- No actionable guidance
- Claude can't verify compliance
✅ Good
## Code Style
- TypeScript strict mode — no `any` types, ever
- Variables: descriptive nouns (e.g., `userEmail` not `data`)
- Functions: start with verbs (e.g., `getUserById`, not `user`)
- Async/await only — never use `.then()` chains
- Imports: absolute paths with `@/` alias, never relative `../../`
- Max function length: 50 lines. If longer, extract functions.
Why it's good:
- Specific, measurable rules
- Examples provided
- Claude can check compliance automatically
Example 3: The Missing Structure
❌ Bad
# My Project
Built with React and Node.js.
The code is in the src folder.
Problems:
- Doesn't explain how src is organized
- Claude will have to explore every time
- No guidance on where to put new code
✅ Good
## Project Structure
/src /api - Express route handlers (thin, no business logic) /services - Business logic layer (all domain operations) /db - Database access via Prisma (all queries here) /lib - Shared utilities (logger, config, http client) /types - TypeScript type definitions /middleware - Express middleware (auth, error handling) /tests - Mirror structure of /src /prisma - Database schema and migrations
### Architecture Rules
- API routes call services, never access DB directly
- Services return `Result<T, AppError>` (never throw)
- All cross-service communication via event bus
- Database queries only in /db repositories
### Key Files
- Server entry: /src/server.ts
- Auth middleware: /src/middleware/auth.ts
- DB schema: /prisma/schema.prisma
- Environment config: /src/lib/config.ts
Why it's good:
- Visual directory structure
- Explains what goes in each directory
- Architecture rules prevent common mistakes
- Key files listed for easy reference
Example 4: The Ambiguous Commands
❌ Bad
## Commands
Run the app and do the tests.
Problems:
- What's the actual command?
- How to install dependencies?
- How to run in dev vs production?
✅ Good
## Development Commands
```bash
# Install dependencies
bun install
# Start dev server (hot reload on port 3000)
bun run dev
# Run tests once
bun test
# Run tests in watch mode
bun test --watch
# Lint
bun run lint
# Type check
bun run typecheck
# Build for production
bun run build
# Database migrations
bunx prisma migrate dev # Dev: create and apply
bunx prisma migrate deploy # Prod: apply only
Workflow
- After making changes, run
bun testbefore committing - Never commit if tests fail
- Run
bun run lintto auto-fix style issues
**Why it's good:**
- Exact commands copy-pasteable
- Explains what each does
- Includes workflow guidance
---
## Example 5: The Toothless Restrictions
### ❌ Bad
```markdown
## Rules
- Don't do bad things
- Try not to use console.log
- Avoid making mistakes
Problems:
- "Try not to" isn't enforceable
- "Bad things" is meaningless
- No consequences specified
✅ Good
## Never Do This
- ❌ Never use `console.log` — use `logger.info()` from /src/lib/logger.ts
- ❌ Never commit .env files — use .env.example as template
- ❌ Never push directly to main — always create a PR
- ❌ Never use `any` in TypeScript — use `unknown` if type is truly unknown
- ❌ Never write raw SQL — use Prisma client methods
- ❌ Never use `var` — use `const` (preferred) or `let`
- ❌ Never add dependencies without checking bundle size impact
If I tell you to do any of these, push back and explain why it violates our rules.
Why it's good:
- Absolute rules (Never, not "try to avoid")
- Explains the alternative for each
- Empowers Claude to push back
Example 6: The Missing Context
❌ Bad
# E-Commerce Site
We sell products online.
Problems:
- No mention of scale, constraints, or special considerations
- Claude doesn't know what's important
- Missing domain-specific terminology
✅ Good
# ShopFast - Multi-Vendor E-Commerce Platform
An e-commerce marketplace where independent vendors list products. Platform handles payments, takes 15% commission, and manages vendor payouts.
## Scale & Constraints
- ~10k active vendors
- ~100k daily active users
- Payment processing via Stripe Connect
- Payout schedule: monthly, 1st of each month
- Must support multi-currency (USD, EUR, GBP)
## Domain Glossary
- **Vendor** = businesses selling on our platform (not just any user)
- **Commission** = our 15% fee on each sale
- **Payout** = monthly payment from platform to vendor
- **Settlement** = the batch processing of all payouts
- **Float** = money held between sale and payout
## Critical Constraints
- Financial: All money math uses integer cents (never floats)
- Security: Vendor data is isolated (RLS in PostgreSQL)
- Compliance: GDPR-compliant data exports within 30 days
- Performance: Product search must return under 200ms (indexed)
Why it's good:
- Provides business context
- Defines domain-specific terms
- Lists scale and critical constraints
- Prevents common mistakes (float math for money)
Example 7: The Git Disaster Waiting to Happen
❌ Bad
## Git
Use git for version control.
Problems:
- No branching strategy
- No commit conventions
- No protection against dangerous operations
✅ Good
## Git Workflow
### Branch Naming
- Feature: `feat/short-description`
- Bug fix: `fix/short-description`
- Hotfix: `hotfix/critical-issue`
### Commit Messages
Use conventional commits:
feat(scope): add user export feature fix(auth): resolve token refresh race condition chore(deps): update dependencies docs(readme): add setup instructions
Keep first line under 72 characters.
### Protected Operations
- ✅ Always create a PR for main/develop
- ✅ Always run tests before committing
- ✅ Always pull before push
- ❌ Never `git push --force` to main/develop
- ❌ Never `git commit --no-verify` (bypasses hooks)
- ❌ Never merge main into feature branches (use rebase)
### Workflow
1. Create feature branch from develop
2. Make changes, commit frequently
3. Run full test suite
4. Create PR (requires 1 approval + passing CI)
5. After approval, squash merge to develop
Why it's good:
- Clear branching strategy
- Commit message format specified
- Dangerous operations explicitly forbidden
- Complete workflow documented
Example 8: The Testing Void
❌ Bad
## Testing
We have some tests. Run them sometimes.
Problems:
- No guidance on what to test
- No test structure
- No coverage expectations
✅ Good
## Testing Standards
### Test Organization
- Unit tests: colocated with source (e.g., `user.service.test.ts` next to `user.service.ts`)
- Integration tests: `/tests/integration/`
- E2E tests: `/tests/e2e/`
### What to Test
**Unit tests:**
- All service methods
- Pure utility functions
- Complex business logic
- Edge cases and error handling
**Integration tests:**
- Database operations (use test DB)
- API endpoints (full request/response cycle)
- Auth flows
**E2E tests:**
- Critical user journeys (signup, purchase, payout)
- Cross-feature workflows
### Coverage Requirements
- Minimum: 80% overall coverage
- Critical paths: 100% (payment, auth, payout)
- New features: include tests in the same PR
### Test Patterns
```typescript
// Always use AAA pattern
describe('UserService', () => {
it('should create user with valid data', async () => {
// Arrange
const userData = { email: 'test@test.com', ... }
// Act
const result = await userService.create(userData)
// Assert
expect(result.success).toBe(true)
})
})
Running Tests
- Before committing:
bun test - Watch mode while developing:
bun test --watch - CI must be green before merge
**Why it's good:**
- Defines what to test and where
- Sets coverage expectations
- Provides test pattern template
- Integrates testing into workflow
---
## Common Patterns to Avoid
### ❌ Platitudes
```markdown
Write high-quality code.
Be efficient.
Think about the user.
These mean nothing. Be specific.
❌ Contradictions
Use TypeScript strict mode.
It's okay to use `any` sometimes.
Pick one. Be consistent.
❌ Information Without Context
The webhook handler is at /api/webhooks/stripe.ts
Why is this important? When would Claude need this?
Better:
## Critical Files
- Stripe webhooks: /api/webhooks/stripe.ts
- Handles: payment_intent.succeeded, subscription updates
- Must respond within 30 seconds or Stripe retries
- Updates DB and triggers notification emails
❌ Commands Without Explanation
Run: npm run special-build
What does this do? When should it be used?
Better:
Build Commands:
- `npm run build` - Standard production build
- `npm run build:debug` - Build with source maps (larger, for debugging prod issues)
- `npm run build:analyze` - Build + bundle analysis (use to check bundle size)
The Test: Would a New Team Member Understand?
A good CLAUDE.md should let a new developer (or Claude) understand your project without asking questions.
Read your CLAUDE.md and ask:
- Could someone build a new feature with just this context?
- Are the rules specific enough to be enforced?
- Is the project structure clear?
- Are commands copy-pasteable?
- Are forbidden patterns explicit?
If not, keep refining.
Your Turn
Take your current CLAUDE.md and check it against these examples. Find at least 3 places where you're being vague, and make them specific.
Vague → Specific transformations to look for:
- "Good naming" → "Variables are nouns, functions start with verbs"
- "Use the logger" → "Never use console.log — use logger.info() from /lib/logger.ts"
- "Files are in src" → Full directory tree with explanations
- "Run tests" → Exact command with flags and when to use it
- "Don't break things" → Explicit list of forbidden operations
Your CLAUDE.md should be your project's source of truth. Make it count.