此中文翻译尚未经过母语人士校对,如有不通顺之处,请以英文版为准。
夜间自动化:我开始在睡觉的时候,把写代码的活交给 AI
U-PRO Build Journal(产品构建日记)— 第 4 期
第 3 期的结尾,是一个我原本没打算优先做的场景——报废车回收场,而不是日常汽修厂——最终被做成了一个功能。但我当时没提到的是,差不多同一段时间里,我自己的工作时长发生了什么变化。就在那段日子里的某个节点——没有经过任何一次明确的决定——我不再是唯一一个往这个项目里提交 commit 的人了。
我想把这里实际发生的事情讲得尽量准确,因为我坐下来写这一期之前参考的笔记,一拿去跟真实记录核对,就站不住脚了。那份笔记描述的大概是一整夜连轴转的场景——傍晚开始,一路做到凌晨,那段时间里落地了二十来个 commit。可当我把真实的 commit 历史拉出来核对细节时,发现根本不是这样。那天晚上我以自己名义提交的最后一个 commit,时间是 19:41。之后整晚都没有我的 commit 了。下一批 commit 出现在另一个作者名下——也就是我刚开始合作的那个 AI 助手——一直到刚过凌晨 5 点才出现,其中十三个几乎在同一个几秒钟的窗口内落地。第二批共十七个,出现在刚过上午 10 点,同样扎堆得很紧,明显是作为一整批一次性提交的,而不是随着工作进度一个个提交的。

现在回头看,我觉得挺好笑的一点是:早先那份笔记其实什么都没编造。里面的数字——commit 落在大约 22:15,又一批落在大约 03:03——都是 git 历史里真实存在的时间戳,只是记录用的是另一个时区的钟,跟挂在我墙上那个不一样。跑那批 commit 的机器,系统时钟设的是 UTC,不是曼谷时间,而从拉取原始日志到写成文字的中间某个环节,没有人把那七个小时补回去。按字面、按本地时间去读,22:15 到 03:03 看起来像是一段戏剧性的通宵。但换算成我实际所在的时区,就变成了凌晨 5:15 到上午 10:03——是一大批在早餐时间前后冒出来的工作,而不是像这一期标题暗示的那样,发生在我睡着的时候。这些工作背后的实际劳动很可能确实是在夜里完成的——好几条 commit message 里直接写着"今晚"——但我已经没办法准确告诉你,那些工作到底占用了曼谷时间的哪几个小时,我最初参照的那份笔记同样也说不出来。记录能明确说清楚的是:在我第二天早上再次碰键盘之前,有三十个 commit 落在一个不是我的名字之下,覆盖了大概十几项不同的功能。

我花这么大篇幅纠结一个时间戳,而不是直接讲有意思的部分,是因为这背后有个道理,在这个系列后面会越来越重要:一个看起来精确的数字,和一个真正正确的数字,不是一回事,两者之间的落差完全可能撑过一整轮写作都没被任何人发现。这对"这件事到底几点发生"这种无关紧要的小细节成立,事实证明,对更重大的说法同样成立——包括我在这一期后面会讲到的,关于一项工作到底有没有真正做完的说法。
别人交出的三十个 commit,到底是什么样子

