Why Adding More Rules Still Fails to Control AI—and the 4 Principles That Actually Work

The article explains why piling up dozens of ad‑hoc rules makes AI agents noisier rather than safer, identifies the real bottleneck as behavioral, and presents four concrete principles—clear questioning, minimal implementation, targeted edits, and verifiable goals—with code examples and practical guidance.

DeepNoMind
DeepNoMind
DeepNoMind
Why Adding More Rules Still Fails to Control AI—and the 4 Principles That Actually Work
Rules keep growing, but AI gets messier; the real fix isn’t a patchy rule list but four behavioral principles: ask clearly, avoid over‑design, change only what’s needed, and drive a verifiable goal loop.

You may have experienced this: the more rules you write, the less confident you feel.

AI does absurd things—creates its own conventions, changes code you didn’t ask it to—so you add a rule to CLAUDE.md; a few days later a new issue appears and you add another. After months the file contains dozens of rules, but the result is worse: it follows the wrong rules and invents unnecessary ones.

It looks like you’re “tightening control,” but you’re actually “adding noise.”

The bottleneck is always behavior, not model capability

The problem isn’t that the model can’t write; it’s that it can’t receive.

Models often break down in judgment, showing these common failure modes:

Making unverified assumptions and acting on them directly.

Not managing its own uncertainty: no questions, no trade‑off exposition, no rebuttal when needed.

Over‑design: turning a 100‑line solution into a 1000‑line one.

Silently modifying or deleting code in unrelated tasks without truly understanding it.

These are not “lack of ability” issues. The model can write code, but it doesn’t always know when to stop, what to ask before starting, how much to change, or when a task is truly complete. It’s a behavior problem that won’t disappear by lengthening the rule list.

The model already has the ability to “write,” but there’s still a gap before you can safely hand work to it. That gap is a behavioral one, and the four principles hit the bull’s‑eye.

Configuration paradox: why more rules increase failure risk

Why do dozens of rules often backfire? Two core reasons.

1. Context dilution

CLAUDE.md is injected into the agent’s context each turn, but the context budget is limited. The model performs best when information is dense and relevant; noise degrades performance. For a concrete task, most of the dozens of rules are irrelevant. Irrelevant instructions drown out the few critical ones.

Before adding a rule, ask “If we delete this, will it cause an error?” If not, delete it. The file should be short and effective, not exhaustive.

2. Transferability

Most new rules are scenario‑specific, e.g., “must use TypeScript strict” or “no semicolons.” They only apply to a particular tech stack, so the rule list grows fragmented. In contrast, a principle like “don’t assume, ask first” works in any language or project.

Four behavioral principles (with code examples)

The four rules split into two categories: the first three act as brakes to prevent common mistakes; the fourth is the accelerator that unlocks latent model capabilities.

Behavior 1: Don’t assume, surface uncertainty, and explicitly weigh trade‑offs

The first rule catches the most common fatal mistake: not asking when you should.

When you tell an unconstrained agent “add caching to this endpoint,” it will immediately write code and make hidden decisions:

# Without rules, the following four hidden decisions appear
cache = {}

def get_user(user_id):
    if user_id in cache:
        return cache[user_id]
    user = db.query(User).get(user_id)
    cache[user_id] = user  # never expires
    return user

Each assumption (in‑memory cache, no expiry, unlimited size, cache‑by‑id) can cause production bugs.

With behavior 1, the model first asks:

Before I add caching, a few choices affect the approach:
1. Store: in‑memory (per‑process) or Redis (shared across instances)?
2. Expiry: how long should an entry stay valid?
3. Invalidation: should updates to a user purge their cache entry?
4. Scale: roughly how many users / how hot is this path?

Why it works: the model naturally prefers to continue writing; this rule forces it to pause and clarify uncertainty.

Behavior 2: Write the minimal implementation that solves the current problem, no speculative expansion

The second rule cures “premature abstraction.”

When the task is “write a function to parse a config file,” an unconstrained agent may generate a large scaffolding:

# Without rules, a 6‑line problem becomes a 40‑line scaffolding
class ConfigSource(ABC):
    @abstractmethod
    def load(self) -> dict: ...

class JSONConfigSource(ConfigSource):
    def __init__(self, path):
        self.path = path
    def load(self) -> dict: ...

