317 of 584

让我们谈谈 LLM

似乎每个人都同意我们正处于某种变革的浪潮之中,但具体是什么,似乎还有待争论。它可能是一场史无前例的生产力和能力革命,甚至可能是技术“奇点”的前兆,一旦跨越这个奇点,我们将无法想象未来的世界会是什么样子。它也可能只是另一个终将随风而逝的“雾件(vaporware)”炒作周期。它还可能是像互联网泡沫(dot-com bubble)那样的存在:会导致一场大崩盘,但依然会给我们留下一些有用的东西(就像互联网泡沫推动了 Web 的大规模普及一样)。当然,它也可能什么都不是。

关于这些立场的各种变体,人们已经写了成千上万字来争论。所以,今天我当然也要再抛出几千字来凑凑热闹,毕竟博客就是用来干这个的。至少你在这里读到的每一个字都是我亲自写的(哪怕我死了,你们也别想从我冰冷僵硬的手里抢走我的破折号)。

术语界定与选定赛道 (Terminology, and picking a lane)

但首先,快速说明几点:

在这篇文章中,我将几乎独占性地使用“LLM(大语言模型)”和“LLMs”这两个词,因为我认为这种精确性很有用。“AI”是一个模糊且含义过载的术语,人们太容易陷入对“AI”到底指什么的诡辩和争论中。实际上,目前关于编程和“AI”的所有争议,几乎都可以具体追溯到大型语言模型的出现。我想,如果用“GPT”来代替,精确度可能会稍微高一点,但 OpenAI 一直试图将其据为己有,这是另一种不受欢迎的包袱。所以,就用“LLMs”吧。

当我谈到“LLM 编程(LLM coding)”时,我指的是利用 LLM 生成某种编程语言的代码。我将此作为所有此类用法的统称,无论是否在人类监督下进行,无论它是否作为代码的唯一生产者(完全没有人类生成的代码)等。

我也将尽量把我这里的评论限制在与技术和作为一种职业的编程直接相关的事情上,因为这是我所了解的(我有哲学学位,所以我其实有资格评论 LLMs 的其他一些方面,但在这篇文章中我刻意避开了它们,因为我觉得很多那种争论既乏味又带有“大二学生式的幼稚(sophomoric)”——字面意思,让我想起了我大二时读过和讨论过的东西)。

如果你在其他领域使用 LLM,那么,我可能对那个领域了解不够,无法给出有用的评论。在看到一些没有遵循这一原则的人发表的真正“暴论”后,我好几次觉得,我们真的需要一个将“LLM”和“盖尔曼失忆症(Gell-Mann Amnesia)”结合起来的可爱合成词,来描述这样一种现象:在许多与 LLM 相关的讨论中,人们似乎期望 LLM 接管除了他们自己领域之外的所有工作和领域。

没有银弹 (No silver bullet)

几年前,我写过一篇关于 Fred Brooks 的《没有银弹》(No Silver Bullet)的文章,并说我认为这可能是 Brooks 写过的最好的东西。如果你从未读过《没有银弹》,我强烈建议你去读一读,而且我建议你亲自通读全文(而不是仅仅看个摘要)。

《没有银弹》发表于计算硬件以惊人速度发展的时期,但我们构建软件的能力却远远跟不上。因此,Brooks 对软件做出了一个大胆的预测:

在十年内,无论是在技术还是管理技巧上,没有任何单一的发展能够保证在生产力、可靠性或简洁性上取得哪怕是一个数量级的提升。

为了支持这一点,他研究了软件开发中的困难来源,并将它们分为两大类(粗体与原文一致):

遵循亚里士多德的观点,我将它们分为 本质(essence)——软件特性中固有的困难——和 偶然/附属(accidents)——那些如今伴随其生产过程但并非固有的困难。

一个经典的例子是内存管理:有些编程语言需要程序员手动分配、跟踪和释放内存,这是一个困难的来源。但这是一种偶然的困难,因为没有什么是本质上要求这样做的;很多其他编程语言都有自动内存管理。

但其他的困难来源则不同,它们似乎是软件开发本身固有的。以下是 Brooks 的一种总结方式(粗体与我手头那本《没有银弹》中的一致):

软件实体的本质是一个由相互交织的概念构成的结构:数据集、数据项之间的关系、算法以及函数的调用。这种本质是抽象的,因为概念结构在许多不同的表示形式下都是相同的。尽管如此,它依然是高度精确且细节丰富的。

