为没有用户会遇到的情况加固代码
Hardened the code against cases no user will hit
智能体(或它的审查者)为真实用户根本到不了的状态堆上分支、守卫和测试,同一个 diff 里明显的问题却没人发现。
适用于: Claude Code · Cursor · Codex · Copilot agent
症状
为不可能同时发生的事件组合写的长处理逻辑、“以防万一”的检查、给一次性改动加的 e2e 测试,以及永远能再找出一个理论情况的审查意见。diff 越来越大,几行之外真正的问题(N+1 查询、设计问题)却原封不动。
为什么会这样
@RhysSullivan 引用 Thorsten Ball 的 PR 帖说,模型“喜欢为永远不会发生的情况加一大堆代码”。他举的审查“发现”是:“在同一个请求里一边被取消管理员身份一边改设置”。回复里还有更多:为不可达状态写了 40 行处理逻辑,却漏掉旁边的 N+1 查询(@giskyexplorer);“能通过测试的无用机制”(@Gabezor);给一个即将关停的项目加告别页,智能体却想先写 e2e 测试再做全量测试审查(@NapVeg);智能体写了太多又重又慢的测试(@kopseng);审查机器人“总能找到点什么”,修的是用户永远碰不到的问题(@kenryu42)。@leothecurious 把“偏执的防御式编码”列为他不得不一直纠偏的习惯之一。“周全”会得到奖励,而没有任何信息告诉智能体某个情况有多大可能发生,于是每个情况看起来都值得处理。
示例
示意,基于 @RhysSullivan 的例子。
tsasync function updateSetting(user, key, value) {
// Agent-added: "user could be demoted from admin during this same request"
const fresh = await db.users.findFresh(user.id);
if (!fresh.isAdmin && wasAdminAtRequestStart(user)) {
await auditLog("demoted-mid-request", user.id);
throw new ConcurrentDemotionError(user.id);
}
// ...30 more lines of guards...
return db.settings.set(key, value);
}
复制即用的修复
按你用的智能体挑选文件。规则片段用英文写,放进任何仓库都能用。
Claude Code:加入 CLAUDE.md
CLAUDE.md## Handle realistic cases, not imaginable ones
- Before adding a guard, branch or test for an edge case, state in one line how a real user or
caller reaches it. If you can't, don't add it; mention it in your summary instead.
- Match the effort to the task. A one-off or throwaway change doesn't get new e2e suites or a full
test review unless the user asks.
- When reviewing, rate each finding by severity and likelihood, and only fix findings the user
agrees are likely. List the rest as "not fixed: unlikely".
- Prefer one test that starts from real user behavior over many tests for hypothetical states.
Codex、Copilot 编码智能体等:加入 AGENTS.md
AGENTS.md## Handle realistic cases, not imaginable ones
- Before adding a guard, branch or test for an edge case, state in one line how a real user or
caller reaches it. If you can't, don't add it; mention it in your summary instead.
- Match the effort to the task. A one-off or throwaway change doesn't get new e2e suites or a full
test review unless the user asks.
- When reviewing, rate each finding by severity and likelihood, and only fix findings the user
agrees are likely. List the rest as "not fixed: unlikely".
- Prefer one test that starts from real user behavior over many tests for hypothetical states.
Cursor:项目规则
.cursor/rules/imaginary-edge-case-hardening.mdc---
description: "AgentFails: Hardened the code against cases no user will hit"
alwaysApply: true
---
## Handle realistic cases, not imaginable ones
- Before adding a guard, branch or test for an edge case, state in one line how a real user or
caller reaches it. If you can't, don't add it; mention it in your summary instead.
- Match the effort to the task. A one-off or throwaway change doesn't get new e2e suites or a full
test review unless the user asks.
- When reviewing, rate each finding by severity and likelihood, and only fix findings the user
agrees are likely. List the rest as "not fixed: unlikely".
- Prefer one test that starts from real user behavior over many tests for hypothetical states.
Diff 预算检查(提交前或在 CI 中运行)
改编自 dev.to(yureki_lab)的 diff budget 文章。读取真实的 git diff,所以通过 shell 做的修改也逃不掉。用法:python3 scripts/diff_budget.py origin/main 6 150
scripts/diff_budget.py#!/usr/bin/env python3
"""Fail if the diff vs BASE touches files outside .agent-scope or exceeds the budget."""
import fnmatch, os, subprocess, sys
base = sys.argv[1] if len(sys.argv) > 1 else "HEAD"
max_files = int(sys.argv[2]) if len(sys.argv) > 2 else 6
max_lines = int(sys.argv[3]) if len(sys.argv) > 3 else 150
allowed = []
if os.path.exists(".agent-scope"):
allowed = [l.strip() for l in open(".agent-scope") if l.strip() and not l.startswith("#")]
out = subprocess.run(["git", "diff", "--numstat", base], capture_output=True, text=True, check=True).stdout
untracked = subprocess.run(["git", "ls-files", "--others", "--exclude-standard"],
capture_output=True, text=True, check=True).stdout.split()
files, lines, outside = 0, 0, []
rows = [r.split("\t") for r in out.strip().splitlines()] + [["0", "0", p] for p in untracked]
for added, deleted, path in rows:
files += 1
if added != "-":
lines += int(added) + int(deleted)
if allowed and not any(fnmatch.fnmatch(path, p) for p in allowed):
outside.append(path)
problems = []
if outside:
problems.append("out-of-scope files: " + ", ".join(outside))
if files > max_files:
problems.append(f"{files} files changed (budget {max_files})")
if lines > max_lines:
problems.append(f"{lines} lines changed (budget {max_lines})")
if problems:
print("OVER BUDGET: " + "; ".join(problems))
sys.exit(1)
print(f"within budget: {files} files, {lines} lines")
全新上下文的审查技能(子智能体只看 diff,不看聊天记录)
沿用 Anthropic 代码审查文章里的做法:先验证,再让一个上下文干净的子智能体审 diff。命名为 review-diff,避免和内置或流行的 /code-review 技能重名。
.claude/skills/review-diff/SKILL.md---
name: review-diff
description: Review the current diff with fresh context before a PR. Use after /verify passes.
---
# Review the diff
Start a subagent that has NOT seen this conversation. Give it only:
- the original request (one paragraph, verbatim),
- the output of `git diff origin/main...HEAD`,
- the names of the test/build commands.
Ask it to report, with file:line for each finding:
1. **Wrong feature**: does the diff do what the request asked? Quote the request line each change serves.
2. **Bugs**: logic errors, missing edge cases (empty, null, boundary), unhandled errors,
unawaited promises, invented APIs or packages, swallowed exceptions.
3. **Overreach**: changes no request line needs (refactors, renames, formatting, dependency or
config edits, public API changes, weakened tests).
4. **Evidence gaps**: claims in the PR description that no command output supports.
Then fix real findings, or list them under "Known issues" in the PR. Do not argue with the reviewer
in the PR text; either fix or explain in one line.
来源与致谢
- @RhysSullivan on X: models add extra code for cases that will never happen (admin demoted mid-request)
- @giskyexplorer on X: a 40-line handler for an unreachable state, the N+1 query missed
- @Gabezor on X: overbuilding useless machinery that passes tests
- @NapVeg on X: e2e tests and a full test review proposed for a goodbye route
- @kopseng on X: agents create too many heavy and slow tests
- @kenryu42 on X: review bots always find something users would never hit
- @leothecurious on X: "paranoid defensive coding patterns" among recurring agent habits
- @thorstenball on X: "Why are you still using pull requests?" (the thread)