相信不少开发者都遇到过这样的困境:规则清单越写越长,心里反而越来越不踏实。
当 AI 编码助手做出一些意料之外的操作——比如擅自创建规范、修改了你未授权的代码——你自然会往 CLAUDE.md 中追加一条规则。隔几天又出现新问题,再补一条。几个月下来,配置文件里堆积了几十条规则,效果却适得其反:该遵守的规则被忽略,不该自行脑补的内容却全都补上了。
表面上看,你是在“加强管控”,但本质上,你可能只是在“制造噪音”。
瓶颈从来不是模型能力,而是行为
问题不在于“它不会写代码”,而在于“它不知道何时收手”。
模型常常在判断环节出现偏差,典型表现包括:
- 代替你做出未经确认的假设,并直接按照这些假设执行操作。
- 不主动管理自身的不确定性:不提出问题、不展示权衡取舍、该反问时选择沉默。
- 过度设计架构:原本 100 行能搞定的功能,硬生生写成 1000 行。
- 在不相关的任务中,悄悄修改或删除自己并未真正理解的代码。
注意,这些都不是“能力不足”导致的。模型具备编写代码的能力,但并非总能判断何时该停下来、开始前该确认什么、究竟该改动多少、怎样才算真正完成。说到底,这是行为层面的问题,不会因为你把规则写得越来越长就自动消失。
配置悖论:为什么规则越多越容易翻车
为什么几十条规则常常会适得其反?核心原因有两点。
1. 上下文稀释(Context dilution)
CLAUDE.md 会在每轮对话中进入上下文窗口,而上下文预算终究是有限的。模型在“信息相关、密度高”的情况下表现最佳;一旦噪音增多,性能就会明显下滑。对于某个具体任务而言,几十条规则里往往大部分都不相关。无关指令越多,真正关键的那几条就越容易被淹没。
每添加一条规则,都要问问自己“删掉它会不会出问题?”如果不会,那就删掉。这个文件不是越全越好,而是越短越有效。
2. 可迁移性(Transferability)
大多数新增规则都高度场景化,比如“必须使用 TypeScript strict 模式”“不要分号”“优先采用函数式组件”。它们只在特定的技术栈、特定的项目场景下才生效,所以规则清单只会越滚越大、越滚越碎。
而“不要假设,先提问”这类行为原则,几乎适用于任何编程语言、任何项目类型。前者是永远补不完的清单,后者是可以长期复用的底层逻辑。
四个行为原则(含代码示例)
这四条规则可以分为两类:前三条是“刹车”,用来预防常见失误;第四条是“油门”,把模型本身具备但默认不主动使用的能力调动起来。
行为 1:不要假设,不要掩饰困惑,要显式给出权衡
先看第一条。它针对的是最常见也最致命的问题:该问的时候不问。
当你对一个无约束的上下文说“给这个端点加缓存”,它往往会直接开写,并在背后替你完成一串关键决策:
# 没有规则约束时,模型会默认做出以下四个隐藏决策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 # 缓存永不过期return user
这段代码默认使用了内存缓存、无过期策略、无容量上限、按用户 ID 缓存。每一条都可能在生产环境中引发问题。
加入行为 1 后,它会先停下来向你提问:
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?
先确认清楚,再开始动手。
为什么有效:模型天生就倾向于一路向下编写代码,很少主动停下来问一句“这里我拿不准”。这条规则,本质上是在重置它的默认行为模式。
行为 2:只写解决当前问题的最小实现,不做猜测性扩展
第二条,专门整治“过早抽象”的问题。
当需求只是“写个函数解析配置文件”,无约束的上下文往往会先搭建一大堆框架:
# 没有规则约束时,原本 6 行代码能解决的问题,却写成了 40 行的脚手架class ConfigSource(ABC):@abstractmethoddef load(self) -> dict: ...class JSONConfigSource(ConfigSource):def __init__(self, path): self.path = pathdef load(self) -> dict: ...class ConfigSourceFactory:_registry = {}@classmethoddef register(cls, ext, source): cls._registry[ext] = source@classmethoddef create(cls, path): ...# 并且还会附带一个永远用不上的插件系统
加入行为 2 后,输出通常会收敛成:
# 有了规则约束后,会得到以下最小实现import jsondef load_config(path: str) -> dict:with open(path) as f:return json.load(f)
为什么有效:过度设计不一定“在技术上错误”,但时机通常不对。它会增加潜在的 bug 面、提高代码审查成本,也会让后续的调整变得更加笨重。今天就能解决的问题,别借用明天的复杂度来处理。
行为 3:只改必须改的部分,只清理自己引入的问题
第三条,保护的不是“代码看起来整洁”,而是“改动可核对、可追溯”。
任务明明只是修复 email 缺失导致的崩溃,但无约束时 diff 往往会越改越大:
- 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")+ # 顺便再改点别的...(没人要求做这些)+ if len(user.get("username", "")) < 3:+ raise ValueError("Username too short")+ if not user["username"].isalnum():+ raise ValueError("Username must be alphanumeric")
实际请求只有“补上 email 缺失检查”,其余改动都在增加审查负担。
行为 3 的目标很明确:把改动范围限制为只修改必须修改的部分。
- if not user["email"]:+ if not user.get("email", "").strip():raise ValueError("Email required")
为什么有效:如果 40 行改动里只有 3 行和需求直接相关,你就得硬着头皮审完剩下的 37 行,才能放心合并。每一行“顺手优化”,都在给代码评审增加不必要的负担。
行为 4:定义可验证的成功标准,并循环直到验证通过
第四条,是放大器。前三条解决的是“别出事”,这一条解决的是“把事做成”。
模糊指令:
"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)
可验证指令:
"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 passesLoop until all three are green."
这时,AI 编码助手会自己跑起闭环:编写基准测试、查看结果、发现 450ms、添加索引、重新运行到 180ms、再跑全量测试,直到所有条件全部满足。
为什么有效:约束只能减少不良行为,杠杆才能放大优质行为。这条规则把 AI 编码助手擅长的“朝目标反复迭代”的能力真正激活。你不用盯着每一步,只需要盯住最终的验证条件。
除了这 4 条规则,还应该加什么,不该加什么
这 4 条是底座,不是全部。在这之上,只补充那些 AI 编码助手无法从代码里直接看出来的信息,比如:
## 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
每添加一行之前,都先过一遍这个问题:删掉它是否会导致 AI 编码助手犯下难以自行恢复的错误?如果不会,那就先别加。
记住这条筛选原则:能从代码里看出来的信息,就别在配置里重复。
不该写的内容包括:
- AI 编码助手从代码结构就能读到的架构说明。
- AI 编码助手能从现有文件推断出的风格规则。
- package.json 已明确列出的依赖信息。
什么时候 4 条规则不够用
当然,这 4 条规则不是银弹,它们有明确的边界:
- 大型多文件重构需要架构上下文,仅靠行为原则不够。
- 受监管领域需要硬约束(如禁止记录 PII、认证改动必须安全评审)。
- 团队一致性是协作问题,不只是配置问题;可检入、工具无关的 AGENTS.md 仍有价值。
- 这些表述是按 Claude Code 调过的,对 Cursor/Copilot 大体可迁移,但具体措辞和响应强度需要自行实测。
延伸阅读/参考链接:
- Andrej Karpathy — the original thread on agent coding and its failure modes
- Claude Code Docs — Best practices
- HumanLayer — Writing a good CLAUDE.md
- Builder.io — 50 Claude Code Tips and Best Practices
- aibuilderclub — Karpathy's Agentic Engineering framework
- jonbeckett.com — The Karpathy Guidelines: Taming AI Coding Agents