我认为构建软件最困难的部分在于这种概念结构的规范、设计和测试,而不在于表达它以及测试这种表达的保真度。 诚然,我们仍然会犯语法错误;但与大多数系统中的概念性错误相比,它们只是皮毛。

如果这是真的,那么构建软件将永远是一件困难的事情。本质上没有银弹。

为了进一步阐明观点,他还解释了仅仅解决偶然困难所带来的边际收益递减:

现在软件工程师所做的工作中,有多少还是致力于解决偶然性问题,而不是本质性问题的?除非这个比例超过所有努力的 9/10,否则即使将所有偶然活动的时间缩减到零,也不会带来一个数量级的提升。

这是一个直截了当的数学论证。如果它的两个经验前提——偶然/本质的区分是真实的,且今天遗留的偶然困难不占总工作量的 90% 以上——成立,那么排除通过减少偶然困难来获得数量级提升的结论便自动成立。

我认为大多数程序员都相信第一个前提,至少是隐性地相信。一旦接受了第一个前提,就很难反驳第二个前提。事实上,我个人的看法甚至超过了 Brooks 论证所需的最低标准。只要偶然困难没有达到 90%+ 的门槛,他的数学推论就站得住脚,因为低于这个数字,消除偶然困难就不可能带来 10 倍的提升。但我怀疑,在今天,偶然困难在总工作量中所占的比例远远低于这个数字。在很多成熟的编程领域,如果彻底消除所有剩余的偶然困难还能带来生产力翻倍,我都会感到惊讶。

《没有银弹》中也有一节讨论了针对“AI”的“银弹希望”,尽管 Brooks 所认为的“AI”(书中有一段偏题去澄清这个词的确切含义)与今天所推崇的“AI”有很大不同。在《没有银弹》中,与 LLMs 最贴切的比较实际上不是对“AI”的讨论,而是对自动编程(automatic programming)的讨论。这个词多年来有很多不同的含义,但 Brooks 当时将其定义为“根据问题规范的陈述生成用于解决该问题的程序”。这几乎就是目前向程序员推销 LLMs 时所宣称的那个核心任务。

但 Brooks 在这个话题上引用了 David Parnas 的话:“自动编程一直是一种委婉的说法,它指的是使用一种比程序员目前能用的语言更高级的语言来编程。” 而 Brooks 并不认为单靠更高级的语言就能成为银弹。正如他在讨论 Ada 语言时所说:

毕竟,它只是另一种高级语言,此类语言带来的最大回报来自第一次飞跃,即从机器的偶然复杂性中解脱出来,进入针对逐步解决问题的更抽象的陈述。一旦这些偶然性被移除,剩下的偶然性就会变小,从移除它们中获得的回报肯定也会更少。

许多人目前将 LLMs 宣传为软件开发的一大革命性进步,但几乎完全是基于关于 LLMs 高速生成代码能力的论断。《没有银弹》的论证对这些说法构成了挑战,因为它设定了我们仅仅通过更快地生成代码所能获得的收益的上限。

在《人月神话》(The Mythical Man-Month)第 2 章中,Brooks 提出了一条日程安排准则:在一个“软件任务”上,六分之五(83%)的时间将花费在非编码的事情上。这给仅仅通过加速编码所能带来的生产力提升设定了一个相当低的上限。即使我们假设 LLMs 将编码时间缩短为零,并采用更宽松的《没有银弹》公式(仅仅预测单一发展无法带来数量级的提升),这依然低于 Brooks 本人认为的通过雇佣优秀的人类程序员所能带来的收益。来自《人月神话》第 3 章:

编程管理者早就认识到,优秀的程序员和差劲的程序员之间存在巨大的生产力差异。但实际测量出来的幅度让我们所有人都感到震惊。在 Sackman、Erikson 和 Grant 的一项研究中,他们测量了一组经验丰富的程序员的表现。仅仅在这个群体内,最佳表现和最差表现之间的比例,在生产力测量上平均约为 10:1,在程序速度和空间测量上更是达到了惊人的 5:1!

(尽管我个人对“10倍程序员”的概念持怀疑态度,但整个软件行业似乎都接受它是真实的)

