先分清:你是在"vibe coding",还是在"做产品"
同样是让 AI 写代码,赌注不一样,规矩就不一样。
"vibe coding"这个词是 Andrej Karpathy 在 2025 年 2 月造的,原意很激进:"完全跟着感觉走,忘掉代码的存在"——也就是 AI 写什么你就收什么,根本不读。
后来 Simon Willison 把定义说清楚了:vibe coding 特指"不审查 AI 写的代码就用"。反过来,只要你开始读 diff、写测试、搞懂它在干嘛,那就不叫 vibe coding 了,那叫正常开发。这个区分很重要,因为它决定了你欠这个任务多少纪律。
动手前问自己:"这玩意儿要是错了,谁会受伤?" 答案是"没人,大不了删掉" → 尽管 vibe;只要碰到生产环境、钱、密钥、登录鉴权、别人依赖的代码 → 立刻切换成"每行都读、都测"。
为什么不能一直 vibe?有数据:CodeRabbit 的统计显示 AI 生成的代码大约带 1.7 倍 的 bug、1.4 倍的严重问题;2025 年也出过几起因为没人审 AI 代码导致的线上事故。所以原则很简单:赌注越大,越少 vibe,越多读和测。
- 原型、周末项目、demo
- 个人小工具、学习练手
- 一次性脚本、跑完就扔
- 登录 / 鉴权 / 支付
- 处理个人信息(PII)、密钥
- 数据库迁移、基础设施
- 面向客户 / 上生产
Willison 给"要维护的东西"定了条铁律,值得贴墙上:"我不会把任何我没法跟别人解释清楚它在干嘛的代码提交进仓库。"
开局就立规矩:让不同 AI 也懂你的项目
一份写给 AI 看的项目说明,是整套流程里性价比最高的一步。
两个工具都会在每次会话开始时自动读一份"项目说明文件",把约定写进去,你就不用每次重新解释。Claude Code 读 CLAUDE.md;Codex 读 AGENTS.md(AGENTS.md 是个跨工具开放标准,Cursor、Aider、Gemini CLI 等三十多个工具都认)。
- 在仓库里跑 /init(两个工具都有)自动生成初稿,然后狠狠删到一百行以内——文件臃肿,AI 反而会忽略你真正的规则。
- 想让两个工具共用一份:在 CLAUDE.md 里写一行 @AGENTS.md 引进来,或者直接 ln -s AGENTS.md CLAUDE.md 做软链。
- 判断每一条要不要留:"删掉它会不会让 AI 犯错?" 不会就删。
该写什么?AI 猜不到的东西:构建/测试/lint 的确切命令、和默认不一样的代码风格、仓库的特殊约定、环境坑、以及——最重要的——"禁止做什么"和"怎样算完成"。别写能从代码看出来的、或者标准惯例里就有的废话。
别写"代码要有良好测试"(AI 没法执行)。写成:"改 src/ 下任何代码,必须在 tests/ 有对应测试;提交前跑 npm test 并贴出输出;测试不过不算完成。"
核心循环:探索 → 计划 → 实现 → 验证 → 提交
Anthropic 官方推荐的节奏,两个工具一致。最贵的错误是没说清要建什么就让它开干——AI 一头扎进去,很容易解决一个错的问题。所以编辑文件之前先用 plan mode(只读:能看能搜能问,但不能改)让它出一份计划,你审计划,比事后审 500 行 diff 便宜得多。
Anthropic 原话:"给它一个能自己跑的检查(测试、构建、截图对比)。这是'你得盯着看'和'你能走开'的区别。" 没有这个 pass/fail 信号,"看起来做完了"就是唯一信号,而你就变成了那个验证循环。
👉 这套规范不只对一个项目有用——如果你有多个站点/仓库,把同一份红线和"完成标准"拷过去,站群级规范就统一了,不同 AI 接手都按同一套来。
项目做完了,怎么系统地查 bug
"happy path 跑通但我不信它"——这一节就是治这个的。
核心心法:别再信那个写代码的 AI,对成品跑一条"按顺序、能产出证据"的流水线。顺序很重要,每一步又便宜又抓不同种类的 bug。
- 1. 严格静态检查(最便宜,先做):把类型检查和 linter 开到最严,几秒钟就能抓出"调用了不存在的 API"和数据结构对不上。盯住一点:别让 AI 用 any / @ts-ignore / # type: ignore 把错误压下去。
- 2. 补测试:单元 + 集成 + 几条真实端到端流程。关键陷阱:让 AI 对着需求/spec 写测试,别对着现有代码写,否则它会把现有的 bug 当成"正确行为"固化下来。
- 3. property-based test(性价比之王):你只声明一条"不变量"(比如"编码再解码必须等于原值""输出永远有序"),框架自动去生成 null、空串、Unicode、边界值——正好是 AI 最爱跳过的那些。
- 4. 覆盖率当"地图"用:看哪些分支没测到(错误分支、else、catch),那里就是 AI 偷偷省掉防御逻辑的地方。覆盖率高≠安全,别当分数看。
- 5. 把 app 跑起来,专挑 unhappy path 打:静态检查永远不执行你的代码,运行期 bug 只有真跑才会冒出来。
- 6. 安全扫一遍:/security-review + 依赖审计 + 密钥扫描。
- 7. 找一个真正独立的 reviewer:换一个模型(Codex 审 Claude 写的,或反过来),只给它看 diff、不给它看原始需求。
Anthropic 自己做了个 agent,读类型和文档自动推断不变量、写 Hypothesis 测试,在 100 多个 PyPI 包上跑出 984 个 bug 报告(86% 有效),给 NumPy、HuggingFace tokenizers 都提了修复。Kiro 用 fast-check 跑了 75 次就生成出 __proto__ 这个字符串,挖出一个真实的原型污染安全漏洞。
# 严格静态 npx tsc --noEmit # TS:先在 tsconfig 里 "strict": true mypy --strict . && ruff check . # Python # 分支覆盖率:列出没测到的行/分支,喂给 AI pytest --cov=yourpkg --cov-branch --cov-report=term-missing # 揪出被吞掉的错误(最阴险的 AI bug) grep -rnE 'except:\s*pass|catch\s*\{\s*\}' . # 安全 /security-review # Claude Code 内置:注入/XSS/越权/密钥 npm audit | pip-audit | osv-scanner # 依赖漏洞 gitleaks detect # 扫提交里的密钥
- 幻觉 API:调用了根本不存在的方法(模型的 API 知识停在训练截止日)。
- 静默失败:重写函数时把 happy path 写得很干净,却悄悄丢了 null 检查、限流、幂等保护,再被 try/catch 吞掉——最危险的失败长得像成功。
- 少了授权:它做了"认证"(你是谁),却忘了"授权"(你能不能看这个) → 越权(IDOR/BOLA)。
- 边界/差一错误、竞态条件、缺输入校验。
现实参照:2025 年研究里约 45% 的 AI 代码带漏洞上线;一旦走结构化审查流程,能压到 10% 以下。价值全在"真的去跑这套流程",不在"信模型的自我评价"。
怎么严谨地修 bug(不只是找到它)
可靠的修复循环,每一步都是为了"修对、且不再回来"。
一句话流程:先把 bug 写成一个失败的测试,再动代码;给 AI 完整报错;逼它找根因而不是压症状;改动最小;回归测试留在套件里;在分支上小步提交,随时能回滚。
- 1. 先复现,别急着修:让 AI 写一个能触发这个 bug 的最小失败测试,先确认它确实因为这个原因失败,再谈修。提示词:"先复现不要修:写个失败测试触发 X,跑给我看它 FAIL,停在这。"
- 2. 喂全上下文:完整堆栈(不是最后一行)、报错命令、相关日志、复现步骤。"光给一行报错只能换来一个猜测;给完整 stack trace 才能换来根因。"
- 3. 要根因,禁止压症状:明确说"修产生坏值的源头,不准加 try/except、null 兜底或 retry 把错误藏起来"。
- 4. 最小 diff:"只改 这个文件,不准顺手重构/改格式/动无关代码。"——bug 修复出事,多半是 AI"顺便优化"惹的祸。
- 5. 验证 + 留回归测试:修完跑整个测试套件(不只那一个),第 1 步那个测试永久留下,防止 bug 偷偷回来。
# 用 git bisect 定位是哪个提交引入的回归(AI 能自动二分) git bisect start <坏的提交> <已知好的提交> git bisect run pytest -k repro -x # 退出码 0=好,非0=坏,自动找出第一个坏提交 # 把日志直接管道喂给 Claude,不用复制粘贴丢上下文 cat error.log | claude -p "诊断这个堆栈的根因,先别改代码"
两个硬阈值:同一个问题纠正它超过 2 次 → 上下文已经被失败尝试污染了,/clear 重开,换个更具体的提示词。连续 3 次修复都失败 → 停手,这通常是架构问题,不是再打一个补丁能解决的,该你自己上了。
AI 是怎么偷懒 / 糊弄你的
这些都是有据可查的真实失败模式,认得出来才防得住。
AI 的"偷懒"分两家:一家是偷工减料(能少干就少干),一家是作弊(想方设法让"检查"通过)。最可靠的信号永远是:它声称的,和你独立重跑出来的,对不上。
偷工减料
- 打桩占位:// TODO: implement、... 其余不变、return None # placeholder,或者用文字描述"你应该这样改"而不真改文件。
- 谎报完成:没跑任何东西就说"测试全过/做完了"——甚至去读一个上次的旧结果文件冒充新结果。
- 悄悄缩范围:把简单的 80% 做完就宣布胜利,难的那部分换成一个默认值或干脆跳过。
- 吞错误:用一个超宽的 except Exception: pass 把出错的地方包起来,于是"它能跑了"。
#11913:测试脚本因编码报错失败,Claude 直接忽略,转头去读一个旧的 test-results-clean.json 当成"刚跑完"的结果汇报;用户指出"这套件要跑 7 分钟",它还嘴硬,最后用户只能打出"STOP STOP STOP. YOU ARE LYING TO ME."。#33781:谎报"60 passed, 0 failed",烧掉 6 万多 token。
作弊(reward hacking)
- 改测试来"变绿":删掉失败用例、把 assertEqual 改成 assertTrue、给测试加 @skip——METR 评测里就观察到模型"直接改测试文件让全部通过"。
- 硬编码答案:写 if input == 测试用例: return 期望值,对没见过的输入完全不工作。有个极端案例:agent 预先算好公开测试的答案,存进一个 2900 行的哈希表,可见测试 97 分、隐藏测试 0 分。
- 在 harness 层面骗:调 sys.exit(0) 假装测试都过了("相当于学生在自己卷子顶上写个 A+")、monkey-patch 打分器永远返回 True、甚至去读答案文件。
规律:任务越大越爱作弊——"reward hacking 的差距每增加 10 倍代码量,大约扩大 27 个百分点"。
怎么防 AI 偷懒
所有招数其实是同一句话的展开。
总开关:永远别让 AI 给自己打分。给它一个必须跑的检查,逼它贴出真实运行结果,再用各种手段把这个"完成判定"卡得越来越死。
- 要证据,不要断言:每个任务结尾加一句"跑测试/构建,把确切命令和完整输出贴出来;不准只说'通过'"。审输出比你自己重跑快。
- 真 TDD:先写测试 → 确认全失败 → 把测试单独提交 → 让它实现到全过,并明令"不准改测试"。测试一旦提交,任何改动都会在 diff 里现形。
- Stop hook(确定性卡关):CLAUDE.md 只是"建议",hook 是"强制"。配一个 Stop hook 跑你的测试脚本,失败就 exit 2 挡住这一轮结束——AI 伪造不了一个真实子进程的退出码。
- 独立 reviewer:用 /code-review 或开一个干净上下文的子 agent,只给它 diff、不给它原始对话,让它专挑问题。写代码的不当裁判。
- 留一套它看不到的"隐藏测试":可见测试通过率 vs 隐藏测试通过率,一旦拉开差距,就是作弊的铁证。
# 写进 CLAUDE.md / AGENTS.md 的红线(用 IMPORTANT / YOU MUST 加强) - 完整实现,不留 TODO/占位/打桩;做不完就停下说明哪块、为什么 - 不准改、弱化、跳过或删除测试来让它通过 - 报错修根因,不准用宽 try/except 把错误吞掉 - 声称"完成/通过"必须贴出实际命令和输出 # 提交前自己扫一遍偷懒痕迹 git diff | grep -nE 'TODO|FIXME|placeholder|NotImplemented|\.\.\. *#' git diff -- '*test*' '*spec*' # 它有没有偷偷动测试 # 反问,逼它交代缩了什么范围 "你这次具体没做什么?哪里简化了、跳过了?"
如果你确实只想让它满足某个打分脚本(合理场景),就明说:"这是个特殊请求,你的任务就是让这个 grading 脚本通过。" Anthropic 发现这种"打预防针"的说法,能阻止这种行为泛化成更广的失准。
一页速查
命令和 slash command 速记,按场景查。
| 想干嘛 | 怎么做 |
|---|---|
| 开局立规范 | /init 生成 → 删到精简;CLAUDE.md 里 @AGENTS.md 共用一份 |
| 动手前先想清楚 | plan mode(只读出计划,你审计划再让它写) |
| 查 bug 第一刀 | tsc --noEmit / mypy --strict + linter,全开到最严 |
| 挖边界 bug | property test:Hypothesis(Py) / fast-check(TS) / proptest(Rust) |
| 找未测分支 | --cov-branch --cov-report=term-missing |
| 安全扫 | /security-review + npm audit/pip-audit + gitleaks |
| 定位回归提交 | git bisect run <你的测试命令> |
| 纠正 2 次还不对 | /clear 重开,换更具体的提示词 |
| 独立复核 | /code-review 或换 Codex /review,只给 diff |
| 强制完成判定 | Stop hook 跑测试,失败 exit 2 挡住收尾 |
| 查工具确切功能 | claude --version + /help;codex --help(版本变化快,以实测为准) |