ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Vibe Coding 实战:从逐行审查到信任式协作的编码习惯转变

Vibe Coding 实战:从逐行审查到信任式协作的编码习惯转变

1. 从逐行审查到信任式协作:我的编码习惯是怎么被掰弯的

第一次用 Coding Agent 帮我写完一个完整模块时,我盯着屏幕上那两百多行代码,心里其实挺矛盾的。一方面确实爽,原本要磨一下午的活,十几分钟就跑通了;另一方面又有点不踏实,总觉得不逐行看一遍,就像把家门钥匙交给一个刚认识的人。那段时间我的做法很拧巴:让 AI 写,然后自己再一行一行读回去,读完还要改注释、调命名、补边界判断。结果算下来,省下的时间又被审查吃回去了大半。

后来我慢慢意识到一个问题:我审查代码的方式,其实还停留在"读源码"这个动作上。但 AI 生成的代码和人类同事写的代码有个本质区别——人类写代码时,脑子里有一条清晰的推理链,你读代码其实是在重建这条链;而 AI 生成代码时,推理链是隐式的,它可能一次给出三种等价写法,也可能在某个角落塞进一个你没要求的抽象。你逐行读,读到的只是"结果",读不到"意图"。这就是为什么很多人读 AI 代码会觉得累,因为你在试图从结果反推一个根本不存在的、稳定的设计思路。

Vibe Coding 这个词最近被聊得很多,我的理解是:它描述的不是"不看代码",而是"把注意力从逐行语法转移到意图、边界和文档上"。我现在的工作流大致是这样的——先跟 Agent 把需求聊清楚,让它输出一份简短的实现说明(不是代码,是说明),我确认方向没问题,再让它写代码。代码写完后,我不再逐行读,而是做三件事:跑测试、看关键路径、读它自己写的文档。这个转变花了我大概两个月才适应,中间也翻过车,后面会细说。

需要先讲清楚的是,这套方法有明确的适用边界。它适合业务逻辑清晰、有测试兜底、迭代频繁的项目;不适合底层算法、并发控制、安全敏感这类"错一行就出大事"的场景。我现在的判断标准很简单:如果这段代码出错,我能不能在五分钟内通过测试或日志定位到问题?能,就放心交给 Agent;不能,就老老实实逐行看。这个标准帮我省了大量纠结时间。

2. Vibe Coding 到底在"vibe"什么:把审查对象从代码换成意图

2.1 逐行审查为什么在 AI 时代性价比暴跌

先说个反直觉的结论:逐行审查 AI 代码,很多时候是一种"伪安全感"。你花两小时读完三百行代码,感觉自己很严谨,但实际上你只是确认了"语法没错、逻辑看起来通顺",而真正会出问题的地方——比如某个边界条件、某个并发时序、某个第三方库的隐式行为——恰恰是你逐行读也读不出来的。人类代码审查之所以有效,是因为审查者和作者共享一套约定俗成的模式,你看到if (x != null)就知道作者在防空指针。但 AI 代码里这种"约定"是漂移的,同一个 Agent 在不同轮次可能用完全不同的风格处理同一类问题。

我做过一个粗略统计:在我经手的项目里,AI 生成代码真正出 bug 的地方,八成集中在三类——边界条件、状态管理、外部依赖调用。这三类问题有个共同点:它们都不是"读代码"能发现的,而是"跑起来"或者"看文档"才能发现的。换句话说,我把审查精力投在逐行读上,投入产出比极低。真正高价值的审查动作是设计测试用例、检查状态流转图、核对依赖文档。

2.2 意图对齐:比读代码更靠前的一步

我现在把大量精力前移到"意图对齐"阶段。具体做法是:在让 Agent 写代码之前,先让它用自然语言描述它打算怎么做,包括数据结构、关键函数、异常处理策略。这一步看起来多此一举,但它能拦住大部分方向性错误。比如有次我让它做一个"任务重试"功能,它描述里写的是"失败后立即重试三次",我一看就发现不对——我要的是指数退避,不是立即重试。如果直接让它写代码,我可能得读完代码才发现这个偏差。

