why your CLAUDE.md sucks

Table of content

by Ray Svitla


I’ve read probably 200 CLAUDE.md files at this point. community repos, client projects, open-source packages, people’s dotfiles. the median CLAUDE.md is actively making Claude Code worse at its job.

not because people are bad at writing instructions. because writing good instructions for AI agents is a different skill than writing documentation, and almost nobody treats it that way.

here’s what’s wrong with yours.


anti-pattern 1: the novel

# Welcome to Our Project!

This is a comprehensive guide to understanding our codebase. 
We've been building this application since 2023 and it's grown 
significantly over time. Our team of 5 engineers works across 
multiple features and we value clean code, thorough testing, 
and clear communication...

[2000 more words of backstory nobody asked for]

your CLAUDE.md is not a README. it’s not onboarding documentation. it’s not a love letter to your codebase. it’s an instruction file that burns tokens on every single session.

every word in your CLAUDE.md costs context. literally. the model reads this file before doing anything. a 5000-word essay about your company’s engineering philosophy displaces 5000 words of actual task context.

fix: ruthlessly cut anything that doesn’t change Claude’s behavior. if removing a sentence wouldn’t change the output, delete it.


anti-pattern 2: the wish list

- write clean, maintainable, well-documented code
- follow best practices
- consider edge cases
- write comprehensive tests
- be thoughtful about performance

congratulations. you’ve described what every developer wants. you’ve told Claude nothing it doesn’t already default to. this is the CLAUDE.md equivalent of a mission statement on a startup’s About page — technically true, functionally meaningless.

fix: replace aspirations with specifics.

- functions must not exceed 30 lines
- every async function needs try/catch with error context
- tests: one happy path + one error case minimum per function
- use zod for all API input validation, not manual checks

specifics change behavior. aspirations don’t.


anti-pattern 3: negative-only rules

- NEVER use any
- NEVER use console.log
- NEVER commit directly to main
- NEVER use var
- DO NOT use default exports

Claude now knows five things it can’t do and zero things it should do instead. when it encounters a situation where any seems necessary, it gets stuck. it knows it shouldn’t, but you haven’t told it what to do instead.

as Shrivu Shankar notes , negative-only constraints create dead ends. Claude needs escape routes.

fix: every “never” needs an “instead.”

- never use `any` — use `unknown` and narrow with type guards, 
  or create a typed interface in types/
- never use console.log in production code — use the logger 
  from lib/logger.ts
- never commit to main — create a feature branch with format: 
  feat/short-description

anti-pattern 4: the @-file dump

For coding standards, see @docs/coding-standards.md
For API documentation, see @docs/api-reference.md  
For deployment process, see @docs/deployment.md
For testing guidelines, see @docs/testing-guide.md
For architecture decisions, see @docs/adr/

two problems. if you @-reference files, their entire contents get loaded into context on every session, eating tokens for information that’s only relevant sometimes. if you just mention paths without @, Claude often ignores them because you haven’t explained when or why to look.

fix: summarize the critical bits inline. reference files only for deep-dives, and explain when to consult them.

## coding standards
- TypeScript strict, no any, prefer interfaces over types
- async/await, never raw promises
- for complex patterns or edge cases, see docs/coding-standards.md

## testing
- jest for unit tests, playwright for e2e
- run: npm test (unit), npm run test:e2e (integration)
- if tests fail with database errors, see docs/testing-guide.md 
  for local DB setup

anti-pattern 5: contradictions

- always write comprehensive tests for every function
- keep sessions fast and minimize token usage
- explore the codebase thoroughly before making changes
- make changes quickly without overthinking

comprehensive tests and minimal token usage are in tension. thorough exploration and quick changes are in opposition. Claude will oscillate between these competing directives, satisfying none of them consistently.

fix: establish clear priorities.

## priorities (in order)
1. correctness — working code with error handling
2. tests — happy path + primary error case
3. speed — don't explore unrelated code, focus on the task

skip comprehensive test coverage unless explicitly requested.

anti-pattern 6: stale instructions

- use the legacy auth system in auth/v1/
- API base URL is https://api-staging.example.com
- run migrations with: python manage.py migrate --database=legacy

you migrated to auth v2 three months ago. the staging URL changed in January. the legacy database is gone. but your CLAUDE.md still points Claude to the graveyard of your previous architecture.

stale CLAUDE.md instructions don’t just waste tokens. they actively mislead. Claude will try to use the legacy auth system, fail, and spend tokens debugging a problem you created.

fix: review your CLAUDE.md monthly. delete anything that no longer reflects reality. put a comment at the top:

# last reviewed: 2026-02-15

anti-pattern 7: no structure

use typescript. run tests with npm test. the database is postgres. 
we deploy to vercel. use prisma for database access. don't use 
default exports. the API is in src/api/. tests go next to their 
source files. use vitest not jest. environment variables are in 
.env.local. never commit .env files.

this is a stream of consciousness, not an instruction file. Claude can parse it, but it’s harder to scan, prioritize, and follow than structured information.

fix: use headers, bullet points, and logical grouping.

## stack
- TypeScript, Prisma, Vitest, Vercel

## commands
- `npm test` — run tests
- `npm run dev` — dev server
- `npx prisma migrate dev` — database migrations

## conventions
- tests: colocated with source files (foo.test.ts next to foo.ts)
- exports: named exports only, no default
- env: .env.local (never committed)

## architecture
- API routes: src/api/
- database: PostgreSQL via Prisma

same information. ten times more useful.


the meta-lesson

your CLAUDE.md is a context engineering artifact. it’s not documentation for humans. it’s programming for an AI agent. the rules are different:

treat your CLAUDE.md like code. refactor it. review it. delete the dead parts. test whether changes improve output.

the best CLAUDE.md is the shortest one that produces the behavior you want. everything else is noise.


CLAUDE.md deep dive — how to write it right → context engineering — the bigger picture → Shrivu Shankar on Claude Code features — real-world CLAUDE.md philosophy


Ray Svitla stay evolving