Archived Entry

mp.weixin.qq.com success

我为什么逼自己一行代码都不看

Open original

今天晚上我跟徐文浩、任鑫一起聊了一个话题:代码治理。下面是这场对话里我的核心观点。

最近我拿 Claude Code 密集地没日没夜地写,尤其是写 VoiceDrop 的时候,每天工作时间大概到了 12 个小时。前面一个月平均每天 9.5 小时,现在每周已经是八十几个小时。

在这个过程里,我有一个越来越强烈的感觉:现在无论是 Python 还是 JavaScript 还是 Swift,这些语言在整个层次上已经沦为汇编那一层了。上面还有一层东西——不仅仅是自然语言,还包括一堆 harness、一堆 check,各种各样的栅栏,这一坨东西去形成下面那一坨东西。

你不会去改编译器编出来的汇编

我以前的立场其实正相反。可能在我们公司所有工程师里面,我是对代码质量、代码品味要求最高的一个:核心代码一定要手搓 20 遍到 50 遍,一行不能多。我以前一直逼着大家一遍一遍重构代码。

但最近我一下就跳到另外一个阵营去了。

打个比方:一旦有了像 C、C++ 一样的高级语言,直接去手写汇编,第一没有必要,第二没有可能。而去改编译器编出来的汇编语言,更是完全没有意义。你把一个 mov 从上面挪到下面,下一次编译不就又没了吗?如果你从头到尾都是手写的汇编,比如写网络的 kernel 那一点点东西,我认。但如果是编译器编出来的,你去改它干嘛?你干嘛不改编译器,不改上面的高层代码?

所以在这一层上,我的观点是:不需要去做修改。你要治理,就在上一层治理——也就是编程的自然语言那一层。你 C 语言写好一点,编译出来的汇编也好看点;你 C 语言不写 for 循环,一直 print(1) 到 print(100),那汇编编出来也是更屎的。从上一层可以影响、可以治理下一层,但不要在下一层去搞任何东西。

我现在的两条死规矩

第一,我现在不允许自己看任何一行代码,绝对不允许。写 VoiceDrop 这个 APP 的过程中,我打开过几次 Xcode,也就两三次,都是去看一个字符串、看 prompt 的文字怎么写的。除此以外没打开过,甚至几乎没在本机编译过。我如果想看任何代码,做法就是问 Claude Code:你把那个逻辑给我讲一下。到现在为止,我发现可行。

第二,一定不用自然语言来控制——因为自然语言不准确——但一定要用 loop 来控制。让它转圈,一遍一遍转,然后把结果定义好。

我很喜欢 Superpowers 里那个 TDD 的 skill。现在我这个小 APP 每一次更改都要过 414 个 test case,做任何更改都得 pass。老实说,我以前自己写这种小规模的软件,从没写过 400 多个 test case。

但我会比较小心,不要过早地把东西变成代码。举个例子:你到底要存的是那套自然语言的东西,还是把它形成一套 Python 或 JavaScript 代码然后去跑?我上个月写 VoiceDrop 之前,比较喜欢说什么东西结束了就都给我沉淀成一套系统。但后来发现两个问题:第一,这种代码的质量还没有大语言模型好,反而有时候会出 bug;第二,自然语言那部分是在一个 loop 里一遍一遍转,可能要转十几分钟。昨天我做一个 feature 从下午 2 点做到 7 点,转了 5 个小时,一圈一圈转,fix fix fix。

这整个过程因为是自然语言的、在 harness 里的,所以很灵活。如果换成我自己编译出来的东西,可能最多 3 分钟就搞定了——因为它是写死的。但它的准确度和最终 quality,跟用 harness 转 5 个小时出来的东西,是天壤之别。

It works 就是那根金线

VoiceDrop 我现在装的是第 148 版,在过去十二天里发了 148 个版,其中发布中断的大概五六个,从这个角度来说,代码的质量还是可以接受的。

