Every CLAUDE.md I read grows. Nobody shrinks one. That's the symptom. The disease is treating it like documentation when it's actually a system prompt.
The file gets loaded into context on every turn. Every line you add is a line Claude has to read to find the rule that matters. Add enough lines and the rule that matters becomes a needle in your own haystack. Worse, it silently changes meaning. The rule that matters today isn't the rule that mattered when you wrote it down six weeks ago.
The framing shift is small but load bearing. CLAUDE.md is a system prompt. That means tokens are a budget. You have somewhere around 500 instruction lines before signal degrades meaningfully. How do you spend them?
Three buckets
Go through your file line by line. Each line falls into one of three buckets.
Deterministic eligible. "Never edit migrations." "Always run lint before commit." "Use snake_case for python variables." These are rules a hook or a settings entry can enforce mechanically. They don't need to live in prose, because they don't need Claude's judgment. They need a guard.
Probabilistic only. "Match the existing code style." "Consider the user's experience." "Weigh the migration cost before refactoring." These require judgment. A hook can't enforce them. They belong in prose.
Vague. "Be helpful." "Write good code." "Be careful." These are aspirational. They don't change behavior. They pad the budget without producing anything. Delete them.
Most CLAUDE.md files I've looked at are around 60% deterministic eligible, 25% probabilistic, 15% vague. The deterministic stuff is the largest movable category. It's also the part that produces the most production incidents when Claude misses it. The prose rule that says never edit migrations is no protection against the day Claude does.
Why hooks beat prose for deterministic rules
Prose rules are advisory. Claude reads them and tries to comply. When the file is short and the rule is recent, this works most of the time. When the file is 2000 lines and the rule is buried at line 1400, Claude misses it sometimes. You don't notice until production.
Hooks are exits. A hook either runs or it doesn't. If it runs and fails, the action doesn't happen. There's no probability gradient. "Never edit X" becomes a Bash deny rule, and "never" actually means never, not "almost always."
The rule of thumb: if a violation could be detected by reading the change after the fact, it can probably be detected before the action with a hook. And if it can be detected before the action with a hook, prose is the wrong place for it.
Migrating, concretely
A hook in Claude Code is a small program that runs on a tool event. PreToolUse fires before Claude's tool call and can block. PostToolUse fires after. UserPromptSubmit fires when the user submits a message and can rewrite it. Stop fires when Claude finishes a turn.
Example: "never run rm -rf in this repo." The prose form is a wish. The hook form is a deterministic block:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "if echo \"$CLAUDE_TOOL_INPUT\" | grep -q 'rm -rf'; then echo 'rm -rf blocked' >&2; exit 2; fi"
}
]
}
]
}
}
Bash. Greps. Exits non zero on match. Claude sees the block, adapts. You delete the prose rule because it's now mechanically enforced.
Half a CLAUDE.md can move this way. The half that can't is the half that mattered.
The audit
A small skill classifies your CLAUDE.md by these categories: github.com/blacksun-dev/claudemd-audit. Drop it into .claude/skills/, point Claude at your file, get a report. The heuristics are not perfect. They catch the common signal patterns and miss subtler ones. Starting point, not final word.
The output looks like:
total file lines: 487
instruction lines: 312
classification:
- deterministic-eligible: 191 (61%)
- probabilistic-only: 78 (25%)
- vague: 28 (9%)
- redundant: 15 (5%)
The 28 vague lines are easy wins. Delete them. Nothing breaks.
The 15 redundant lines are easy wins too. Consolidate them.
The 191 deterministic eligible lines are the actual project. That's the migration. You're not going to do all of it in one weekend. You can move the top ten in an hour and feel the difference.
What stays
After the migration, what's left in CLAUDE.md is short. Fifty to a hundred and fifty lines, maybe two hundred. It reads like a focused brief: project context, judgment calls, things that genuinely require an LLM to weigh.
That's the win. Not "smaller file." "Smaller file with higher signal density." Every remaining rule is one Claude actually has to read carefully because the deterministic ones are gone. They don't take attention budget for compliance any more. They take attention budget only when violated.
The habit
Once you've moved the deterministic rules out, two things happen.
First, you stop adding new rules to CLAUDE.md as the default response to "Claude got it wrong." You ask: was this a judgment call gone wrong, or a deterministic rule waiting to be written? If it's deterministic, you write the hook. If it's judgment, you add a prose line. If it's neither, you don't add anything.
Second, you start treating prose lines as expensive. Each one is read every turn. Each one competes with the others for attention. You write them tighter, with more signal per character. You delete them when they stop earning their seat.
The audit isn't a one time thing. It's a habit. Once a quarter, run it on the current CLAUDE.md, see what's drifted into vague, see what's accumulated as redundant, see what the deterministic share has become.
The skill is open source
github.com/blacksun-dev/claudemd-audit. Take it, fork it, refine the heuristics. File an issue if you find a misclassification. The goal is fewer 2000 line CLAUDE.md files. That's it.
Top comments (0)