说个轶事:在我作为职业程序员的职业生涯中,我所做的大部分工作是构建由数据库支持的 Web 应用程序和服务,我并没有看到 LLMs 带来多大的提升。我猜想,如果你不熟悉这个编程领域,看着只要根据你想要处理的数据的描述,就能自动生成整个应用程序的骨架和基本的增/查/改/删 HTTP 处理程序,可能会觉得非常令人印象深刻。但这种能力早于 LLMs 就存在了:例如,Rails 的脚手架在二十年前就能做到。

而且,不仅仅是原始的代码生成,我们可以使用的抽象概念也已经发展到了一个地步——我基本上从不觉得生成代码的原始速度阻碍了我。正如 Fred Brooks 所预见的那样,我的大部分时间花在了别处:与想要新软件(或想要修改现有软件)的人交谈;找出他们想要什么、需要什么;提出最初的规范;将其分解成大小合适的部分供程序员(可能是我,也可能是其他人)开发;测试第一个原型并获取反馈;准备下一次迭代;审查代码或请求审查等。我个人没有追踪过这是否符合 Brooks 六分之五的估计,但如果真的吻合,我一点也不会感到惊讶。

鉴于这一切,仅仅拥有一个能比我手写代码速度更快的 LLM,并不能给我带来数量级的改进,甚至差得很远。或者正如 Tailscale 首席执行官最近在一篇广受欢迎的博客文章中所说的那样:

AI 对这个问题的直接影响微乎其微。好吧,Claude 能在 3 分钟内写完代码而不是 30 分钟?太棒了,Claude,干得漂亮。

现在你要么自己花 27 分钟在一个循环的来回中与 AI 审查这些代码(这其实还挺好玩的);要么你省下这 27 分钟,把未经核实的代码提交给代码审查员,审查员还是像以前一样要花 5 个小时,但他现在会很生气,因为你让他阅读你自己都懒得看的代码垃圾(slop)。在这个过程中,几乎没有产生什么价值。

更简单地说:在审查队列消化速度与以前相同的情况下,向审查队列中丢入更多的补丁,并不是提高速度的秘诀。真正的软件开发不仅涉及审查队列,还涉及我上面列出的所有其他步骤和流程,甚至更多。让 LLM 更快地生成代码,并不能提高所有其他环节的速度或容量。

因此,作为接受 Brooks 在《没有银弹》中论点的人,我从理论立场上坚信,LLMs 不可能在“生产力、可靠性或简洁性上提供哪怕是一个数量级的提升”。我自己的经验也与这一预测相符。

实践未必出真知 (Practice makes (im)perfect)

理论说得够多了。那么 LLM 编程在经验上的实际情况如何呢?

每个支持使用 LLM 编程的粉丝都有关于其革命性特质的轶事,但我们拥有的非轶事数据点却复杂得多。例如,我已经好几次被甩链接并被要求阅读 DORA 关于“AI 辅助软件开发状态”的报告。起初,它确实似乎在宣布 LLMs 的影响已经尘埃落定,优势全在 LLMs 这边。从其执行摘要(第 3 页)来看:

技术领导者面临的核心问题不再是是否应该采用 AI,而是如何实现其价值。

在其他地方,它还做出了诸如(第 34 页)“AI 是软件开发的新常态”这样的断言。

但是,回到执行摘要,事情听起来就不那么一致乐观了:

研究揭示了一个关键的事实:AI 在软件开发中的主要作用是一个放大器(amplifier)。它放大了高绩效组织的优势,同时也放大了陷入困境的组织的功能障碍。

然后(依然在第 3 页):

AI 投资的最大回报并不来自工具本身,而是来自对底层组织系统的战略聚焦:内部平台的质量、工作流的清晰度以及团队的对齐。如果没有这个基础,AI 只能创造局部的生产力提升,而这些提升往往会在下游的混乱中流失。

继续看第 4 页:

AI 的采用现在提高了软件交付的吞吐量,这是与去年相比的一个关键转变。然而,它仍然增加了交付的不稳定性。这表明,虽然团队正在适应速度的提升,但其底层系统尚未演进到能够安全管理 AI 加速开发的程度。

“交付不稳定性”通过两个因素来定义(第 13 页):

  • 变更失败率(Change fail rate):“部署后需要立即干预的部署所占的比例。”
  • 返工率(Rework rate):“由于生产环境中的事件而发生的计划外部署所占的比例。”