我们这帮老程序员为什么那么在乎代码风格、结构清晰、软件工程那些原则?因为那是基于所有代码都由人脑管理、由人去读的。你要求整洁,要有正确的空格、回车、要写注释,这些都是为人读的。可如果把所有空格和回车全去掉,人就没法管理了,但大语言模型其实不那么在乎。

所以问题就来了:我们对代码的关心里,到底有多少仅仅是为了人看着可理解、可维护?这个标尺全是人。机器的标准一定不一样。那新标准是什么?我的新金线就是:If it works,就不要去管它。

如果我们静下心把任何一个大型软件那几千万行汇编打印出来,认真读一读,里面一定是疯了一样地重复、疯了一样地读不懂。那对我们是问题吗?不是。只要还有 C 的源代码,下面再混乱都没关系。同样的道理,只要在上一层能控制它、改变它、比较一致地出结果,你管它下面写什么呢?唯一我同意要管的,就是你在上面改已经没法得到你要的结果了这个要管。而它只要从外面看是可以运行的,就不要管。

这不是 what 的分歧,是 how 的分歧

我们这场讨论核心的分歧:不是在 what 上,是在 how 上。

首先,把整个系统做得更快更好,这是所有人的目标。在这一层上我们没有分歧。

真正的问题是怎么达到。我画一条线,以「看不看代码」为界:线以下叫水面以下,线以上叫水面以上。冲突点是:精力到底应该放哪边?

这不是零和一,是个比例。分歧在于:我认为应该把 95% 以上的时间花在代码层以上——也就是如何生成代码这一层,最多 5%,最好 1%,甚至未来某天到 0%。即便是 30% 和 5% 的差别,我觉得也是巨大的观点差异。因为在这一层花多长时间,有时候是质的变化。

治理我同意,但别在下一层治理

我同意治理,分歧只在怎么治理。

对于代码问题,没经验的工程师常犯的无外乎这几条:代码重复(反复粘贴复制),不封装、分层不坚决,耦合度太高、接口不清晰等等。有经验的人能列出一二十条原则。但我觉得每一次都在代码层去解决不是釜底抽薪。而且不见得每次都要做,而是每隔一段时间——比如一个月——重构一遍。你如果从没出现重复代码,反而说明刚开始一定 over design了;一定是出现两三次以后,你才意识到该合并。

这些事要在更高一个 level 上做。我发现这正是 Superpower 该加功能的地方——它没有从这个维度看代码的能力。于是我自己做了一个 quality 的 skill,里面有一个专门管 duplication,定期跑一跑。

现状之争:该不该选一条面向未来的路

趋势上我们是一致的:如果现在还需要看一点代码,未来一定越来越不需要。三年前,不看代码这条路根本走不通——那时模型能力、Claude Code、harness 全没有,你拎着当时的 ChatGPT 想这么干必死。从三年前到现在发生了什么,我们都看到了。再预测未来五年,我有十足的信心说:这个趋势只会更强。

所以我们讨论的不是趋势问题,是现状问题:今天这个点该怎么做?这就像 Type-C 刚出来时苹果的 Mac 要不要取消 USB-A,像最早要不要取消软盘和光盘。

我的选择是逼迫自己。注意我说的是「逼迫」——因为现实里肯定有更轻松的路。当你在做一个巨大浪潮的转移时,必须逼自己干一些事,这不见得是最优解,但一定要这么逼才能真转过来。就像学自行车,一遍遍摔跤,别人都走很远了,我还在地上摔。

这里有个前提:不着急。磨刀不误砍柴工——如果明天就要上线、未来的方法今天出不来,那看一眼代码也未尝不可,现实问题不解决就去面向未来是不合理的。但只要我有选择,我现在的选择就是:能不看代码就不看,能在上层解决就在上层解决,能用 loop、用 agentic 的方法就用上层解决。

这条路确实有点难搞,搞出来的效果也不见得完美。但我坚信一件事:花时间花在这上面是值得的;而看代码,哪怕看再少时间的代码,在今天这个年代都是浪费时间。