那批 commit 的规模值得好好琢磨一下,因为把它交出去可不是件小事。以我能指出的两个最典型的 commit 为区间,这批工作涵盖了:一个忘记密码找回流程,让店主就算丢了访问权限也不会被永久锁在外面;三个层级的 platform-admin(平台管理员)角色;修复了一个 audit-log(审计日志)的 bug——之前根本没有真正记录下是谁做了变更;一个把已有客户记录导入进来的 CSV 导入通道;第 3 期提到的报废车辆入库功能的前半部分,已经可以工作;以及两份内部文档——一份日常操作流程,一份功能参考手册——按理说本该早就存在,结果一查,根本没有。
最后这两份文档,才是这一期我真正想聊的部分,因为这不是一个"AI 助手一次能干多少活"的故事,而是一个关于"你赖以判断某件事是不是'做完了'的记录,结果是错的"的故事——以及在你在它之上继续盖楼之前,抓住这个问题需要付出什么。
信任是一条曲线,不是一个开关
在讲到那天晚上真正重要的部分之前,我想先说清楚这次转变的"形状",因为很容易把它压扁成一个瞬间——"我决定让 AI 替我写代码了"——但实际情况完全不是这样发生的。
第一次看到 AI 助手做出让人印象深刻的事情时,会有一种很自然的冲动,直接跳到"现在我可以把一切都交出去了"。我也有过这种冲动。但我没有照着做,与其说是自律,不如说更多是巧合——因为那天晚上第一次以别人的名义落地的工作,不是一个我能单独拿出来评估的、自成一体的功能,而是同时发生的十几件不同的事,风险等级各不相同,从一个 CSV 导入通道,到涉及客户账户锁定/解锁方式的改动都有。没办法对所有这些一视同仁地给予全面信任,因为它们的风险本来就不一样,如果硬要一视同仁,要么就是对危险的部分放得太松,要么就是对安全的部分疑心太重。
回过头看,实际发生的情况更像一条曲线,而不是一个开关:信任的扩展程度,取决于一个说法到底有多容易被验证,以及万一这个说法是假的,后果会有多糟。CSV 导入要么产出你预期的那些行数据,要么不会——看一眼数据,三十秒就能核实。"这份文档已经创建好了"这样的说法,只要想起来去查,几乎一样快就能核实,而"想起来去查"恰恰是当时还没养成的习惯,也正是那天晚上被逼着建立起来的习惯。至于一项安全修复到底有没有真的落到生产数据库上——这条线索会在两期之后重新出现——则需要认真、刻意地花功夫才能核实清楚,而这恰恰是那种一旦省掉核实步骤、损失会最惨重的说法。这条曲线跟喜不喜欢、信不信任那个干活的助手无关,而是关于每一次都让核实的深度匹配上犯错的代价,不管报告听起来多么自信。
在这一切开始的那个晚上,我还完全没有语言去描述这些。我手头有一摞要处理的任务卡,有一个助手,看起来它一晚上能干的活,很可能比我自己同样时间里能干的还多,还有一条规则——还算不上习惯,只是我碰巧加进去的一条规则——大意是:在告诉我某件事完成之前,先对照真实系统去检查它是不是真的完成了,而不是对照任务卡上写的内容。正是这一条规则,才让这一期接下来的内容有故事可讲。
那份本该早就存在的文档
我用任务卡来追踪这个项目的工作,我猜大多数人管理一份不断变长的待办清单时也是这么做的。其中有一张卡,对应的是一份日常操作流程文档,卡上几天前留的备注大意是:"已完成——和 README 一起创建的。"简单明了,看不出任何问题。要是我自己去看一眼,多半就直接信了,然后转去处理下一张卡了。

