When to write an agent rule, and when not
2026-06-11
2026-06-11
Most AI coding agents read a rules file before they touch your code. Claude Code reads CLAUDE.md and the files under .claude/rules. Cursor and Copilot have their own versions. The format differs, but the idea is the same: a place where you write down how code in this project should look, and the agent reads it on every session.
That place fills quickly. Once you have it, almost any coding guideline looks like it belongs there. Naming, commit format, how to structure a NestJS service, security advice, testing habits. It all sounds reasonable as a rule.
From what I have seen, this is where rule files go wrong. They become a second copy of every guideline the team has ever written, and most of those lines do nothing. This post is about a simple test for which lines belong in a rule and which have a better home.
I treat an agent rule as the last place I put guidance, not the first. Before a line becomes a rule, it has to fail every other home.
The test I use has two parts. Keep a line as a rule only if both are true:
If a tool can enforce it, the tool is a better home. If removing it changes nothing, it should not be there at all. What survives both questions is small, and that small set is what a rule is for.
This is the biggest group. A lot of what ends up in rule files is something a linter or a check can verify.
Commit message format is the clearest example. Writing "use the imperative, start with a capital letter" as a rule asks the agent to remember a convention. commitlint just enforces it:
{
"extends": ["@commitlint/config-conventional"]
}
Now the format is not advice, it is a check that fails when the message is wrong. The same goes for naming. A rule that says "boolean variables start with is, has, or should" can be an eslint rule instead:
{
"@typescript-eslint/naming-convention": [
"error",
{
"selector": "variable",
"types": ["boolean"],
"format": ["PascalCase"],
"prefix": ["is", "has", "should"]
}
]
}
When a tool enforces a guideline, something useful happens. The tool becomes the only definition. There is no prose copy that can disagree with it later. A failing check is also the most reliable documentation I know, because you cannot ignore it. It stops the merge.
This fits how I already like to work. I rely on tests and CI to catch mistakes rather than on documents people are supposed to remember. An agent rule that repeats a lint rule is just a less reliable copy of something the linter already does.
The second group is the one that feels the most useful and is usually the most useless. These are general engineering principles. "Simpler designs are more secure." "Prefer composition." "Write tests." "Use clear names."
They are all true. That is exactly why they do not belong in a rule. A current model has read this advice many thousands of times. It already writes simpler code and clearer names by default. Repeating the principle does not change the output, because the model was going to do it anyway.
These lines are also not free. Every line in a rule file is read on every session. It costs tokens, and it competes for the model's attention with the few lines that actually matter. A rule file that is mostly general advice makes its own useful parts harder to notice.
There is also no place to stop once you start. If "simpler is more secure" earns a place, so does every other principle anyone has ever agreed with. You can keep adding them forever, and each one looks justified on its own. When a category has no natural boundary, that is usually a sign the category is wrong.
Some guidelines are real and specific, but they only apply to one area. How to structure a NestJS service. How to write a React component in the admin app. These are worth writing down. They just do not need to live in a global rule.
A README next to that code is a better home. The agent reads it when it works in that folder, and so does the next developer who opens it. One file, both readers, sitting where the work happens. A global rule puts that same guidance far away from the code it describes, which is how it starts to disagree with reality over time. I wrote more about keeping a single document for both humans and agents in an earlier post.
After tooling, deletion, and module docs, what remains is small. It is the guidance that is specific to this project, that a tool cannot catch cheaply, and that the model would get wrong without being told.
An example from the kind of work I do: in one project the convention is to pass a whole entity into a service method instead of passing just its id. A linter will not catch that, the model does not do it by default, and getting it wrong causes real confusion later. That is a genuine rule. It changes what the agent does, and nothing else enforces it.
Notice that this rule is not verifiable. No check confirms it, and that is fine. The test was never "can a tool verify it." The test was "does removing it change what the agent does here." Some judgment conventions pass that test even though no tool can enforce them. Those are the rules worth keeping, and there are not many of them.
Before you write an agent rule, ask two questions. Can a tool enforce this? Then it belongs in the tool. Would removing it change nothing? Then delete it. What is left, the project-specific guidance that a tool cannot catch and the model would otherwise get wrong, is the whole job of a rule file.
In the projects I work on, that final list is short. A rule file that stays short is doing its job. One that keeps growing is usually collecting things that belong somewhere else.