报告的后续部分对这方面进行了更详细的说明。例如,第 38 页的图表显示了交付不稳定性的增加。在包含该图表的部分中,还讨论了吞吐量的增加(DORA 将其定义为变更前置时间、部署频率和失败部署恢复时间的组合)是否足以抵消或弥补这种不稳定性的增加(第 41 页,重点由我添加):

有些人可能会争辩说,不稳定性是 AI 辅助开发所带来的开发吞吐量增益的一个可接受的权衡。

其理由是,AI 辅助交付的数量和速度可以削弱不稳定性的有害影响,或许可以通过实现极其快速的错误修复和更新,从而将对最终用户的负面影响降至最低。

然而,当我们超越纯粹的软件交付指标来看时,这种论点站不住脚。为了评估这一主张,我们检查了 AI 的采用是否会减弱不稳定性对我们那些历来受不稳定性伤害的成果的损害。

我们没有发现这种缓解作用的证据。相反,不稳定性对产品性能和倦怠等关键结果仍然具有显著的有害影响,这最终可能会抵消感知到的任何吞吐量增益。

无论如何,第 38 页的图表似乎显示,不稳定性的增加幅度相当大于吞吐量的增加幅度。

奇怪的是,该图表还声称“代码质量”显著提高,报告的其他部分(例如第 30 页)也声称“生产力”显著提高,同时交付不稳定性也显著增加——这看起来像是一个矛盾。据我所知,DORA 对于“生产力”和“代码质量”的数据来源是受访者自我报告的主观感知影响。其他研究和报告已经设计出不那么主观、更具定量性的方法来衡量这些东西。例如,这项被广泛讨论的关于采用 Cursor LLM 编码工具的研究 使用了代码的静态分析结果来衡量质量和复杂性。尤其是自我报告的生产力影响,应该是一个极具怀疑性的指标。以 METR 2025 年初的研究 为例(重点由我添加):

这种感知与现实之间的差距是惊人的:开发人员预期 AI 会使他们的速度提高 24%,甚至在经历了速度变慢之后,他们仍然认为 AI 使他们的速度提高了 20%。

LLM 编程的倡导者经常批评这项特定研究得出“开发速度变慢”的结论,理由是它基于较老一代的 LLMs(稍后会详细讨论这种说法),但据我所知,没有人能够有力地反驳“开发人员在自我评估生产力方面非常不准确”这一发现。因此,看到 DORA 依赖自我评估的生产力令人感到失望。

DORA 报告接着为组织提供了一个由七部分组成的“AI 能力模型”(从第 49 页开始),其中包含的建议包括:强大的版本控制实践、小批量工作、高质量的内部平台、以用户为中心的焦点……所有这些感觉都应该是一个成功组织的“准入筹码(table stakes)”,无论他们是否碰巧使用了 LLMs。

举个可笑的例子:假设有人告诉你一项新技术正在彻底改变外科手术,但收益分布不均,最好的整体结果出现在这样的手术团队中——除了使用新设备外,团队成员还在手术前洗手。这并不像听起来那么极端:在 DORA 报告以及许多其他类似的白皮书、报告和研究中,建议用于最大化 LLM 相关收益的各种实践,其实就相当于(或应该是)软件开发领域中如同外科手术洗手一样基础的要求。乔尔测试(The Joel Test)在四分之一个世纪前就推荐了其中相当多的实践,敏捷宣言也暗示了其中几个。即使在那时,它们也算不上新颖;如果你深入研究有效软件开发的文献,你可以找到 DORA 建议的各种变体,其历史可以追溯到 20 世纪 70 年代甚至更早。

看一个最近的数据点:我看到很多人讨论并发送给我 CircleCI 的 2026 年“软件交付状态”报告。与 DORA 报告一样,它声称采用 LLM 带来的好处分布不均,甚至指出(第 8 页)“大多数团队的整体吞吐量几乎没有或完全没有增加”。CircleCI 报告还提出了一个令人担忧的观点,这与 DORA 报告中出现的“交付不稳定性”增加相呼应(CircleCI 执行摘要,第 3 页):

关键稳定性指标表明,人工智能驱动的变更更频繁地导致破坏,且团队修复问题的时间更长,使得验证和集成成为主要的瓶颈。

CircleCI 进一步报告(第 11 页)称,与去年同期相比,他们发现修复主分支(main branch)损坏的恢复时间增加了 13%,特性分支(feature branches)的恢复时间增加了 25%。并且(第 12 页)他们还表示失败率正在增加:

主分支的成功率降至 5 年来的最低水平,为 70.8%。换句话说,尝试将变更合并到生产代码库的行动现在有 30% 的概率会失败。

作为对比,他们自己推荐的主分支成功率基准是 90%。

这些不断增加的失败及其不断增加的解决时间所带来的成本被量化了(粗体与报告一致,第 14 页):

对于一个每天向主分支推送 5 次变更的团队来说,成功率从 90% 降至 70%,意味着从每两天发生一次导致停工的损坏,变成每天发生 1.5 次(增加了 3 倍)。

按照每次故障仅 60 分钟的恢复时间计算,你每年要面临 额外 250 小时的调试和被阻塞的部署。这还只是在相对适中的规模下。对于每天推送 500 次变更的团队来说,这相当于损失了 12 名全职工程师的工作量。

对这些报告的常见回应是,声称它们是基于使用旧版 LLMs 的用户得出的,而现在出来的模型才是真正具有革命性的,不会有任何这些问题。例如,这就是针对我上面提到的 METR 研究的主要反驳观点。但这种说法从一开始就站不住脚(因为它很少附带证实该主张所需的证据),而且其反复使用反而自毁信誉:如果那些声称“这一次绝对是改变世界的革命性飞跃”的人,在之前所有这么说的时候都错了(他们必然错了,因为如果之前的任何一次真的是革命性飞跃,他们就不需要说这一次才是了),那为什么现在有人应该相信他们呢?

另外,我阅读了大量关于 LLM 编程的研究和报告,这些发现——影响不均或不一致、质量/稳定性下降等——似乎非常稳定。跨越大量团队、使用各种不同模型及其不同版本、在一段较长的时间内,结果都是如此(DORA 在“代码质量”增加而“交付不稳定性”增加更多这一矛盾主张上确实有点混乱,但如上所述,这似乎是一个方法论问题)。我在本文中大量引用的两份报告(DORA 和 CircleCI 报告)是被特意挑选出来的,因为它们经常被 LLM 编程的倡导者推荐给我,并且在立场上似乎相当支持 LLM。

对这些发现的另一种预期回应是声称:过时的不一定是旧的模型,而是旧的工作流被淘汰了;目前最先进的技术不再仅仅是提示 LLM 并直接接受其输出,而是涉及一个 LLM(或基于 LLM 的智能体)生成代码,而一层或多层“对抗性(adversarial)”的 LLMs 审查并修复代码,同时还要互相审查对方的评论、响应和修复。这样就引入了一种机制,使 LLM(s) 能够自动提高输出的质量。

我尚未看到对这些方法进行严谨研究的报告,但几个广为宣传的早期案例并没有带来信心。我在这里拿 Cloudflare 举例,因为他们一直是倡导以这种方式使用 LLMs 的代表。在他们利用 LLM 重建 Next.js的项目中:

我们也为代码审查接入了 AI 智能体。当一个 PR 被打开时,一个智能体会审查它。当审查意见返回时,另一个智能体会处理这些意见。反馈循环大部分是自动化的。

但他们公开发布的版本,经过了这一流程,并且显然还在这之上叠加了一定程度的人工审查后,最初甚至无法运行最基本的默认 Next.js 应用程序,而且显然漏洞百出。来自一篇漏洞披露文章(重点由我添加):

AI 现在非常擅长让系统达到一种看起来很完整的状态。

引用的一个具体问题是,基于 LLM 的重构根本没有拉取所有原始的测试,因此可能会漏掉这些测试正在检查的涉及安全的关键边界情况。来自同一篇漏洞披露文章:

这个过程是功能优先(feature-first)的:决定 viNext 中存在哪些功能,然后移植相应的 Next.js 测试。这是一种快速推进的合理方式。它能为你提供广泛的理想路径(happy-path)覆盖。

但它不能保证你能把成熟框架多年积累下来的那些丑陋的回归测试、缺失导出的用例以及开放式失效行为检查(fail-open behavior checks)也带过来。

因此,中间件可能看起来被“覆盖”了,而能够证明其安全失败的那个关键测试却从未被迁移过来。

例如,Next.js 有一个专门的测试目录(test/e2e/app-dir/proxy-missing-export/),用于验证当中间件文件缺少必要的导出时会发生什么。该测试从未被移植,因为中间件已经被其他测试认为已被“覆盖”。