但事情没有这样发展,因为那天晚上发出去的指令不是"写这份文档",而更接近"写这份文档,但在动笔之前,先查一下之前那条备注是不是真的属实。"回来的反馈里有一件我没料到的事:那条备注是错的。从来没有任何一个 commit 添加过那个文件。主分支上没有,工作分支上没有,整个历史记录里哪儿都找不到。是某个人——几天前的某个"我",正急着往下过一长串任务卡——把它标成了完成,可能是因为感觉上已经完成了,也可能是在什么地方起草过、但从来没变成真正的 commit,又或者单纯是因为卡上写着"完成",而没有人回头去核实过。
同一天晚上,同样的事情又在第二份文档上发生了一次——一份本该逐个角色说明系统实际能做什么的功能参考指南。模式一模一样:任务卡上宣称已经有了初稿,还引用了之前的一次评审会议,仿佛那次会议真的产出过一个已提交的文件。但并没有。没有文件,没有 commit。说法和现实在某个时刻悄悄脱节了,而在有人明确要求"先核实再相信任务卡"之前,整个流程里没有任何环节察觉到这一点。
我想在这里公道地说清楚,这件事里真正该记的功劳是什么,因为很容易把它讲成"我搭了一个能自己发现错误的系统",但那也不太诚实。实际发生的是:我给出的指令里包含了一个明确的步骤——对照真实的代码和真实的 git 历史去核实,不要只相信任务卡上写的——正是这一步,才是这个差错最终能浮出水面的唯一原因。把这条指令拿掉,这两份文档很可能会一直无限期地被标记为"已完成",因为当时工作流程里的其他任何环节,都不是设计来发现这种问题的。
修正之中的修正
同一晚的工作里还埋着一个更小的瞬间,我觉得它比这条上了标题的差错更重要,因为它没那么戏剧化,却更能代表这种开发方式在日常里真实的样子。
在整理功能参考指南的过程中,第一版对某个角色权限的描述——一位初级团队成员应该能看到、能编辑什么,不应该能看到、编辑什么——是凭记忆写的。结果出了一个小而具体的错误:本该显示为"允许"的一项权限,被写成了"不适用",因为当时感觉这样写是对的,而没有真的去查证。在这份草稿被提交之前,第二轮检查回去核对了代码里定义这些权限的实际配置文件,逐行跟刚写好的内容做了比对,纠正了不一致的地方。
没有人要求做这第二轮检查。也没有任何指令说"提交之前再对照权威来源,把自己刚写的东西核对一遍。"它之所以发生,是因为当时在用的标准——对照真实情况去核实,而不是凭感觉——被一以贯之地应用了,包括应用到同一个会话里刚刚产出的工作上。这是个很小的细节,我不想把它吹成比它本身更大的说法。但这恰恰是"一个能写出听起来靠谱的文档的助手"和"一个能在自己听起来靠谱的错误上线之前先抓住它的助手"之间的差别,而正是这个差别,让我在那天晚上之后,把更多而不是更少的工作交出去。
我到底学会了信任什么,又没学会信任什么
我想把这条教训的形状讲清楚,因为"我学会了在睡觉的时候信任 AI 帮我写代码"这句话,比实际发生的事情干净得多——但干净不等于真实。
我学会信任的,是一件非常具体、非常窄的事情:当我给出明确指令,要求对照代码真实、当下的状态和真实的 git 历史去核实一个说法——而不是对照任务卡上写的,也不是对照凭印象记得的内容——这项核实真的会发生,而且一旦存在真实的差错,它就会被揭示出来。这跟默认信任输出结果不是一回事,而是信任一个流程,前提是这个流程每一次都真正包含一次实际的核实,而不只是在感觉可疑的时候才做。
那天晚上我没有学到的一件事,而且还要再过一阵子才会学到,是当那道核实缺失时会发生什么——当一份报告回来说某件事已经完成、已经核实过、或者已经通过,而没有人想过要在相信它之前先加上"去对照真实记录确认一下"这一步。这是跟这一期不同的一种失败模式,而且更危险,因为一个单纯错误的说法,从外部看,跟一个真正正确的说法一模一样,直到有人去查为止。后来我遇到过这个同类问题里一个严重得多的版本,赌注也比一份缺失的操作流程文档高得多——有一次,一个助手报告的结果,被它自己的 commit 历史悄悄反驳了,而我差点就没注意到,直接把它传递了下去。那是这本日记后面某一期的故事,不是这一期。但这里的问题跟那次同构,只是少了这道安全网。
眼下,我那一周真正带走的教训,比"AI 值得信任"要窄一些,我觉得也更经得起时间考验:信任必须像对待任何一个新的合作者那样去建立——不管是人还是别的什么——按证据的多少去赢得,每次证据都站得住脚,就再往前延伸一点点,而绝不能仅仅因为回来的那句话听起来很有把握,就默认它是对的。一句自信的话和一句真实的话,读起来一模一样,直到你养成了核实的习惯。在这一期里,我还没有把这个习惯完全建立起来。我只是刚刚意外地发现,自己有多么需要它。

这是 U-PRO Build Journal(产品构建日记)的第 4 期。第 3 期讲的是第一个为一个我原本没打算优先考虑的场景而做出来的功能。第 5 期将讲述:一次自动化安全审查告诉我,我的数据库里存在真实的、可被利用的漏洞——然后第二天,它又告诉我一遍,说第一次其实也没修完。