意图对齐还有个隐藏好处:它逼着 Agent 把隐式假设显式化。LLM 生成代码时经常默默假设一些前提,比如"输入一定非空""网络一定可用""这个字段一定是字符串"。这些假设藏在代码里很难发现,但一旦让它用文字描述,它就会暴露出来。我现在会专门问一句:"你这个实现里有哪些前提假设?如果这些假设不成立会怎样?"这个问题经常能挖出一些我没想到的坑。

2.3 文档作为"第二双眼睛"的定位

标题里说"靠文档读懂",这不是修辞。我现在的习惯是:Agent 写完代码后,必须同步产出一份文档,内容包括模块职责、关键接口、数据流、已知限制、测试覆盖情况。这份文档不是给我看的"说明书",而是我用来做交叉验证的工具。我会拿文档和代码对照,重点看三处:文档里承诺的行为,代码里是否真的实现了;文档里提到的限制,代码里是否有对应的处理;文档里没提但代码里存在的行为,是不是多余的。

这个对照过程比逐行读代码高效得多,因为它是在"语义层"做比对,而不是在"语法层"。有次文档里写"该接口幂等",但我扫了一眼代码发现它内部有个自增计数器,明显不幂等。这种矛盾在逐行读时很容易被淹没在细节里,但在文档对照时一眼就能看出来。

3. 我的实际工作流:从需求到合并的六个动作

3.1 需求拆解与上下文投喂

我不会一上来就让 Agent 写代码,而是先做需求拆解。具体做法是把一个大需求切成若干个小任务,每个任务控制在"一个函数或一个类能搞定"的粒度。然后针对每个任务,我会投喂三类上下文:相关的现有代码、相关的接口文档、相关的约束条件(比如性能要求、兼容性要求)。上下文投喂的质量直接决定生成质量,这一点我踩过太多坑。

有个细节值得说:投喂上下文时,我倾向于给"最小必要集",而不是"越多越好"。早期我试过把整个项目代码都塞进去,结果 Agent 反而抓不住重点,生成的东西跟现有风格格格不入。后来我改成只给直接相关的文件,加上一段我手写的"背景说明",效果明显好很多。背景说明里我会写清楚:这个模块在整个系统里的位置、它和哪些模块交互、有哪些历史包袱不能动。

3.2 让 Agent 先写"实现说明"再写代码

这一步是我工作流里最关键的改动。具体操作是:给完上下文后,我不说"帮我写代码",而是说"先描述你打算怎么实现,包括数据结构、函数划分、异常处理、测试点"。Agent 给出的说明通常几百字,我花两三分钟读完,确认方向。如果方向不对,这时候改成本最低;如果方向对,我再让它基于这份说明写代码。

这个做法还有个附带效果:Agent 写代码时会参考自己刚写的说明,一致性明显提升。我观察到的现象是,跳过说明直接写代码时,Agent 容易在实现过程中"跑偏",写着写着就偏离了最初的设计;而先写说明再写代码,偏离概率低很多。这可能是因为说明本身构成了一种"自我约束"。

3.3 代码生成后的三件事:跑测试、看关键路径、读文档

代码生成后,我按固定顺序做三件事。第一件是跑测试,包括已有的回归测试和新写的针对性测试。测试不过,直接打回让 Agent 修,我不看代码。第二件是看关键路径,我会挑出这个模块最核心的两三个函数,快速扫一眼它们的控制流,重点看异常分支和边界处理。第三件是读文档,做前面说的交叉验证。

这三件事的顺序很重要。先跑测试是因为测试是"客观事实",能快速筛掉明显有问题的代码;再看关键路径是因为它需要一点主观判断,放在测试之后能省精力;最后读文档是因为它最耗时,放在最后能确保前面两步已经过滤掉大部分问题。

3.4 合并前的最后一道闸:变更影响面检查

