Stop Adding Rules
Judgment-heavy rule lists make coding agents worse. Keep the small precise rules, turn the rest into patterns with the goal, the reasoning and counterexamples, and move anything deterministic into hooks.
Vous voulez le framework derrière ces projets ?
Obtenez le système Claude Code que nous utilisons pour planifier, construire, tester et livrer des logiciels en production.
Your agent fails, you add a rule. It fails differently, you add another. A few months later the file is a wall of NEVER, IMPORTANT and ALWAYS, and the agent follows it worse than when the file was short. The fix is not a better rule, and it is not deleting every rule either. Small, precise rules earn their keep. The problem is the judgment-heavy ones, the patches written after failures, and those need a different shape: patterns that carry the goal and the reasoning, hooks for everything deterministic, and a strict habit of growing the file only when an observed failure justifies it. This is the full process, with the real files.
Where rule lists come from
When I audit skill setups, I keep finding the same thing: bullet lists.
- NEVER commit directly to main
- IMPORTANT: keep functions under 80 lines
- ALWAYS run the tests before pushing
Every bullet was born from a real incident. The agent committed to main once, so a rule appeared. Each rule made sense on the day it was written. But the incident got deleted and only the order survived. The model reads "NEVER move buttons while saving" and has no idea what that rule protects.
Why the agent picks the wrong rule
The model is not disobeying you. It complies with the easiest visible instruction. When two rules collide, and in any list past ten items they do, it satisfies the one that is cheapest to satisfy and moves on. So you add a third rule to arbitrate between the first two, and now there are three things to misread instead of two.
This is rationale loss, and it compounds into rule debt: every rule written without its reasoning generates more rules later. The file only ever grows, and compliance only ever drops.
There is data on this now. An August 2026 preprint, arXiv 2608.11095, "Why Does CLAUDE.md Keep Growing? Catastrophic Remembering in Agentic Coding", traced 247,694 instruction lifetimes across 1,867 repositories. Agent instruction files more than tripled over their lifetime, and the older an instruction got, the less likely anyone was to delete it, because once the rationale is gone, nobody can prove a deletion is safe. Appending stays cheap forever; deleting gets scarier with every commit. In the paper's controlled experiments, attaching the reasoning to each instruction removed 99.3% of the excess instructions, cutting prompt growth from +211.3% to +1.4%, and improved real-world instruction following by up to 23.1%. One honest caveat: the study measured CLAUDE.md-style files, not skills directly. Skills load on demand, but once invoked their instructions enter the context the same way, so the same debt applies.
Six real rules that defeat each other
Here is a list I see constantly, in some form, for loading states:
- ALWAYS add a skeleton while data loads
- NEVER show a blank page
- ALWAYS put
Suspensearound the page - NEVER move buttons while saving
- ALWAYS disable the button during a request
- ALWAYS show a spinner inside the button
Each one is reasonable. Together they contradict: a page-level skeleton IS a blank page, and a permanently disabled button makes retry impossible after the request fails. The model cannot resolve the contradiction, because the resolution lives in the intent behind the rules, and the list stripped the intent out.
Which rules stay rules
Before converting anything, split your rules into three piles. Small, precise rules and exact procedures stay as rules: a port number, a naming convention, the one command that runs the tests. They are facts, and a fact needs no reasoning section. Deterministic rules a script can check leave the prompt entirely and become hooks, more on that below. What is left is the judgment-heavy pile: the patches written after failures, the rules that collide, the taste encoded as bullets. Those become patterns.
Write the pattern instead
A pattern replaces the list with five parts: the goal, the reasoning, the priority when goals collide, good examples, and counterexamples. Here is the real one that replaced those six rules in my setup, lightly trimmed:
# Preserve the user's mental model during asynchronous work
**Goal.** Loading reduces uncertainty without destroying useful context
or allowing a duplicate action. The user always knows where their thing
is and what is happening to it.
**Reasoning.** The user built a mental model from what was on screen:
the scroll position, the filter they typed, the card they were watching.
Async work that erases it costs them the model; async work that leaves
the screen unchanged costs them the knowledge that anything happened.
Both are failures, and the second looks fine in a screenshot. Six rules
used to cover this and they defeated each other: a page-level skeleton
IS the blank page, and a disabled button makes retry impossible after
the request fails. The judgment is one sentence: content that is
already useful stays, and feedback appears next to the control that
caused the work.
**Priority.**
1. Preventing a duplicate write beats showing progress.
2. Keeping already-useful content visible beats showing a loading state.
3. A recoverable error beats a silent one.
4. First paint is the only moment a skeleton is right. After that the
data exists: dim it, never replace it.
**Good.**
- A save button stays in place, shows pending, blocks a second submit,
then exposes success or a retryable error inline.
- A filter change keeps the old results visible at reduced opacity. The
filter bar never becomes a skeleton, never loses focus, never resets
scroll.
**Counterexamples.**
- Replacing the whole screen with a spinner for a local refresh.
Recorded cost: 30 minutes to unbuild.
- Keying a boundary on the filter state so every keystroke unmounts the
results and the scroll position. 40 minutes.
- Showing success before the server has committed the change.Read the reasoning again. From that one paragraph the model can re-derive all six of the original rules, and it can also handle the seventh case no rule ever covered, because it knows what the rules were for. That is the target: the smallest set of high signals that fully describes the behavior. When a pattern is testable, add a sixth part, Evidence, the checks that prove the behavior, so the agent can verify itself instead of claiming success.
Where patterns live
Nowhere new. A pattern belongs to the skill whose behavior it describes, so it replaces the rule list inside that SKILL.md. When the same judgment shows up in several skills, promote it to where shared instructions already live: a scoped file in .claude/rules/, or CLAUDE.md / AGENTS.md for the few things that apply everywhere. Do not invent a parallel folder for this. A new place to look is a new thing to forget, and the whole point is fewer moving parts with more signal in each.
The prompt that converts your rules
Run this once against your existing setup:
Read every rules file in this repo: CLAUDE.md, .claude/rules/, and any
skill files that contain rule lists. Sort the rules into three piles:
1. Keep as rules: small, precise rules and exact procedures that stand
on their own (a port number, a naming convention, the command that
runs the tests). Converting these would only add words.
2. Move to hooks: anything deterministic a script can check
(formatting, secrets, banned commands).
3. Convert to patterns: the judgment-heavy rules, the patches written
after failures. Group them by the behavior they protect. You should
end with 5 to 8 groups, not 20.
For each group, write one pattern in this shape:
# One sentence that states the behavior
**Goal.** What this protects, and for whom.
**Reasoning.** The failures these rules came from, and the single
judgment that resolves conflicts between them. This is the most
important section. Write it as an argument, not a list.
**Priority.** A numbered order of what wins when two goals collide.
**Good.** Three to five concrete examples of the behavior done right.
**Counterexamples.** The concrete mistakes the old rules guarded
against, with the cost when known.
Keep each pattern under 30 lines. Cut every rule the reasoning already
implies, and show me all three piles so I can veto any move.Review the output by hand. The agent can draft the patterns, but the reasoning is your judgment, and you are its editor.
Add only what observed failures justify
Start small and stay small. When the agent fails, the old reflex was "write a rule". The new reflex is a question: does the reasoning of an existing pattern already cover this? If yes, the file does not change, the failure was a one-off. If no, add one counterexample to the existing pattern, with the cost written down ("Recorded cost: 30 minutes"). A brand-new pattern needs repeated failures pointing at the same missing judgment.
This is why the patterns stay under 30 lines while a rule list doubles every quarter. Growth is driven by evidence, not by anxiety.
Deterministic rules do not belong in the prompt at all
Some rules have no judgment in them. Formatting, never exposing secrets, banned commands. Those should not be patterns either, because prompt instructions are advice, and advice decays over a long session. Anything a script can check, a hook should enforce. A hook runs outside the model, costs zero tokens, and wins at message one and at message two hundred.
Three from my own hooks.json:
{
"hooks": {
"PreToolUse": [
{ "matcher": "Bash", "hooks": [{ "type": "command", "command": ".claude/hooks/guardrails.sh" }] },
{ "matcher": "Read|Bash", "hooks": [{ "type": "command", "command": ".claude/hooks/protect-secrets.sh" }] }
],
"PostToolUse": [
{ "matcher": "Edit|Write", "hooks": [{ "type": "command", "command": ".claude/hooks/auto-format.sh" }] }
]
}
}Formatting, enforced instead of requested:
#!/bin/bash
# PostToolUse (Edit|Write): format the file that was just changed
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
case "$FILE" in *.ts|*.tsx|*.js|*.jsx) ;; *) exit 0 ;; esac
npx eslint --fix "$FILE" 2>/dev/null
exit 0Secrets, kept out of the context window entirely:
#!/bin/bash
# PreToolUse (Read|Bash): deny any read that touches an env file
INPUT=$(cat)
TARGET=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.command // empty')
case "$TARGET" in
*".env"*)
jq -n '{hookSpecificOutput:{hookEventName:"PreToolUse",
permissionDecision:"deny",
permissionDecisionReason:"env files are read by the app, never by the agent"}}'
;;
esac
exit 0The same deny shape gives you custom linting on anything. My guardrails hook blocks git rebase, git reset --hard and git push --force before they run, and it splits chained commands first so cd app && git rebase main is caught on its second segment. No rule in any prompt can promise that. A hook can.
If you want the continuous system, this is it
Everything above works as a manual habit. The last piece makes it run on its own: once a day, a scheduled job launches Claude over yesterday's session transcripts and proposes improvements to your patterns and skills. It suggests, it never edits. You stay the editor.
Claude Code already keeps a transcript of every session under ~/.claude/projects/, one folder per project. Save this as scripts/review-prompt.md:
Read the session transcripts from the last 24 hours in
~/.claude/projects/ for this project. Find every moment that cost time:
a correction I made, an instruction I had to repeat, a task that failed
and was retried, a diff I rejected.
For each one, check the skills involved and .claude/rules/ and answer
one question: does an existing pattern's reasoning already cover this?
If yes, skip it. If no, propose the smallest possible change: usually
one new counterexample on an existing pattern, with the cost recorded.
A new pattern needs repeated failures pointing at the same missing
judgment. Anything deterministic becomes a hook proposal instead.
Write the proposals to skill-suggestions.md as ready-to-apply diffs,
newest at the top. Do not edit the skills or rules yourself.Then schedule it with whatever your OS already has.
macOS, a LaunchAgent at ~/Library/LaunchAgents/com.me.skill-review.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key><string>com.me.skill-review</string>
<key>ProgramArguments</key>
<array>
<string>/bin/zsh</string>
<string>-lc</string>
<string>cd ~/code/my-project && claude -p "$(cat scripts/review-prompt.md)"</string>
</array>
<key>StartCalendarInterval</key>
<dict><key>Hour</key><integer>7</integer><key>Minute</key><integer>0</integer></dict>
</dict>
</plist>Load it once with launchctl load ~/Library/LaunchAgents/com.me.skill-review.plist.
Linux, one cron line (crontab -e):
0 7 * * * cd ~/code/my-project && claude -p "$(cat scripts/review-prompt.md)"Windows, Task Scheduler:
schtasks /Create /SC DAILY /ST 07:00 /TN skill-review /TR "cmd /c cd /d C:\code\my-project && type scripts\review-prompt.md | claude -p"claude -p runs headless, so this works while you sleep. Each morning skill-suggestions.md holds the proposed diffs, and you apply the ones the failures actually justify. The same growth rule, running on a clock.
If you want all of this without assembling it, the Build This Now $29 Code Kit ships these patterns, the hooks, and a production SaaS skeleton already wired together.
FAQ
How is this different from just writing a shorter CLAUDE.md?
Shortening compresses the rules. Patterns replace them with the judgment that produced them, which is what lets the model handle cases no rule anticipated. A short list of flat rules still fails the collision problem; a pattern resolves collisions in its priority section. See CLAUDE.md best practices for the file-level hygiene that pairs with this.
How many patterns should I have?
Five to eight, each under 30 lines. If you end up with twenty, you wrote rules with headers. Group by the behavior being protected, not by technology or by folder.
What goes in a pattern versus a hook versus a plain rule?
Anything that needs judgment goes in a pattern: tradeoffs, priorities, what wins when goals collide. Anything a script can check goes in a hook: formatting, secrets, banned commands. If you can write the check in bash, it should never be a sentence in a prompt. And a small, precise fact (a port, a command, a naming convention) stays a plain rule, because converting it adds words and no signal.
Does the daily review change my skills automatically?
No. It writes proposals to a suggestions file and never touches the pattern files. The whole system depends on evidence-gated growth, and you are the gate.
Vous voulez le framework derrière ces projets ?
Obtenez le système Claude Code que nous utilisons pour planifier, construire, tester et livrer des logiciels en production.

