Your CLAUDE.md Is Working Against You
The file that tells your AI assistant how to code is probably full of contradictions, stale references, and wasted tokens. Here’s how to…
Your CLAUDE.md Is Working Against You
The file that tells your AI assistant how to code is probably full of contradictions, stale references, and wasted tokens. Here’s how to fix it — and an open-source tool that does the auditing for you.
The Silent Saboteur in Your Repo
Here’s something nobody talks about in the Claude Code community: your CLAUDE.md file is probably making Claude worse at its job, not better.
Last week I was helping someone debug a strange pattern. Claude Code kept generating Express.js middleware in a project that had migrated to Hono three months ago. The code was syntactically correct, the patterns were sound — but it was for the wrong framework. Every single time.
We opened CLAUDE.md. Line 23: “API framework: Express.js with middleware pattern.” Nobody had updated it after the migration. Claude was dutifully following outdated instructions.
This isn’t an edge case. It’s the norm. Most CLAUDE.md files are written once and never maintained. They accumulate contradictions, reference files that no longer exist, and burn through precious context window tokens with vague instructions like “write clean code” that change absolutely nothing about Claude’s behavior.
Your CLAUDE.md is supposed to be your AI assistant’s operating manual. Instead, it’s probably a source of confusion that’s silently degrading the quality of every response you get.
CLAUDE.md 101: What It Is and Why It Matters
For readers who are newer to Claude Code: CLAUDE.md is a markdown file that lives in your repository and tells Claude how to behave when working on your project. It’s essentially a persistent system prompt that gets loaded automatically every time you start a conversation.
What makes it interesting is the hierarchy system. Claude Code reads CLAUDE.md files from multiple locations, with more specific files overriding more general ones:
~/.claude/CLAUDE.md # Global — applies to every project
./CLAUDE.md # Project root — team-wide conventions
./src/CLAUDE.md # Directory-level — more specific
./src/api/CLAUDE.md # Deeper nesting — most specific
The analogy is CSS specificity. Your global file sets defaults. Your project root overrides those defaults. A nested CLAUDE.md in your API directory gives Claude completely different rules when it’s working on API code versus frontend code. This is how monorepos with a React frontend and a Python backend can give Claude appropriate context for each part of the codebase.
When it works well, it’s powerful. A clean CLAUDE.md means Claude uses the right package manager, follows your naming conventions, writes tests the way your team writes them, and references files that actually exist.
When it doesn’t work well — and this is the case for most projects — it means Claude is juggling contradictory instructions, wasting context on irrelevant rules, and confidently following outdated patterns because your CLAUDE.md told it to.
The Central Tension: Configuration Drift
Here’s the uncomfortable truth at the heart of this problem.
We’ve spent decades learning to lint our code, type-check our interfaces, and validate our configuration files. We have ESLint for JavaScript. Stylelint for CSS. hadolint for Dockerfiles. mypy for Python. Every configuration file that matters has a tool that checks it for correctness.
Except the one that configures your AI assistant.
CLAUDE.md is, functionally, the most-read configuration file in your codebase. Claude loads it on every conversation. If you run Claude Code 20 times a day, that file gets parsed 20 times. And yet it lives outside every quality gate you’ve built: no linting, no CI check, no PR review template, no automated staleness detection.
The result is configuration drift — the same phenomenon that plagues infrastructure, applied to AI behavior. Your CLAUDE.md slowly diverges from reality. Instructions that were correct six months ago become harmful. New conventions get added without removing the old ones. Nobody notices because the symptoms (inconsistent AI output) don’t obviously trace back to the cause (a stale config file).
This drift has real costs. Developers waste time re-prompting Claude to override bad instructions they don’t know exist. Teams lose trust in Claude Code because it “keeps doing the wrong thing.” New team members copy the existing CLAUDE.md — contradictions and all — into new projects, spreading the problem.
The Seven Dimensions of CLAUDE.md Health
After studying this problem across dozens of codebases, I’ve identified seven distinct ways a CLAUDE.md file breaks down. These aren’t hypothetical — they’re patterns I’ve seen repeatedly, and they form the analytical framework behind claudemd-lint, the open-source tool we built to catch them automatically.
1. Consistency — When Your File Contradicts Itself
The most damaging failure. Your CLAUDE.md says “use npm” in one section and “run pnpm install" in another. Claude tries to follow both. Sometimes you get npm commands, sometimes pnpm. Your lockfiles diverge. You blame Claude.
This happens most often after tool migrations (npm to pnpm, Jest to Vitest, Express to Hono) where the old instruction survives alongside the new one.
2. Staleness — When References Point to Ghosts
File paths that no longer exist. Framework references that were replaced quarters ago. The telltale sign: parenthetical apologies like “(we migrated away from this in Q3).” If you have to annotate that something is outdated, delete it.
3. Redundancy — When You State the Obvious
“Write clean, maintainable code.” “Follow best practices.” “Use meaningful variable names.” Claude already does all of this by default. These instructions consume context tokens without changing behavior. They’re the AI equivalent of telling a chef to “use fresh ingredients.” They were going to do that anyway.
4. Scope Specificity — When Rules Land in the Wrong File
Tailwind CSS instructions in your API directory’s CLAUDE.md. Database migration rules in a frontend-only config. Every out-of-scope instruction means Claude loads irrelevant context for the current task, pushing more relevant rules further from attention.
5. Token Efficiency — When Prose Replaces Precision
A 75-word paragraph about your testing philosophy that could be three bullet points with 19 words. Every extra token in CLAUDE.md is one fewer token available for your actual conversation. The math is unforgiving: bloated instructions directly reduce the space Claude has to reason about your code.
6. Actionability — When Instructions Are Too Vague to Follow
“Handle errors properly.” “Use appropriate patterns.” “Follow conventions.” These pass a human reading test but fail the machine execution test. Claude needs specifics: which error class, which logging function, which pattern, applied where.
7. Maintainability — When There’s No Structure to Maintain
No headings. No sections. A stream-of-consciousness list of rules in no particular order. When a teammate needs to add a new rule, there’s no obvious place for it. It goes at the bottom. Over time, the file becomes an archaeological dig where you can read the team’s history in the order of the rules.
What Good Looks Like: Before and After
Theory is useful. Examples are better. Here are two transformations that illustrate the difference between a CLAUDE.md that’s working against you and one that’s working for you.
The Contradictory Monorepo
Before:
# Project Rules
Use ESLint for linting all code. TypeScript strict mode is required.
We use Jest for testing. Run tests with npm test. All packages use
yarn workspaces. Install dependencies with yarn add. Make sure to
run npm run lint before committing. Our CI uses pnpm for faster builds.
Three package managers (npm, yarn, pnpm). No structure. Unclear which instruction wins.
After:
# Project Rules
## Tech Stack
- Monorepo: pnpm workspaces
- Language: TypeScript (strict mode)
- Linting: ESLint — run `pnpm lint`
- Testing: Jest — run `pnpm test`
## Commands
- Install: `pnpm install`
- Add dependency: `pnpm add <pkg> --filter <workspace>`
- Lint: `pnpm lint`
- Test: `pnpm test`
One package manager. Explicit commands. Structured sections. Claude will never generate a yarn add command again.
The Prose Wall
Before (127 words):
## API Design
When designing API endpoints, we follow REST conventions as much as
possible. All endpoints should return JSON. Use proper HTTP status
codes — 200 for success, 201 for creation, 400 for bad requests,
401 for unauthorized, 404 for not found, and 500 for server errors.
Request validation is important and should be done at the controller
level using Zod schemas. We prefer to keep controllers thin and push
business logic into service files. Error responses should follow a
consistent format with a message field and optional details field.
Always include pagination for list endpoints using cursor-based
pagination with a limit parameter.
After (52 words, same information):
## API Design
- REST + JSON, standard HTTP status codes (200/201/400/401/404/500)
- Validate requests with Zod at the controller level
- Thin controllers — business logic in `src/services/`
- Error response format: `{ message: string, details?: object }`
- List endpoints: cursor-based pagination with `limit` param
60% fewer tokens. Zero information lost. Claude finds the relevant rule immediately instead of parsing a paragraph.
claudemd-lint: Automated Auditing for Your AI Config
You could do all of this manually. Open your CLAUDE.md, check every file reference, hunt for contradictions, compress every paragraph. It works once. But configuration drift means you’ll need to do it again next month, and the month after that.
This is exactly the problem linters solve. So we built one.
**claudemd-lint** is an open-source CLI tool that scores your CLAUDE.md across all seven dimensions, from 1 to 10, and produces an overall health score with line-level recommendations.
Install it:
npm install -g claudemd-lint # Node.js 20+ required
Run it:
claudemd-lint ./CLAUDE.md
Sample output:
claudemd-lint v1.0.0 — CLAUDE.md Health Report
File: ./CLAUDE.md (2.1 KB, 847 tokens)
Dimension Score Issues
───────────────────── ─────── ──────────────────────────────
Consistency 6/10 2 contradictions found
Staleness 4/10 3 file references not found
Redundancy 5/10 4 boilerplate phrases detected
Scope Specificity 8/10 1 out-of-scope rule
Token Efficiency 3/10 38% of tokens are filler
Actionability 7/10 2 vague instructions
Maintainability 9/10 Well-structured with headings
Overall Health Score: 6.0/10
Top 3 Priority Fixes:
1. [TOKEN] Lines 12-28: Prose paragraph can be reduced by 65%
2. [STALE] Line 34: `src/utils/helpers.ts` not found on disk
3. [CONSISTENCY] Lines 5,41: Conflicting package managers (npm vs pnpm)
Every issue comes with a line number, a dimension label, and a specific fix. No vague guidance.
Auto-Fix: Six Specific Operations
The --fix flag runs six targeted fix types, each mapped to a specific failure dimension:
claudemd-lint --fix --dry-run ./CLAUDE.md # Preview changes
claudemd-lint --fix ./CLAUDE.md # Apply them
Here’s exactly what each fix type does:
**remove-boilerplate** — Strips "write clean code" type phrases that don't change Claude's behavior. Maps to the redundancy dimension.**remove-duplicate** — Deletes exact duplicate lines. You'd be surprised how often the same rule appears in two sections.**trim-filler** — Condenses wordy phrases. "It is important to" becomes "to." "In order to" becomes "to." Maps to token efficiency.**trim-whitespace** — Collapses 3+ consecutive blank lines down to 2. Cosmetic, but it adds up in longer files.**add-timestamp** — InsertsLast updated: YYYY-MM-DDat the top of your file if it doesn't already have one. This is a staleness canary — when you see "Last updated: 2025-09-14" and it's March 2026, you know it's time for a review.**add-vague-marker** — Tags imprecise rules with<!-- TODO: make this more specific -->. It doesn't delete your vague instructions — it marks them so you can fix them on your own terms. Maps to actionability.
What auto-fix won’t touch: contradictions. Those need a human to decide which rule is correct. The tool can tell you that lines 5 and 41 disagree about package managers, but it can’t know whether you meant npm or pnpm.
CI Integration
The --ci flag fails your build if CLAUDE.md quality drops below a threshold:
# .github/workflows/lint-claude-md.yml
name: Lint CLAUDE.md
on:
pull_request:
paths: ['**/CLAUDE.md']
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
- run: npm install -g claudemd-lint
- run: claudemd-lint --ci --discover .
Now every PR that touches a CLAUDE.md gets automatically validated. Configuration drift gets caught at the gate.
Monorepo Discovery
For projects with multiple CLAUDE.md files:
claudemd-lint --discover .
This finds every CLAUDE.md in your repo, scores them individually, and runs a cross-file consistency check that catches contradictions spanning multiple files. This is the most valuable check for monorepos — the root says “use npm” but the API package says “use pnpm,” and nobody noticed until now.
Verdict System
The overall health score now maps to a human-readable verdict:

The verdict appears at the top of every report. It’s a quick pulse check — “Needs work” means spend 15 minutes cleaning up. “Excellent” means move on.
MCP Server Mode — The Killer Feature
Everything above assumes you’re running claudemd-lint in a terminal. That’s useful. But the feature that changes the workflow entirely is MCP server mode, which lets you run the linter inside Claude Code itself.
claude mcp add claudemd-lint -- npx claudemd-lint --mcp
One command. After this, Claude Code gains two new tools:
**lint_claudemd** — Analyzes your CLAUDE.md and returns the full health report inline in your conversation. Scores, issues, line numbers, suggested fixes — everything.**discover_claudemd** — Finds every CLAUDE.md file in your project and reports their locations and scores.
This means you can type “lint my CLAUDE.md” in Claude Code, and instead of switching to a terminal, running the CLI, reading the output, and then switching back, you get the analysis right where you can act on it. Claude sees the report in the same conversation. It can fix the issues in the same turn.
Think about what this collapses. The old workflow was: run linter in terminal, read report, open CLAUDE.md in editor, make changes, re-run linter, verify. The new workflow is: “lint my CLAUDE.md and fix whatever scores below 7.” One prompt. Done.
This is where linting meets AI-assisted editing. The tool that finds the problems runs inside the tool that fixes them. It’s the same reason having ESLint integrated into your editor is better than running it from the command line — except here the “editor” can also understand and apply the fixes.
Hook Migration Recommendations
Here’s a subtlety that most CLAUDE.md authors miss: some rules that belong in CLAUDE.md are actually enforcement rules that would be more reliable as Claude Code hooks.
Claude Code supports PreToolUse and PostToolUse hooks — scripts that run automatically before or after specific tool invocations. "Never commit files containing API keys" is a common CLAUDE.md instruction. But it's the kind of rule that should be a PreToolUse hook on the Bash tool — a script that checks for secrets before git commit runs, rather than a prose instruction that Claude might overlook.
The maintainability checker now identifies these patterns:
[HOOK] Line 18: "never commit API keys" → candidate for PreToolUse hook on Bash tool
[HOOK] Line 34: "always run tests before committing" → candidate for PreToolUse hook
This doesn’t mean the CLAUDE.md rule is wrong. It means there’s a more reliable enforcement mechanism available. The linter surfaces the opportunity — you decide whether to act on it.
The distinction matters: CLAUDE.md is for guidance (style, conventions, architecture context). Hooks are for guardrails (things that must never happen, things that must always happen). When guidance and guardrails live in the same file, the guardrails are only as reliable as Claude’s attention to prose. Hooks make them deterministic.
Programmatic API
For teams building custom tooling — editor extensions, dashboard integrations, CI reporters — claudemd-lint exports everything as a library:
import {
lint,
fix,
parseFile,
discoverFiles,
formatTerminal,
formatJson,
formatCi,
startMcpServer
} from "claudemd-lint";
lint() returns the scored report as a structured object. fix() applies the six auto-fix operations and returns the diff. discoverFiles() walks your project tree. The format functions render the same report for terminals, JSON consumers, or CI systems. startMcpServer() is the function that powers the --mcp flag.
The tool ships with 120+ tests covering all 7 dimensions, the auto-fix operations, cross-file consistency, and the MCP server protocol. If you’re contributing or building on top of it, the test suite is comprehensive enough that regressions get caught fast.
The Bigger Picture
CLAUDE.md isn’t a quirky feature of Claude Code. It’s the beginning of a pattern that will define how developers work with AI for the next decade.
GitHub Copilot has .github/copilot-instructions.md. Cursor has .cursorrules. Windsurf has .windsurfrules. Every major AI coding tool is converging on the same idea: a version-controlled configuration file that tells the AI how to behave in your project.
The teams that treat these files as first-class infrastructure — linted, reviewed, tested, monitored — will compound their advantage over time. Better instructions lead to better AI output, which leads to more trust in the tool, which leads to more ambitious use cases.
The teams that write these files once and forget them will keep running into the same frustrating inconsistencies and wonder why AI coding assistants “don’t work” for their codebase.
The discipline is the same discipline we’ve always needed for configuration management. The stakes are just higher now because the configuration doesn’t just affect a build process — it affects every line of code your AI assistant generates.
Strategic Takeaways
- Audit your CLAUDE.md quarterly. Set a calendar reminder. Five minutes of review prevents hours of debugging inconsistent AI-generated code.
- Use the file hierarchy like you’d use TypeScript’s project references. Global defaults at the top, project rules in the middle, directory-specific overrides at the leaves. Don’t flatten everything into one file.
- Every instruction must pass the actionability test. Ask: “Could Claude generate correct code from this rule alone?” If the answer is no, add the concrete details — the function name, the file path, the exact pattern.
- Lint your CLAUDE.md in CI. Install claudemd-lint, add the GitHub Actions workflow, and stop configuration drift at the PR stage. It takes five minutes to set up.
- Register the MCP plugin for inline linting. Run
claude mcp add claudemd-lint -- npx claudemd-lint --mcponce, and you can lint from inside Claude Code itself. The tool that finds the problems runs inside the tool that fixes them. - Move guardrails from prose to hooks. If a CLAUDE.md rule is really an enforcement rule — “never commit secrets,” “always run tests” — implement it as a Claude Code
PreToolUsehook. claudemd-lint now flags these candidates for you. - Think of CLAUDE.md as the most-read file in your repo. It gets loaded on every single Claude Code conversation. Give it the same care you give your TypeScript config, your test setup, and your CI pipeline. It affects your output just as much.
claudemd-lint is open source under the MIT license: github.com/codecoincognition/claudemd-lint
메타데이터
- post_id
- a1b72ef87bbb
- slug
- your-claude-md-is-working-against-you-a1b72ef87bbb
- url
- https://medium.com/@engineeratheart/your-claude-md-is-working-against-you-a1b72ef87bbb
- canonical_url
- https://medium.com/@engineeratheart/your-claude-md-is-working-against-you-a1b72ef87bbb
- author_url
- https://medium.com/@engineeratheart
- status
- ok
- fetched_at
- 2026-08-07 21:52:05