总的来说,那篇文章算是比较乐观的。但考虑到 Next.js 的重建是由想必知识渊博的人进行的,他们想必遵循了良好的现代实践,并提示了优秀的现代 LLMs 来执行一项 LLMs 本应极其擅长的任务——一种在训练数据中充分体现、文档完善的语言和框架,带有一个大型的、用目标语言编写的现有测试套件来辅助自动化验证——我很难保持那种乐观。

虽然我个人没有通读最近据称泄露的 Claude Code 源码,但我阅读了一些看过源码的人的评论和分析,同样似乎表明,一个本来最应该能够充分利用 LLM 编程所谓革命性能力的团队,并没有做到这一点。

因此,在这里的研究、报告和最近的公共案例中,一致的主题是:即使在 2026 年拥有现代 LLMs 和现代实践,能够比以前快得多的速度生成代码,也仍然无法保证能够以比以前快得多的速度交付软件。正如 CircleCI 报告所指出的那样(第 3 页):

数据指向了一个清晰的结论:在 AI 时代,成功不再取决于代码编写的速度有多快。决定性的因素是大规模验证、集成和恢复的能力。

如果这听起来像是 Fred Brooks 过去常说的话,那是因为它本来就是 Fred Brooks 过去常说的话。生成代码的原始速度过去不是、现在也不是软件开发的瓶颈;加速它,甚至将生成代码的时间有效降低至零,也不会产生让软件开发的所有其他部分消失或变快的效果。

所以,此时此刻,对我来说已经很清楚了:在实践和理论上,LLM 编程都不能代表一颗银弹,而且它似乎极不可能在不久的将来转变成一颗银弹。

关于被时代抛弃 (On being left behind)

当对 LLM 编程表达怀疑时,一种常见的回应是:不采用它,或者即使只是稍微延迟采用,也会不可避免地导致被“时代抛弃”,甚至产生更强烈的影响(例如,我那些本应有更好判断力的熟人不止一次使用了“彻底毁灭/淘汰 (obliterated)”这样的词)。LLMs 是未来,不管你喜不喜欢,它都会发生,所以在为时已晚之前赶紧跟上时代的步伐吧!

我之前说过我在这里会坚持从技术角度探讨,但我顺便提一下,我经常遇到这种“不管你喜不喜欢,它都会发生”的话术设定。我发现它相当令人不安且令人反感,完全无助于改变我的想法。而像“不可否认的是……”这种温和一点的形式,在修辞上也是可疑的。举证责任应该落在那些声称 LLMs 确实具有革命性的人身上,但这种设定试图隐含地转移举证责任,是一个罕见的字面意义上的“乞题(begging the question)”谬误的例子:它把需要证明的结论(LLMs 实际上是革命性的)当作了既定前提。

同时,我看到了两种可能的结果:

  1. 怀疑论者的立场获胜。LLM 编程工具没有达到革命性的银弹地位。也许它们成为工具箱中的另一个工具,就像 TDD(测试驱动开发)或结对编程一样,有些人或公司非常喜欢它们。也许它们只是成为 IDE 的另一个功能,提供诸如生成样板代码以引导新项目的功能(如果你的首选库/框架本身没有提供引导工具的话)。
  2. 怀疑论者的立场失败。LLM 编程工具确实达到了真正的革命性银弹地位甚至更高(始终如一地在软件开发生产力方面提供一个或多个数量级的提升),并真正成为每个职业程序员工具和工作流中强制性的一部分,接管所有或几乎所有代码的生成。

在第一种情况下,延迟采用没有任何弊端,除非有人碰巧在决定强制使用 LLM 的公司工作。即使那样,如果他们不介意或者不想找新工作,他们也可以随时在那个时候把它捡起来。

至于第二种情况:基于我上面关于 LLMs 迄今为止的状态和前景的论证,我显然认为,继续延续迄今为止在模型和实践方面的渐进式进展,并没有提供任何通向银弹的可行路径。这意味着,一个真正的革命性突破必须与目前的最先进水平有足够大的差异,它将不可避免地使许多(甚至所有)以前基于 LLM 的工作流失效,就像它会使不基于 LLM 的工作流失效一样。

