作者变审稿人——这是这一课里最难的一次转身,因为它反的是十几年的习惯。 写代码时你在求「对」,审稿时你在求「符合意图」——这两个「对」不是一回事。 任务描述也要换口径:从「怎么写」改成「要什么」,从「步骤」改成「验收」。
前两篇讲工具变了什么。这一篇讲人得变什么。 这是全书唯一一篇几乎不涉及技术、只涉及习惯的。 但它是后面所有章节的前提—— 如果还按「写代码」的心态用它,那工具再强,你也只能拿到「更快的代码」,拿不到「交付」。 这一篇的 Demo 做了一个能算的实验:同一件事,用「怎么写」和「要什么」两种说法各写一遍, 按五个要素逐条对,看各缺几项、要追问几次。 打分是规则匹配,不是模型打分——这一点在 Demo 里会明说。
一、你的角色从作者变成审稿人
写代码的时候,你是作者。 作者关心的是「这段逻辑对不对」——变量名、边界、复杂度、这一行能不能再省一个判断。 这些是「造」的活儿。 交给它做之后,你变成审稿人。 审稿人不关心「你怎么想出来的」,只关心三件事: 这是不是我要的、有没有碰到不该碰的、出问题退得回去吗。 这三件事,没有一件是「写代码」的技能。 这个转身难,难在它有损失感。 你花了十年练出来的手速、对 API 的熟悉、对边界条件的直觉,突然用不上了。 而且你会经历一段「我好像什么都不会了」的时期——因为你原来最擅长的那个环节,被抽走了。 但抽走的只是「造」,没抽走「判」。 判的能力恰恰是那十年练出来的——你能一眼看出这段代码有问题,正是因为你写过很多烂代码。 审稿人的本事,是作者的本事沉淀下来的。
二、任务描述从「怎么写」改到「要什么」
新人最容易犯的错,是把「实现方案」当成「任务描述」。 比如:「用 re.match,正则写成 ^[a-z0-9]+@...$,返回 bool」。 这段话里全是「怎么」,一个「什么」都没有。 为什么这很糟? 第一,它把方案锁死了—— 万一项目里已经有现成的邮箱校验工具函数,它也不会去用,因为你让它照着做。 第二,它没有验收标准——做完了怎么算做完?正则对不对怎么判? 第三,它没有边界——254 字符上限、连续两个点、大写域名,这些它都不会管。 换成「要什么」的说法,同样一件事会写成: 「注册接口现在会放过没有 @ 的邮箱。 加一道校验:没有 @ 的、超过 254 字符的、@ 后面没有点的,都拒掉,错误信息用中文说清哪里不对。 只改注册函数,现有三条用例必须继续通过。」 这段话里没有一句「怎么实现」,但每一句都是可验证的。 它甚至把「用什么方案」这个决定还给了工具—— 工具如果发现项目里已有工具函数,会直接用,这比你指定的方案更好。
三、验收标准必须写在动手之前
这是最容易忽略、后果最严重的一条。 验收标准写在动手之前,是「约束」;写在动手之后,是「找茬」。 先写验收,工具才知道往哪个方向收敛。 你写「三条用例必须继续通过」,它就会在改完之后自己跑一遍; 你写「错误信息用中文」,它就不会给你英文报错。 这些约束如果事后才提,等于让它白做一遍。 而且验收标准写下来之后,你自己也会变清醒。 很多人写着写着才发现:我其实没想清楚「做完是什么样」。 这个发现越早越好——在动手之前发现,改一句话;动手之后发现,重做一遍。 一个可操作的做法: 描述任务时先写最后一句——「做完之后,我怎么验证它做对了?」 这句话写不出来,说明任务还没想清楚,先别交出去。
四、转变发生的四个信号
| 信号 | 转变前 | 转变后 |
|---|---|---|
| 看 diff 的方式 | 逐行读逻辑 | 先看改了哪些文件、多少行 |
| 描述任务 | 说步骤 | 说输入输出、边界、验收 |
| 出错的反应 | 自己上手改 | 先问「是我没说清吗」 |
| 对工具的评价 | 它聪不聪明 | 它守不守规矩、验不验证 |
四个信号里,第三个最能说明问题。 出错了先问「是我没说清吗」,说明你已经把它当成协作者了—— 协作出问题,第一反应是查沟通,不是替对方重做。 第四个信号是评价标准变了。 「聪不聪明」这个指标其实没用——聪明但乱改的工具,比笨但守规矩的工具危险得多。 所以后面几章的重点全在「怎么让它守规矩」,不在「怎么让它更聪明」。 最后提醒一句:这四个信号不会一起到。 通常是先变第二个(描述方式),再变第一个(看 diff),第三个和第四个要慢得多—— 因为它们改的是肌肉记忆,不是知识。
五、动手 Demo:同一件事,两种说法,追问次数差多少
这个 Demo 拿同一件事写两段描述:一段告诉它「怎么写」,一段告诉它「要什么」。 然后按五个要素逐条对照。 打分的方式要说清楚:这是关键词规则匹配,不是模型打分。 每缺一项算一次追问——真机上是模型自己判断要不要问,有时候它不问、直接猜,那才是最贵的。 五个要素:输入(改哪儿)、输出(要什么)、边界(什么算例外)、验收(怎么算完)、 禁区(什么不许动)。这五项是我从教材 1.3 和 6.1 的写法里归出来的,不是标准答案。
# 写法一:告诉它「怎么写」
A = "在 user.py 里加一个 validate_email 函数,用 re.match," \
"正则写成 ^[a-z0-9]+@[a-z]+\.[a-z]{2,}$,返回 bool。"
# 写法二:告诉它「要什么」
B = "注册接口现在会放过没有 @ 的邮箱。加一道校验:没有 @ 的、" \
"超过 254 字符的、@ 后面没有点的,都拒掉,错误信息用中文," \
"写清新邮箱哪里不对。只改 user.py 的注册函数," \
"现有 test_user.py 里的三条用例必须继续通过。"
# 五个要素,按关键词命中判「有没有」
KEYS = [
("输入", ["文件", ".py", "函数", "接口"]),
("输出", ["返回", "错误信息", "错误", "拒"]),
("边界", ["超过", "没有", "空", "例外"]),
("验收", ["通过", "测试", "用例", "必须"]),
("禁区", ["只改", "不许", "别动", "不要"]),
]
实跑输出:
--------------------------------------------------------------------
同一件事,两种说法,追问次数差多少
--------------------------------------------------------------------
① 五个必填要素
--------------------------------------------------------------------
一个能被直接执行的任务描述,至少要说清五件事:
| 要素 | 回答的问题 | 缺了会怎样 |
|---|---|---|
| 输入 | 改哪个文件、哪个函数 | 它从仓库里瞎猜 |
| 输出 | 要什么结果、什么形态 | 做出来不是你要的 |
| 边界 | 什么情况算例外 | 边界条件全漏 |
| 验收 | 怎么算做完 | 它自己说做完了 |
| 禁区 | 什么不许动 | 顺手重构一片 |
② 写法一:告诉它「怎么写」
--------------------------------------------------------------------
原文:
在 user.py 里加一个 validate_email 函数,用 re.match,正则写成 ^[a-z0-9]+@[a-z]+\.[a-z]{2,}$,
返回 bool。
③ 写法二:告诉它「要什么」
--------------------------------------------------------------------
原文:
注册接口现在会放过没有 @ 的邮箱。加一道校验:没有 @ 的、超过 254 字符的、@ 后面
没有点的,都拒掉,错误信息用中文,写清新邮箱哪里不对。
只改 user.py 的注册函数,现有 test_user.py 里的三条用例必须继续通过。
④ 按五个要素逐条对
--------------------------------------------------------------------
| 要素 | 写法一(怎么写) | 写法二(要什么) |
|---|:---:|:---:|
| 输入 | 有 | 有 |
| 输出 | 有 | 有 |
| 边界 | 缺 | 有 |
| 验收 | 缺 | 有 |
| 禁区 | 缺 | 有 |
⑤ 追问次数与到手速度
--------------------------------------------------------------------
| 写法 | 缺几项 | 预计追问 | 打回重做的风险 |
|---|---:|---:|---|
| 怎么写 | 3 | 3 次 | 高(验收标准没说) |
| 要什么 | 0 | 0 次 | 低(验收写在里面了) |
追问次数按「缺一项问一句」算。
这是近似——真机上是模型自己判断要不要问,有时候它不问、直接猜,那才是最贵的:猜错要重来。
! 写法一里那段正则看着很专业,但它把「怎么实现」当成了任务。
结果是没有边界、没有验收、没有禁区——最要命的三项全缺。
还有一个隐性成本:写法一把实现方式写死了(必须 re.match、必须那个正则),
万一项目里邮箱校验已经有现成的工具函数,它也不会去用——因为你让它照着做,不是让它解决问题。
结果很集中:写法一缺三项(边界、验收、禁区),写法二一项不缺。
注意写法一并不「短」——它甚至更长,因为它把正则的每一个字符都写出来了。
长不等于清楚。
写法一为什么必然缺这三项?
因为当你在描述「怎么实现」的时候,脑子里装的是实现细节——
正则怎么写、返回什么类型、函数叫什么。
这些占满了注意力,边界和验收自然就想不起来了。
还有一个隐性成本,表格里看不出来:
写法一把方案锁死了(必须 re.match、必须那个正则)。
万一项目里已经有邮箱校验的工具函数,它也不会去用。
你让它照着做,它就不会替你解决问题。
最后说清这个 Demo 的局限:
关键词匹配只能判「有没有提到」,判不了「说得对不对」。
写法二的「超过 254 字符」看上去像边界,但如果业务上其实允许更长的地址,这条就是错的边界。
机器判不了这个,只有你能判。
30 秒实验
- 把你正在做的一件事,试着只写「要什么」,不写「怎么做」。 写不出来,说明你自己还没想清楚——这时候交出去,返工概率极高。
- 下次任务被做歪了,先别改代码,先回头看你的描述缺了五项里的哪一项。 十次里至少有六次,问题在「验收没写」上。
- 练一个动作:描述任何任务时,先写最后一句——「做完之后,我怎么验证它做对了?」 这句话是整段描述的地基。
常见误区
- 「我写得越细,它做得越对。」 只对一半。 细在「要什么」上有用,细在「怎么做」上有害—— 后者会锁死方案、挤掉边界、还让它不敢用现成的东西。
- 「验收标准等它做完再看。」 那就变成找茬了。 验收写在前面是约束,写在后面是返工——同一句话,位置不同,成本差一个数量级。
- 「我审不动它的代码,因为它写得比我快。」 审稿人不需要比作者写得快,需要能判断「这是不是我要的」。 审不动通常不是速度问题,是「验收标准没写」——标准清楚了,审起来是几分钟的事。
自测三问(点开对答案)
1. 为什么「告诉它怎么写」反而容易做歪? —— **因为写实现细节时,注意力全在方案上,边界、验收、禁区这三项最容易被挤掉。** **而且方案被锁死之后,它不会去用项目里现成的东西**——**你让它照着做,它就不替你解决问题。** 2. 验收标准为什么必须写在动手之前? —— **写在前面是约束,工具知道往哪儿收敛;写在后面是找茬,等于让它白做一遍。** **而且写验收的过程会逼你想清楚「做完是什么样」**——这个发现越早越便宜。 3. 「出错了先问『是我没说清吗』」为什么是个好信号? —— **因为它说明你已经把它当协作者,不是当工具。** **协作出问题的第一反应是查沟通,不是替对方重做**——**这也是审稿人心态的起点。**下一篇 1.4 收这一章:既然要靠「说清」和「验证」,那有没有比每次都说更省事的办法? 有——把规则写进仓库,一次写、次次生效。
- ☐ (本篇完,下一篇 1.4 规则优先于「更聪明」:工程视角下的 Codex 使用哲学)