合并之前,我会做一次变更影响面检查。具体做法是让 Agent 分析这次改动可能影响到哪些其他模块,然后我针对这些模块跑一遍相关测试。这一步经常能发现一些"看起来无关但实际有关"的连锁问题。比如有次改了一个工具函数的默认参数,结果影响到了三个调用方,其中一个调用方依赖旧默认值,测试直接挂了。

这个检查我建议不要完全交给 Agent,因为它对"影响面"的判断依赖它对整个项目的理解,而它往往只看到了你投喂的上下文。我的做法是让 Agent 给出一个初步列表,然后我自己再补一遍,重点补那些"跨模块的隐式依赖"。

4. 文档为什么成了我的主要阅读对象

4.1 文档是 AI 代码的"意图快照"

前面说过,AI 代码的推理链是隐式的,文档的价值就在于把这个隐式链显式化。一份好的文档应该回答四个问题:这个模块解决什么问题、它对外暴露什么接口、它的内部数据怎么流转、它有哪些已知限制。这四个问题对应的是"意图层"的信息,而不是"实现层"的细节。我读文档时,脑子里建立的是一个"行为模型",而不是"代码结构模型"。

这个行为模型有个好处:它更容易验证。我可以拿行为模型去对照测试用例,看测试是否覆盖了文档承诺的所有行为;也可以拿它去对照需求,看是否有遗漏。而代码结构模型很难直接验证,你只能靠"读得仔细"来保证正确性,这在 AI 代码场景下不太可靠。

4.2 文档和代码不一致时,我信谁

这是个很实际的问题。我的经验是:短期信代码,长期信文档,但必须立刻消除不一致。具体来说,如果文档说"幂等"但代码不幂等,我会先按代码的实际行为来处理当前任务(因为代码是运行时的真相),然后立刻让 Agent 修正文档或代码,消除这个矛盾。放任不一致的后果是,下次你再读文档时,你已经不知道哪句是真的了,文档就废了。

我现在的做法是,把"文档与代码一致性"作为合并前的一个硬性检查项。具体操作是让 Agent 自己对照一遍,列出所有不一致的地方,然后我逐条确认。这个检查花不了几分钟,但能避免文档逐渐腐烂。

4.3 给文档定"最小可用标准"

文档不是越详细越好。我早期让 Agent 写文档,它动辄写几千字,把每个函数都描述一遍,结果我根本读不完。后来我定了个"最小可用标准":每个模块的文档不超过一页,必须包含模块职责、对外接口、数据流图(文字描述即可)、已知限制、测试覆盖情况这五项。超出这个范围的细节,一律不写,需要时直接看代码。

这个标准的好处是,文档始终保持在"可读"的规模。我实测下来,一页以内的文档,我能在三分钟内读完并建立行为模型;超过两页,阅读成本就上去了,反而不如直接看代码。所以文档的价值不在于"全",而在于"精"。

5. 那些让我翻车的时刻:三个真实踩坑记录

5.1 一次"看起来没问题"的并发 bug

有次我让 Agent 写一个简单的缓存刷新逻辑,代码生成后测试全过,文档也写得清楚,我就直接合并了。结果上线后偶发数据不一致。排查了半天,发现是 Agent 在刷新缓存时用了"先删后写"的策略,但在并发场景下,删和写之间有个窗口期,其他请求会读到空值。这个问题逐行读代码也很难发现,因为单看每一行都没问题,问题出在时序上。

这个坑教给我一件事:涉及共享状态的代码,必须专门设计并发测试,不能只靠功能测试。现在我让 Agent 写这类代码时,会明确要求它说明"这段代码在并发场景下的行为",并且要求它给出并发测试用例。文档里也必须包含"并发安全性"这一项。

5.2 文档写得漂亮但代码没实现

还有一次,Agent 写的文档里明确说"该接口对输入做了长度校验",但我实际调用时传了个超长字符串,它照单全收。回去看代码,发现校验逻辑写在了另一个函数里,而这个接口根本没调用那个函数。文档描述的是"设计意图",代码实现的是"另一回事"。