即使这没有导致一个完全“白纸”的、所有人平等的起跑点——即使对旧版 LLM 工作流的经验在“后银弹世界”中仍然是一种优势——我也不认为它会像人们通常假设的那样,是一种不可逾越的优势。首先,即使平均生产力有了极大的提高,可能也没有足够数量的、具备充分先验 LLM 经验的人来满足因此产生的极度膨胀的软件需求(这就是为什么许多领域的 LLM 倡导者花这么多时间谈论杰文斯悖论(Jevons paradox)的原因)。其次,任何真正的银弹突破都必须攻克并减少构建软件的本质困难,而不是偶然困难。让我们再次回到 Brooks 的话:

我认为构建软件最困难的部分在于这种概念结构的规范、设计和测试,而不在于表达它以及测试这种表达的保真度。

如今,人类 LLM 用户所需的许多技能正是这方面的能力:将软件作为一种“概念结构”进行规范和设计。尽管需要以特定方式将其放入 LLM 的上下文窗口(context window)中以便让它生成代码。但在任何真正的银弹世界中,大部分或所有的这种技能都将被淘汰,这就大大降低了在银弹最终实现时因延迟采用而受到的惩罚。

技术赋权于民?(Power to the people?)

除了对职业程序员和专业软件开发团队的影响外,另一个支持 LLM 编程的常见主张是它将使软件开发的门槛大众化(民主化)。有了 LLM 编程工具,即使不是经验丰富的专业程序员,也能开发出解决他们在日常工作和生活中面临的问题的软件。这绝对是一个巨大的社会效益,对吧?而且它也非常有趣!

撇开上面链接的那篇《纽约时报》文章是由一位本身就是经验丰富的专业人士撰写的事实不谈,我也并不相信这种用例。

在很大程度上,我认为这是一种“鱼与熊掌不可兼得”的情况。LLM 编程的倡导者似乎普遍同意,这是一项需要大量的理解、练习和经验才能产生稳定有用结果的技能(这也是上一节中讨论的“现在采用,否则就会被抛弃”这一说法的依据);拥有如何设计和构建优秀软件的扎实先验知识通常也是被推荐或预设的前提。但这与“软件民主化”的主张完全相悖:即一个没有任何编程知识或经验的人,只要拿起一个 LLM,用简单的非技术性自然语言让它构建一些东西,就能得到一个足够实用的结果。

我认为最可能的结果是,非技术用户将收到一个明显不符合使用目的的东西,因为他们不具备有效提示 LLM 所需的知识。他们不知道如何设置包含指令、技能定义和针对其问题的架构信息的 Markdown 文件目录。他们没有编写技术规范(无论是给其他人还是给 LLMs)的经验来详细描述他们想要什么。他们不知道如何设计和架构优秀的软件。他们不知道如何编排多个 LLMs 或基于 LLM 的智能体来对抗性地互相审查。简而言之,他们不具备任何据称对于成功使用 LLM 编程至关重要的技能。

还有一种可能性是,由于其固有的歧义性和缺乏精确性,“自然的”人类语言永远都不足以用于指定程序,即使是对更高级的 LLMs 或其他未来的“AI”系统也是如此。在这种情况下,用于指定程序的某种专用形式语言将永远是必要的。例如,Edsger W. Dijkstra 就持有这种立场,并以嘲笑他所谓的“‘自然语言编程’的愚蠢”而闻名,这篇文章值得一读,里面有一些经典的 Dijkstra 式格言,比如:

说到底,我们使用母语的“自然性”归根结底在于:我们可以轻易地用它们说出连篇废话,而其荒谬性却不明显。

非程序员使用 LLM 编程的另一个可能结果,是人们经常提到的 3D 打印的类比。3D 打印也曾被大肆炒作,被认为是让任何人都能设计和制造任何东西的伟大平民化工具,但它从未兑现这一承诺。在个人层面上,它成了少数愿意且能够投入时间、金钱和精力去达到中等熟练水平的爱好者的一个利基爱好。

但最噩梦般的结果是,非程序员 LLM 用户收到了一些看起来能工作的东西,直到很久以后才暴露其缺陷。考虑到我经常看到有人争辩说 LLMs 将普及编程,并为那些在隐私和机密性至关重要且受法律强制保护的领域工作的人编写实用程序,我对这种潜在的失效模式感到恐惧。我认为对于倡导采用 LLM 的人来说,可能发生的最糟糕的事情之一,就是新闻里充斥着这样的故事:好心办坏事的非技术人员,因为他们用 LLM 编写的辅助程序意外导致了数据泄露,或者哪怕“仅仅”是将一个具有微妙错误的财务模型释放到他们的业务中,从而毁了他们的生活。因此,即使我是 LLM 编程的倡导者,我也会对向非程序员推销它保持非常谨慎的态度。