class ConfigSourceFactory:
    _registry = {}
    @classmethod
    def register(cls, ext, source):
        cls._registry[ext] = source
    @classmethod
    def create(cls, path): ...
# ...and you end up with a never‑used plugin system

With behavior 2, the output converges to the minimal solution:

# With the rule, we get the minimal implementation
import json

def load_config(path: str) -> dict:
    with open(path) as f:
        return json.load(f)

Why it works: over‑design adds bug surface, review cost, and makes later adjustments cumbersome.

Behavior 3: Change only what must be changed, and clean up only the issues you introduced

The third rule protects auditability and traceability.

Task: fix a missing email causing a crash. An unconstrained diff may balloon:

- if not user["email"]:
+ email = user.get("email", "").strip()
+ if not email:
+     raise ValueError("Email required")
- if "@" not in user["email"]:
+ if "@" not in email or "." not in email.split("@")[1]:
+     raise ValueError("Invalid email")
+ # while I'm here... (nobody asked for any of this)
+ if len(user.get("username", "")) < 3:
+     raise ValueError("Username too short")
+ if not user["username"].isalnum():
+     raise ValueError("Username must be alphanumeric")

The real requirement is only the email check. The rule narrows the change to:

- if not user["email"]:
+ if not user.get("email", "").strip():
    raise ValueError("Email required")

Why it works: if only a few lines relate to the requirement, reviewers can focus on them instead of wading through unrelated “optimizations.”

Behavior 4: Define verifiable success criteria and loop until they pass

The fourth rule is the amplifier. The first three prevent mishaps; this one ensures the goal is achieved.

Vague instruction:

"Make the search endpoint faster."
→ Agent: "I'll review the code, find inefficiencies, and optimize."
(Changes something, declares victory, no way to know if it worked)

Verifiable instruction:

"Get /search p95 latency under 200ms.
Success =
- a benchmark script exists and reports p95
- p95 < 200ms on the 10k‑row fixture
- every existing test still passes
Loop until all three are green."

The agent then writes a benchmark, runs it, sees 450 ms, adds an index, reruns to 180 ms, and repeats until all conditions are green.

Why it works: constraints prune bad behavior, while the loop leverages the agent’s strength of iterating toward a measurable target.

Beyond the four rules: what to add and what to omit

The four rules form a foundation, not a complete checklist. Add only information the agent cannot infer from code, e.g.:

## Project
- Build: npm run build
- Test: npm test
- Lint: npm run lint -- --fix
## Conventions
- API errors return { error, code } — never throw across the boundary
- Dates stored UTC, displayed in the user's timezone
## Watch out
- Payments service timeout is 30s, not the default 5s
- Don't import from /internal — it breaks the public build

Before adding a line, ask: “If we delete this, will the agent make an unrecoverable mistake?” If not, skip it.

Remember: if the information is visible in the code, don’t duplicate it in the config.

Architecture details the agent can read from code.

Style rules inferable from existing files.

Dependencies already listed in package.json.

When the four rules aren’t enough

Large multi‑file refactors need architectural context beyond behavioral principles.

Regulated domains require hard constraints (e.g., forbid storing PII, mandatory security review).

Team consistency is a collaboration issue, not just a config problem; an AGENTS.md outside of tools still has value.

The wording and response intensity are tuned for Claude Code; you’ll need to adapt for Cursor, Copilot, etc.

Further reading / reference links

Andrej Karpathy — the original thread on agent coding and its failure modes [1]

Claude Code Docs — Best practices [2]

HumanLayer — Writing a good CLAUDE.md [3]

Builder.io — 50 Claude Code Tips and Best Practices [4]

aibuilderclub — Karpathy's Agentic Engineering framework [5]

jonbeckett.com — The Karpathy Guidelines: Taming AI Coding Agents [6]

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

AI AgentsPrompt EngineeringClauderule managementagentic codingbehavioral principles
DeepNoMind
Written by

DeepNoMind

I’m Yu Fan, a tech leader with deep technical expertise and managerial vision. Formerly at Motorola, now at Mavenir, I’ve led teams for years, focusing on backend architecture and cloud-native solutions, staying abreast of AI and other frontier fields, and championing personal growth and lifelong learning.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.