这次我偷懒是用 GPT-5.4 写的。
上篇:AI编程之我见2
第一篇写的时候,我还在被 WordPress 迁移那点破事折磨得死去活来。那时候最大的感受是,AI 确实能一下子帮你干完前 80%,但剩下那 20% 的小问题,足够把一个外行反反复复按在地上摩擦。
第二篇写的时候,我开始意识到,vibe coding 绝对不是撒手不管,回归测试才是底线。没有测试,你根本不知道 AI 是在帮你写代码,还是在悄悄把项目拆掉。
而到现在,我越来越觉得,这件事还得再往前走一步。
AI 编程到了今天,对我来说已经不是“怎么让 AI 多写几行代码”,而是“怎么驾驭 AI 这架马车”。
马当然是 AI。真正决定车翻不翻的,是你手里有没有缰绳。
到现在为止,我觉得自己最重要的三个抓手,已经慢慢固定下来了:纯用 AI 写代码,回归测试,契约文档。
这三个东西,不是三条零散小技巧,而是我这段时间一点点摸出来的一套系统。
驾车 vs. 拉车
第一个抓手,其实反而是最“野路子”的那个:纯用 AI 写代码。
这听起来好像很不稳,甚至像是在作死,但我现在反而觉得,这是一个绕不过去的阶段。因为如果你一直只是把 AI 当成一个高级搜索引擎,或者当成一个自动补全工具,那你其实根本没有真正进入 AI 编程。
只有当你真的让它从 0 到 1 去做,让它自己写、自己接线、自己跑、自己修,你才会真正看清楚这玩意到底是什么水平。
它强在哪,蠢在哪,什么时候会突然像开窍了一样,什么时候又会一本正经地胡来,这些都得在“真用”的过程中才看得出来。
而且也只有到了这个阶段,人类的角色才会开始变化。你不再只是一个亲手敲代码的人,而会慢慢变成一个调度 AI 的人。你开始关心的,不再只是“这段代码写出来没有”,而是:这个任务到底该怎么拆?这段上下文是不是已经污染了?哪些信息该给它,哪些信息现在给了反而坏事?它现在是在局部修 bug,还是已经开始顺手漂移整个架构?
说白了,不纯用 AI,你就永远只是把它当 copilot。纯用 AI 之后,你才会被迫长出“驾驶”的意识。
测试 vs. 侥幸
第二个抓手,就是我上一篇已经说过很多次的回归测试。
这玩意对 vibe coding 来说,真的是命根子。
因为 AI 最大的问题,从来不是写不出来,而是它太容易顺手改坏别的地方。眼前这个功能也许修好了,但昨天还能跑的流程、上周还正常的入口、某个角落里你根本想不到的东西,说不定已经被它悄悄带崩了。
没有测试的时候,每次改代码都像蒙着眼睛开车。你只能靠肉眼看,靠感觉猜,靠 AI 自己拍着胸脯说“应该没问题”。这种“应该”基本上没什么价值。
一旦有了回归测试,局面就不一样了。你不用跟它争辩,也不用靠主观感觉判断。过不过测试,就是唯一标准。挂了就继续修,过了再说。
所以我到现在依然觉得,外行做 vibe coding,第一原则还是死磕测试。每实现一个功能就补测试,每改一次代码就跑回归。这个习惯养不出来,后面项目一变大,迟早会吃大亏。
但后来我发现,光有测试,其实还是不够。因为测试能解决“别改坏”,却解决不了“别写丑”。
测试 vs. 质量下滑
AI 有一个很典型的特点,就是它特别擅长先把东西做出来,但也特别擅长在反复迭代里偷偷堆技术债。
死代码越来越多,helper 越来越散,重复逻辑到处都是,明明一个模块该负责的事,慢慢渗到另一个模块里去了。更麻烦的是,这些问题很多时候并不会立刻炸,测试也照样能过。
也就是说,项目可能功能上是健康的,但结构上已经开始发霉了。
所以这段时间我还自己实现了一个 Code Elegance Reviewer,它在开发过程中起到了很大的作用。
这玩意不查“功能对不对”,而是专门盯代码优雅性和架构质量。比如有没有重复实现、有没有废代码、有没有越堆越厚的兜底逻辑、哪些职责已经开始膨胀、哪些边界已经发虚、哪些地方该抽 helper、哪些地方该收回模块内部。
如果说回归测试是在防止功能回归,那 Code Elegance Reviewer 更像是在防止代码质量慢性下滑。
它不是第四大支柱,更像是开发过程里的巡检员。很多问题短期内不会把项目炸掉,但你不早点收拾,后面一定会越滚越大。尤其是 AI 参与开发的时候,这种“看起来都能跑,但实际上越来越臃肿”的趋势特别明显。
文档 vs. 说明书
而第三个抓手,就是我最近越来越重视的东西:契约文档。
我现在的感受已经很明确了,防止架构漂移这件事,文档系统太重要了。
但这里说的文档,不是传统意义上那种技术文档、开发文档,不是那种写给人类看、写完就放在角落里吃灰的说明书。那种文档当然也有用,但不是我现在说的重点。
我说的这个文档,是专门锁定契约的,是为了 AI 并行开发而设计的。
它锁的不是实现细节,而是边界。
哪些模块各自负责什么,哪些路径归谁,哪些入口是稳定的,哪些文件是共享瓶颈点,哪些改动一碰就自动升级成跨模块任务,哪些是硬约束,哪些只是默认建议。这些东西必须被写清楚,而且最好是让 agent 直接能读。
因为测试只能告诉你“结果炸没炸”,它不能提前告诉 AI:“这里不是你该动的地方。”
契约文档干的,就是这个事。
渐进式披露
我最近在 SnowFox 这个独立应用上,对这件事的体会特别深。
那个仓库里的文档系统,本质上就不是为了介绍项目,而是为了让 coding agent 能安全开工。最前面先是 AGENTS.md,规定阅读顺序;然后是 docs/00-03,分别去讲约束模型、系统架构、模块地图、共享瓶颈点;再往后才是每个模块自己的文档和对应的 runbook。
这套东西最核心的思想之一,就是“渐进式披露”。
这个词听起来有点学术,说白了其实很简单:别一股脑把所有信息全塞给 AI。
因为 agent 的上下文空间是有限的。你把整个仓库背景、所有历史、所有恩怨情仇一次性喂进去,它不一定更聪明,很多时候只会更乱。上下文一多,它反而更容易抓错重点,更容易把局部任务升级成全局乱改。
所以正确的做法,不是“把所有信息都给它”,而是“在正确的时候,只给它当前任务真正需要的信息”。
先让它知道任务属于哪个模块。再让它去看这个模块相关的文档。如果命中了共享瓶颈点,再补读跨模块流程。如果只是一个局部 GUI 修改,就不要让它背着整个库存核心和迁移系统的历史包袱上阵。
这才是真正适合 agent 的文档方式。
并行 vs. 冲突
契约文档的另一个核心思想,就是模块化解耦。
这也不是为了画一个漂亮架构图,而是为了并行开发不互相踩踏。
AI 单兵已经很强了,但真正可怕的地方其实在并行。你可以开多个 agent 一起干活,但并行的前提不是“多开几个窗口”,而是边界必须足够清楚。否则效率不一定翻倍,冲突和覆盖倒是一定翻倍。
所以我现在越来越在意的一件事,就是把共享瓶颈点单独拎出来。
像 SnowFox 里,lib/tool_api.py、lib/tool_registry.py、app_gui/main.py、app_gui/tool_bridge.py 这种文件,就不是普通文件,而是模块汇合口。它们不是谁都不能碰,但你一旦碰它,就别再假装这是一个普通小任务了,这本质上已经是跨模块任务。
然后再按一种很朴素的方式去拆:能在模块内部解决的,就别上升到共享层。必须改共享入口的时候,只让一个 agent 去收口。其他 agent 围绕它,各改各自模块内部实现。
这套东西一旦立起来,AI 并行开发才开始真正变得可控。
文档也要进测试
更妙的一点是,契约文档最好别只靠大家自觉遵守。
我现在越来越喜欢的一种做法,就是让文档本身也进测试。把 Markdown 里的契约块做成机器可读,然后用 contract tests 去检查:哪些契约块必须存在,哪些路径在文档里写了但磁盘上根本不存在,哪些模块文档和 runbook 对不上,哪些共享瓶颈点少了一个都会直接报红。
到了这一步,文档就不再是“建议你看一看”的参考资料,而是正式进入系统本身。
测试负责防止功能回归,契约文档负责防止架构漂移,而文档测试则负责防止“契约本身变成一句空话”。
最后
所以我现在对 AI 编程的理解,已经慢慢从“让 AI 替我写代码”,变成了“我来设计它应该怎么写、写到哪、什么地方不能越界”。
如果继续用“马车”这个比喻,那我现在的感觉就是:
纯用 AI 写代码,是先让马跑起来。
回归测试,是给车装上刹车。
契约文档,是先把路、车道线和路标画出来。Code Elegance Reviewer,则像是时不时下车看看这辆车有没有越跑越歪、越跑越散架。
少了第一个,你根本不知道这匹马能跑多快。少了第二个,你迟早翻车。少了第三个,你就算不翻车,也会越跑越偏。而少了对代码优雅性和技术债的持续审视,车也许还能开,但会越来越笨重,越来越难修。
第一篇的时候,我还在问 AI 能不能帮我写。第二篇的时候,我开始问它写完以后怎么验收。到了现在,我越来越关心的已经变成:我到底有没有本事不被它带着跑。
模型当然重要,马力越大越爽。但马力不是全部。再强的马,没有缰绳、没有刹车、没有路标,照样能把车拉进沟里。
说白了,以前是自己拉车,现在是学会怎么驾车。