这个坑让我养成了"文档代码对照"的习惯。具体做法是,对文档里每一条"承诺",我都在代码里找到对应的实现位置,确认它真的被执行了。这个动作听起来繁琐,但熟练之后很快,而且能拦住大量"文档与实现脱节"的问题。

5.3 过度信任导致的依赖版本灾难

最惨的一次是 Agent 引入了一个第三方库的新版本 API,代码在本地跑得好好的,部署到测试环境直接崩了,因为测试环境的依赖版本是锁定的旧版本。Agent 生成代码时默认用了它"知道"的最新 API,但它不知道我们项目的依赖约束。

这个坑的教训是:投喂上下文时,必须包含依赖版本信息。现在我每次让 Agent 写涉及外部库的代码时,都会把package.json或requirements.txt的相关部分贴给它,并且明确说"只能用这些版本里存在的 API"。文档里也要记录"依赖的库及版本约束",方便后续排查。

6. 让 Vibe Coding 真正稳下来的几个硬约束

6.1 测试覆盖率是底线,不是目标

我现在的项目里,AI 生成代码的合并前提是"关键路径测试覆盖率达标"。注意是"关键路径",不是"整体覆盖率"。整体覆盖率是个容易造假的指标,你写一堆无意义的断言就能刷上去。关键路径覆盖率才是有意义的,它衡量的是"核心逻辑是否被验证过"。

具体操作上,我会让 Agent 在写代码的同时写测试,并且要求它说明"这些测试覆盖了哪些关键路径、哪些边界条件没覆盖"。对于没覆盖的部分,我会判断是"确实不需要"还是"漏了",前者放过,后者补上。这个判断过程本身就是一种高质量的审查。

6.2 关键模块保留人工审查

不是所有代码都适合 Vibe Coding。我给自己划了条线:涉及资金、权限、数据一致性、外部支付的核心模块,一律人工逐行审查,不管 Agent 写得多漂亮。这些模块的特点是"错误成本极高",逐行审查的时间投入是值得的。其他模块则走 Vibe Coding 流程。

这条线不是固定的,会随着项目阶段调整。项目早期,我对 AI 代码的信任度低,人工审查比例高;项目稳定后,信任度上来,人工审查比例下降。但那条"核心模块"的线始终不动。

6.3 建立"回滚友好"的提交习惯

Vibe Coding 的一个隐含前提是"允许犯错",因为犯错成本低。要让犯错成本真的低,就得保证每次提交都能干净回滚。我的做法是:每个 AI 生成的改动单独提交,提交信息里写清楚"这次改了什么、为什么改、怎么验证的"。这样一旦出问题,能快速定位到具体提交并回滚。

这个习惯还有个附带好处:它逼着我把每次改动控制在合理粒度。如果一次提交改了五个不相关的模块,回滚时就会很痛苦。所以我现在会刻意把 AI 生成的改动拆成小提交,每个提交只做一件事。

7. 我对这套方法的一些个人体会

用到现在,我最大的感受是:Vibe Coding 不是"偷懒",而是"把精力重新分配"。以前我把大量时间花在逐行读代码上,现在我把这些时间花在设计测试、对齐意图、写文档、做影响面分析上。总工作量其实没少多少,但产出质量明显更稳,因为我把精力投在了"真正能发现问题"的地方。

另一个体会是,这套方法对"人的判断力"要求更高了,而不是更低。逐行读代码时,你只需要判断"这行对不对";而 Vibe Coding 时,你要判断"这个方向对不对""这个测试够不够""这个文档可不可信"。后者需要更全局的视角,也更依赖经验。所以我不建议新手直接上这套方法,先把基本功练扎实,再逐步过渡。

最后分享一个小技巧:我会定期让 Agent 回顾它自己之前写的代码和文档,问它"如果现在重写,你会怎么改"。这个动作经常能挖出一些当时没注意到的设计问题,也能帮我判断哪些模块该重构了。这个习惯我坚持了几个月,效果不错,推荐你也试试。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进