四道题答不上来就别动手:入口、数据流向、改动半径、验证方法。 复述验证的用法是「让它说,你去核那几条一查就露馅的」。 第一轮一行没改,但它产出的地图让后两轮从「摸着改」变成「照着改」。
上一篇把规矩写进了文件。这一篇讲动手之前的那一步。 为什么这一步要单独成篇? 因为在陌生代码库里,「理解」和「修改」的边界最容易糊掉—— 改着改着才发现自己理解的架构是错的,前面改的全要回退。 Demo 用六道题让 Agent 复述,逐条跟正解对,四条对两条错; 再演示三轮推进,第一轮改动量为零,但它是后两轮的前提。
一、理解的四道题
四道题不是随便挑的,它们对应四种「改坏」的方式。
| 题 | 答不上来会怎样 |
|---|---|
| 入口在哪 | 改了一个没人调用的函数 |
| 数据从哪来、到哪去 | 改了字段,下游全崩 |
| 会碰哪些文件 | 改动半径估错,越改越大 |
| 怎么验证没改坏 | 没有回头路,只能靠眼看 |
第一题最基础,也最容易跳过。
同一个功能可能有两个入口:一个走 HTTP,一个走定时任务。
你改了共用的那个函数,以为只影响一个,其实两个都变了。
第二题是「改坏」最常见的形态。
一个字段从 name 改成 title,看起来是局部改动——
但所有读这个字段的地方都得跟着改,而那些地方你还没看过。
第三题决定这件事要花多久。
估小了,改到一半发现牵连七个文件,你会开始「先把能跑的路径糊过去」——技术债就是这么产生的。
第四题是唯一一条「没有它就别动手」的。
没有验证手段的改动,等于把「有没有改坏」交给运气。
遗留项目里这一条尤其致命,下一篇细讲。
二、复述验证
让 Agent 把理解复述一遍,然后你去对。
这件事的价值不在于「听它说」,在于「它的说法能被一条命令查掉」。
Demo 里六道题它对四条,错的两条很典型:
一条把入口说成前端文件 web/index.js,一条把验证命令说成 make test。
这两条错的共同点:都能用一条命令否掉。
make 在不在 PATH 里,一句 which 就有答案;入口文件在不在,看目录就知道。
所以复述验证的正确用法是:你不用核全部,只核那几条一查就露馅的。
这跟「逐条审阅」是两种工作方式。
逐条审阅要求你比它更懂,成本极高;
挑几条查,成本很低,而且能查出来的错往往是系统性的——
它把入口搞错,说明它没读 main;它把命令搞错,说明它没跑过。
这两个「没」比那两条错本身更值得知道。
所以复述之后要问的不是「对不对」,是「它凭什么这么说」。
答案如果是「从目录结构推的」,那就还得验;如果是「跑过一次」,可信度就高一个量级。
三、不动手也算进度
Demo 里三轮推进,第一轮的改动量是零。 但这一轮不是空转:它产出了地图,而地图决定了第二轮补哪个测试、第三轮改哪一行。 很多人卡在「今天什么都没写」这件事上。 在陌生代码库里,这个感受是错的——因为「知道该改哪儿」本身就是进度,而且是不可跳过的那部分。 判断标准只有一条:这一轮产出的东西,下一轮能不能直接用。
| 看起来像进度 | 实际 |
|---|---|
| 先改两行试试 | 没用,还没定位 |
| 画出依赖关系 | 有用,第三轮靠它定半径 |
| 跑一遍现有测试 | 有用,这就是回头路 |
| 顺手改个错别字 | 没用,还污染 diff |
最后一行要多说一句。 顺手改的东西会混进 diff 里,让审查者分不清哪几行是在修问题。 它本身没错,但它增加了别人的判断成本——下一篇整篇都在讲这件事。
四、动手 Demo:先理解再修改:四道题与复述验证
先让 Agent 就六道题各复述一句,逐条跟正解比;
再走三轮:只读、补测试、改代码,每轮的改动量用 git diff 真量。
六道题里两道是故意让它答错的,为的是演示「错得能被一条命令查掉」。
QUIZ = [
("入口在哪", "app/main.py", "web/index.js"),
("订单表谁写", "app/models/order.py", "app/models/order.py"),
("支付回调走哪个服务", "app/services/pay.py", "app/services/pay.py"),
("改库存要碰几个文件", "3", "3"),
("怎么验证没改坏", "跑 tests/ 下的 unittest", "make test"),
("缓存谁清", "core/tasks/clean.py", "core/tasks/clean.py"),
]
for i, (q, ok, said) in enumerate(QUIZ, 1):
print(i, q, ok, said, "对" if said == ok else "错")
实跑输出:
--------------------------------------------------------------------
先理解再修改:四道题与复述验证
--------------------------------------------------------------------
① 四道题
--------------------------------------------------------------------
| # | 题 | 答不上来意味着 |
|---|---|---|
| 1 | 入口在哪 | 你不知道改动从哪被执行 |
| 2 | 数据从哪来、到哪去 | 你不知道改了会影响谁 |
| 3 | 改这个功能会碰哪些文件 | 你估不出改动半径 |
| 4 | 怎么验证没改坏 | 你没有回头路 |
四道题全答对之前别动手。答不上第三题的时候动手,改出来的东西基本都要重做。
② 让 Agent 复述一遍,逐条对
--------------------------------------------------------------------
| # | 问题 | 正解 | 复述里写的 | 判定 |
|---|---|---|---|---|
| 1 | 入口在哪 | app/main.py | web/index.js | 错 |
| 2 | 订单表谁写 | app/models/order.py | app/models/order.py | 对 |
| 3 | 支付回调走哪个服务 | app/services/pay.py | app/services/pay.py | 对 |
| 4 | 改库存要碰几个文件 | 3 | 3 | 对 |
| 5 | 怎么验证没改坏 | 跑 tests/ 下的 unittest | make test | 错 |
| 6 | 缓存谁清 | core/tasks/clean.py | core/tasks/clean.py | 对 |
六条对 4 条。错的两条,一条把入口说成前端文件,一条把验证命令说成 make test。
! 关键不是「它答错了」,是「错得能被一条命令查出来」。make test 跑一下就知道不存在。
所以复述验证的用法是:让它复述,你只用去核那几条「一查就露馅」的,不用核全部。
顺手验一下那条错的:make 在 PATH 里吗——不在。
③ 三轮推进,改动量摆出来
--------------------------------------------------------------------
第 0 轮(还没动手)的 diff:空的,一行没改
| 轮次 | 做了什么 | 文件 | 增 | 删 | 产出 |
|---|---|---|---:|---:|---|
| 1 | 只读,不动手 | 0 | 0 | 0 | 地图 1 份 |
| 2 | 补一条特征测试 | 1 | 18 | 0 | 钉住现状 |
| 3 | 改目标函数 | 1 | 2 | 2 | 行为变更 |
! 第一轮一行代码没改,但它不是空转——那份地图让后两轮的改动从「摸着改」变成「照着改」。
④ 不动手也算进度
--------------------------------------------------------------------
| 看起来像进度 | 实际有没有用 |
|---|---|
| 先改两行试试 | 没用,还没定位 |
| 画出依赖关系 | 有用,第三轮靠它定半径 |
| 跑一遍现有测试 | 有用,这就是回头路 |
| 顺手改个错别字 | 没用,还污染 diff |
判断标准只有一条:这一轮产出的东西,下一轮能不能直接用。
六条对四条。
这两个错误不是随机分布的:一条错在「没读入口」,一条错在「没跑命令」。
这两件事恰好是「进入陌生仓库」最该先做的两件事。
所以复述输出的真正价值,是暴露它跳过了哪一步。
对四条的答案可能全是猜的,错的那两条反而信息量大。
三轮那张表里,第一行的三个零是有意的。
只读那一轮真的没改任何文件,git diff 是空的。
但没有它,第二轮不知道该补哪个测试——
补测试的前提是知道「哪个行为不能变」,而这个只能从地图里来。
第三轮只改了一个文件、四行。
这是刻意的:有了前两轮,改动范围是算出来的,不是试出来的。
下一篇会把「改动范围」量化成三个数字。
30 秒实验
- 拿一个你不熟的模块,试着回答那四道题。 哪一道卡住了,就从哪一道开始读——不要从头读。
- 让 Agent 复述它的理解,然后只核「入口」和「验证命令」这两条。 两条都对,再往下问。
- 下次动手前,先提交一个空 diff 之外的「只读轮」: 产出一份依赖清单,什么都不改。看第二轮是不是快了很多。
常见误区
- 「理解就是读代码,读完了自然就懂了。」 读完不等于能回答那四道题。 读完只说明你见过,能答上来才说明你定位过。
- 「复述要逐条审,不然不放心。」 逐条审要求你比它更懂,成本极高。 挑几条一查就露馅的,反而能看出它跳了哪一步。
- 「没写代码就是没进度。」 在陌生仓库里这是错的。 判断标准是「下一轮能不能直接用」,不是「今天写了几行」。
自测三问(点开对答案)
1. 四道题分别对应哪种改坏方式? —— **入口错改到没人调用的地方;数据流向错下游全崩;半径估错越改越大;没验证等于靠运气。** 2. 复述验证为什么不用逐条核? —— **因为逐条核要求你比它更懂。** **挑「一查就露馅」的几条,成本低,而且能看出它跳过了哪一步。** 3. 第一轮改动为零,凭什么算进度? —— **因为它产出了地图,而地图决定了后面补哪个测试、改哪一行。** **判断标准是下一轮能不能直接用。**下一篇把「改动范围」量化:三个数字,一眼看出这份改动好不好审。
- ☐ (本篇完,下一篇 9.4 产出审查友好的最小变更)