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:
- token cost is real. every word has a price.
- specificity beats aspiration. concrete rules change behavior; vague goals don’t.
- structure aids parsing. headers and lists beat paragraphs.
- freshness matters. stale instructions are negative value.
- alternatives beat prohibitions. “don’t do X” needs “do Y instead.”
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