但归根结底,LLMs 能够有意义地降低软件开发门槛的唯一情况是,它们成为了一颗真正的银弹——显著减少或消除了软件开发过程中的本质困难。而如上所述,LLM 的倡导者似乎认为,即使在有银弹存在的情况下,那些具备先验 LLM 使用技能的人和那些没有该技能的人之间仍然会存在鸿沟,以至于后者永远无法真正赶上。尽管我碰巧不同意这种信念,但事实依然是倡导者不能两头占理(XOR / 异或关系):要么 LLM 编程将成为掌握必要技能的人的专属俱乐部,要么它将成为伟大的平民化工具,从而消除对这些技能的需求。

核心收获 (Takeaways)

我在这篇文章中已经写了 6000 多字,虽然我还可以轻松地写出更多,但我大概应该做个总结了。

如果非要让我用一句话来概括我对 LLM 编程的立场,那就是“请去阅读《没有银弹》”。我认为 Brooks 在那里的论证在理论上是正确的,且被经验结果所验证;并且为 LLM 编程(或任何其他仅仅或主要解决偶然困难的工具或技术)所能产生的影响设定了相当严格的界限。

当然,我们所能做或获得的限制并不一定是世界末日。从《论可计算数》(On Computable Numbers)到莱斯定理(Rice’s theorem)以及更远的研究,计算机科学的许多基础理论都对我们能做的事情设定了不可逾越的限制,但我们依然在编写软件,并且我们依然在努力推动我们行业技术的进步。因此,《没有银弹》的论点并不等同于争辩 LLMs 必然是无用的,或者从中不可能获得任何收益。但它确实是一个论点,表明我们实际获得的任何收益都可能是增量式和进化式的,而不是许多人似乎期待的改变世界的革命。

相应地,我认为,目前缓慢或延迟采用 LLM 编程并没有巨大的弊端。很少有组织具备吸收即使是相对温和的、增量式生成的代码数量增加所需的强大基础(fundamentals),我怀疑这就是为什么如此多的研究和报告发现结果参差不齐且有大量被破坏的 CI 流水线的原因。不仅没有银弹,而且在没有首先打好基础的情况下,盲目冲刺采用 LLM 编程尤其不会带来快速或神奇的收益。事实上,我们拥有的证据表明,这样做更有可能损害而不是帮助你的生产力。

我也不认为 LLMs 会在近期内有意义地让编程大众化;即使它们成为程序员不可或缺的工具,在使用它们进行规范和提示时,可能仍需要用户“像程序员一样思考”。如果教更多的人如何严谨地思考和对抽象进行推理,我们将受益良多(他们自己也会受益良多),这比仅仅把他们原封不动地推到 LLMs 面前要好得多。

至于由于害怕被时代抛弃,你不应该急于采用 LLM 编程,那应该做什么:我认为你应该听听所有那些白皮书、报告和研究实际上在告诉你什么,并致力于打好基础。你应该采用并完善坚实的软件开发基础实践,如版本控制、全面的测试套件、持续集成、有意义的文档、快速反馈周期、迭代开发、关注用户、小批量工作……这些几十年来已被广为人知和证明,但在实际现实世界的软件商店中仍然极其罕见的做法。

如果怀疑论者的立场是错的,结果发现 LLMs 长期来看真的成为了不可或缺的编程工具,那么,现有文献表明,你目前的准备将使你能够最大限度地利用它们。如果结果发现它们并非如此,你依然会处于比之前更好的状态,并且你会比那些人拥有优势——那些人追逐着关于生产力巨大提升的狂热承诺,命令他们的团队只顾着消耗 Token 生成代码而不做基础建设,从而可能彻底毁掉了他们自己的开发流程。

或者正如 Fred Brooks 所说:

疾病管理的迈出的第一步,是用细菌学说取代恶魔学说和体液学说。正是这一步,这个希望的开端,本身粉碎了所有对神奇疗法的幻想。它告诉研究者们,进步将是循序渐进的,需要付出巨大的努力,并且必须对保持清洁的纪律给予持久的、不懈的关注。今天的软件工程亦是如此。