为什么我不进行氛围编程

4
分类佳文共赏
作者jacobharr
来源跳转
发表时间

内容

最近网上关于氛围式编码以及大型语言模型(LLM)将如何彻底改变软件开发领域的讨论很多。每一款新模型都会把我们带入纯粹生产力的境界,以思想的速度交付软件,消除产品开发中的一切摩擦和开销。大概就是这样。

也许吧。那我只能听你这么说了。我不搞氛围式编码。

如果这对你有用,那很好!这篇文章里我并不是想深入争论 LLM 的优缺点,只是它个人上从来没真正打动过我。这一页只是对各种原因的一个“简短”交代。

我是个吝啬鬼

我不是个纯粹主义者。我试过使用集成到 IDE 里的 LLM。它们在某些任务上确实很有用:这些任务足够简单,容易描述,但又烦人到不值得我亲自动手。比如,把一组方形图片缩小尺寸。我本可以去查 ImageMagick 的命令行参数,但让 AI 来做这件事正合适。后来我又试着让某个 AI 工具分析项目里的代码,再做几件小事,结果一切在尴尬中戛然而止。系统告诉我,额度已经用完了,如果我还想继续,就得提供信用卡来购买更多 token。

你得明白,我家两边都是由吝啬鬼组成的“长长谱系”。几个世纪以来,无论是在大西洋这边还是那边,我们一直都在精打细算、四处淘便宜货。举个例子,我的一位远房祖先在菲利普王战争中丧生,原因是他在撤离家园时把一些奶酪落下了,于是他离开堡垒去取回那些奶酪。所以你必须相信我:为了让我思考就要长期付费给某项服务,这个想法简直荒唐、可笑、又可怕到极点,我甚至懒得把卡给他们。我合上了笔记本电脑,卸载了 IDE,甚至又回去用Emacs了。然后我意识到,我竟然已经完全不再在意少了它这回事。

我老了

我确实年纪不小了,这也有帮助。我写代码已经很久了,尤其是在那种把有 5 年经验的开发者称作“资深工程师”的行业里。经验有时候是一剂对抗焦虑的良药(前提是它不是对一个只用 5 年经验就把开发者称作资深的行业里年龄歧视的焦虑),而 AI 热潮也让我想起了早年低代码和无代码工具的那些突破。我不怀疑 AI 能成为开发者的有用工具。我知道它在一些任务上能起作用,能让工具更好用一些。但这些论点总会让我再次想到意外复杂性和本质复杂性。

Fred Brooks 即使在我还是年轻程序员的时候也已经很老了。作为 IBM System/360 系列大型机(以及配套操作系统)的项目经理,他第一线见证了如今软件项目常见的各种出错方式,当年都还是新鲜事物。他把这些观察写进了《人月神话》一书,这本书至今仍应该是软件工程课程的必读书目。我的版本是较新的再版,其中还收录了后来的一篇文章《没有银弹》,Brooks 在那里讨论了新工具对开发者生产力的影响。要像程序员一样思考,你必须理解现实世界是复杂的。编程最好被看作是在我们混乱的现实之上强加简化的表征——我们称之为抽象——通过降低复杂性来让它变得可理解。这使我们能够把具体情境泛化为一层层可以叠加的结构。比如,把把花生酱抹到面包片上的具体动作,泛化成一个 spread(substance) 方法,它可以把花生酱或奶油奶酪作为参数。然后我们可以用这些 spread 方法创建更高层的函数,比如 create_pbj(),等等。在现代高级编程语言里写代码,就像站在一座由抽象构成的金字塔上,一行代码就可能触发多个系统上的数百万次操作。这非常令人兴奋!

那么,如果我们能继续往上走,把编程这件事本身也抽象掉,会怎么样?这就是代理式 AI 的梦想:让一群代理各自完成任务,而无需监督。听起来很棒!但这实际上是在处理 Brooks 所说的意外复杂性——也就是编写代码本身那些复杂的地方。自那篇文章写成以来,软件开发在对抗这类复杂性方面已经取得了很大进展。我们不再需要写底层机器码,而可以使用现代的动态解释型语言,它们会被编译成汇编。我们不必再从头记住如何写一个▶ 快速排序相信我,你会想点开这个链接的),只要调用标准库里的排序方法就行。不必从零搭建整个 Web 应用,我们可以使用现成的框架。如果我想重命名或重构一些代码,编辑器还能帮我做。AI 看起来就是最新一轮迭代,而有些编辑器已经把原本可预测的重命名和重构工具换成了不可预测的 AI 代理。没错,这看起来也许像是在掷骰子,但严重失败又能有多常见呢?

然而,即便更好的工具减少了意外复杂性,本质复杂性依然存在。设计我们的抽象和系统时,依然有大量复杂工作,而且必须做对,要优雅、清晰、可维护。这种复杂性不会消失。这类工作需要技能、经验,以及从过去系统失败中艰难积累起来的智慧。而且,我不确定 LLM 那种花哨的自动补全方式是否真的适合处理这类复杂性,因为这类问题往往并不那么直接可解。也许通过提示词可以把它引导到某种偏好的方法上,但到了那个地步,引导者不如自己单独把方法设计出来,因为 LLM 也说不清自己为什么选了某条路径。本质复杂性往往古怪、罕见、又凌乱。也许我错了,模型在处理这类混乱情境上也越来越强,但我的经验是,这往往需要一种特定的心态和方法。对我来说幸好,我很喜欢这些凌乱的东西。

我喜欢混乱

到目前为止,我一直在谈软件如何抽象流程,但我们也会把抽象的简化属性作为理解世界的工具。在经典著作《像国家一样观看》中,James Scott 描述了后启蒙时代的核心工程如何通过抽象和分类,让人口与财产变得可读。测量即是改造。比如,一个国家可能会开始不再把森林看作复杂的生态系统,而只是按其可用于造船的木材比例来评估。这样一来,国家就能据此采取行动,比如用单一树种的单一栽培来替代这些森林。森林被抽象成了一个生产船桅的系统。

这种方法催生了官僚体系和纸质表格,而后者又演变成了网页表单和数据库。作为程序员,我们需要把世界上杂乱的数据简化,才能对其采取行动。我们期望日期是精确的。我们期望姓名相对简单。我们期望数据在录入时完整,并且在时间上保持一致。每个程序员、每个系统设计,都是一连串普罗克拉斯提斯式的选择:我们希望系统反映现实的哪些方面,又可以舍弃哪些方面。我这么说不是在批评;这正是构建系统的唯一办法,否则系统就会被无穷无尽的特殊情况缠住脚(我们称之为“边缘情况”,因为它们本该是边缘上的罕见路径)。但这个过程太过内化,以至于我们有时会忘记它同样也是人为的,尤其当它在描述人时更是如此。把性别字段强行只允许“男”或“女”,并不会让性别本身变成二元。我们对种族的定义本就是随时变化的社会建构。我们的简化模型也许能给我们提供洞见(自闭症诊断在过去 20 年里增长了 300%!),却未必能捕捉这些洞见背后的根本因素(这很可能只是因为自闭症定义的变化和筛查增加)。重要的是退一步,看清任何模型是如何被构建出来的,以及它没能捕捉到什么类型的知识。每一种抽象,也是一种遮蔽。作为一名数据记者,我学会了如何“采访数据”,以及如何对所有可能误导结论的地方保持高度严谨。若想避免令人尴尬的更正,偏执一点反而是数据记者最好的朋友。你需要不仅思考数据说了什么,还要思考它遗漏了什么。

不幸的是,这种元认知是 LLM 永远做不到的。模型就是它们的现实。正如 Robin Sloan 在他那篇引人入胜的文章《“语言模型在地狱里吗?”》中简洁指出的,AI 模型是由被剥离后的世界构成,也是在这样一种简化视角下看世界。你我看文本时,也许会看到其上下文(比如文本格式和标题、作者简介、链接来源的网站),而 LLM 纯粹是在一个只有字母的世界里运作,仅此而已(严格来说,它们接收的是子词 token,这也是早期模型为什么数不清 strawberry 里字母 ‘r’ 的原因)。让一个 LLM 识别它自己对现实的视角局限,就像问金鱼水是什么

我写这一部分时,一直在想DOGE 试图在社会保障局找欺诈却闹出的拙劣尝试。其中一个例子里,DOGE 查看 SSA 数据库,发现里面有超过 900 万条记录,出生日期距今已超过 120 年,但却没有死亡日期。Elon Musk 宣称,唯一可能的解释就是数百万人在欺诈性领取福利。他不仅对问题成因错了,对其影响严重程度也错了。DOGE 本可以质疑数据质量,本可以检查实际支付情况,本可以请 SSA 的任何专家向他们解释。但他们却把数据当作既成事实,直接跳到错误结论上;这种模式他们一遍又一遍地重复(就像下面这个关于支付欺诈的不同指控例子):

在随后进行的大量分析中,机构专家根据《泰晤士报》及相关人士查阅的文件,仔细记录了 DOGE 工作中的谬误。
“这些支付都是有效的,”代理副专员 Sean Brune 在一份审查其中一个问题的备忘录中写道。(一位财政部女发言人拒绝置评。)
但据熟悉 Russo 说法的人士称,Russo 没有回应置评请求,却表示 DOGE 不会信任职业公务员。相反,他坚持要求曾在 Palantir 实习、并成为 DOGE 首席程序员之一的 21 岁年轻人 Akash Bobba 进行他自己的分析。

DOGE 这帮人以他们自己那种狂野的方式,在为自己复制着导致 LLM 走偏的同样运行条件。他们拒绝考虑数据之外的其他解释。他们不和自己圈子外的任何人交流。他们抓住一个对自己极具吸引力的简化解释,因为这完全印证了他们对无能政府雇员和无处不在的欺诈的世界观。

这并不罕见。我自己也非常害怕看起来像个蠢货,所以我绝不会把数据分析外包给 LLM。当然,很多人会这么做。我担心这个问题只会越来越糟。

摩擦是礼物

LLM 驱动开发之所以有吸引力,是因为它号称能消除摩擦。拥趸们会讲述这样的故事:开发团队在一天之内交付几十个功能,借助多支代理团队在他们的指挥下自主工作,并采用越来越奇怪的拓扑结构。我理解,软件开发有时确实枯燥又令人沮丧。能够以相当离谱的速度产出代码,摆弄经过打磨的成品而不是原型,肯定会让人感觉超级兴奋。

但我需要这种摩擦。

当我第一次学习一门新语言或框架时,我连最基本的任务都会因为摩擦而卡住。这很糟糕!当我面对一个陌生的新代码仓库或数据源时,我需要花上好几个小时去仔细审视它。我常常会做细读,把特定文件打开,一行一行查看,直到我理解它们的上下文以及开发者所做的取舍。我知道我可以直接让 LLM 给我总结项目,省下这些时间,但我的经验是,我确实需要这个过程来让代码真正“入味”。我需要的不只是理解开发者做了什么选择,还要理解他们为什么这么选,以及这些选择如何体现了所用语言的约束或惯用法。我是通过失败来学习的,如果 LLM 把这项工作拿走,我就不会真正理解自己在做什么。

即便是在熟悉的语言和自己的代码里,我仍然非常依赖摩擦作为线索。当写代码变得困难时,这告诉我自己在当前架构上走偏了,我应该认真考虑重新设计,以便让未来的扩展更容易。那种时候,我通常会出去长走一段,或者干脆当天收工,让大脑有空间退后一步,从新的角度看问题。真的很有效。我发现这些停顿如此有效,以至于即使道路看起来很明确,我也会故意强迫自己停下来。在做大型软件项目时,我会先写一份架构决策记录,再开始实现新功能,记录下我此刻想做什么、我对问题的假设,以及我的方案会带来什么影响。有时候,这甚至会让我意识到,我太沉迷于最初的直觉,以至于没看出它会怎样走偏;而且它总能为未来继承我工作的人留下一个很好的“当时到底在想什么?”的记录。

LLM 驱动方式对待摩擦的方法,就是不重新思考,直接一路把代码写过去。而 LLM 也会照做。它大概率会产出能跑的代码。性能指标会没问题,测试会通过(尤其当测试本身也是 LLM 写的)。但它不知道自己为什么选了那条路。它感受不到摩擦,也无法解释为什么某种架构方案比另一种更“干净”。如果设计提示词的工程师缺乏判断好坏方案的洞察力,他们就会陷入一种循环:一次又一次地让 AI 把代码一路写穿摩擦。这可能会导致一大堆怪异抽象,而未来团队唯一的设计文档,就是一份几年前用来给 AI 模型下指令的 Markdown 文件。祝你好运,试着从那里面重建架构决策吧!这件事很能说明问题:我见过的大多数氛围式编码成功案例,要么来自那些本来就已经是所要求 LLM 构建内容的专家的开发者(因此他们能引导它的工作),要么是失败代价很低的场景。至于其他一切,我们只能想办法知道剩下那该死的猫头鹰到底好不好、安不安全用。

如果我不提一点让我不舒服的事,那就太失职了:当 LLM 推广者把“摩擦”拿来当问题时,我最不喜欢的另一点是这个。无论是广告、现场演示还是我见过的 LinkedIn 帖子,大多数 LLM 营销都把一个孤独的工程师(或者也许是一个单独团队)描绘成英雄,用 LLM 驱动编码轰轰烈烈地拼出某种应用或网站并快速上线(我们的速度和 KPI 爆表了!)。但行业真正想让开发者把 LLM 用在工作里,而工作的摩擦通常来自既有流程和实践——这些流程和实践是为了防止缺陷,甚至防止构思都不充分的功能进入生产环境。最终,LLM 驱动的速度需求不可避免地会反过来对付人本身——其他工程师,或者产品管理、项目管理、测试、合规、设计里的队友。因为这些角色也被视作摩擦。既然可以用 AI persona,谁还需要用户研究?既然有 AI 工具能吐出网页布局,谁还需要设计?既然我们才是那支代理军团的管理者,谁还需要项目经理?如果我们不必再等另一位开发者审查我们的拉取请求,而是让通过测试和扫描的代码自动合并,那会怎样?**如果我们根本不需要花工作时间和别人说话,只活在纯粹编码的世界里,那会怎样?**但软件开发本来就是协作过程,团队中的每个人都在帮助优秀的产品成形。把这些角色移除,或者用带着 LLM 气息的幽灵替代,当然会让团队跑得更快,但这并不意味着他们交付的产品会更好。而这个过程肯定也会孤独得多。

我非常在乎

也许我不使用 LLM 的最简单原因,就是我太热爱编程了,不想把它交给机器。就像如果我是艺术家或音乐家,我也不会求助 AI 一样,编程是我表达创造力的一种方式,我不会放弃这种快乐。虽然它有时会极其令人沮丧,但把一个模糊的想法塑造成一个真实系统的过程有一种深刻的愉悦,尤其当它涉及优雅的实现或有趣的问题时。某些晚上,我会合上工作用的笔记本电脑,打开个人电脑,沉浸到某个我想做的新鲜好玩项目里。而当我作为团队的一员做专业软件开发时,那就更好了!我喜欢协作,也喜欢一起塑造软件的过程,尤其是人们挺身而出、主动承担问题的那些时刻。我不认为,当团队只是接手提示词,而由 LLM 助手去干活时,那种动态还是一样的。或者当 LLM 助手替代了团队的一部分时。

承担责任很重要。在过去几十年里,我曾在一些角色中工作,在那里我培养出了强烈的个人责任感。作为数据记者,代码里的一个错误可能导致令人尴尬的更正,甚至是一场毁灭性的诉讼。在公民技术领域,错误可能意味着在服务和福利提供上的灾难性失败,无论对象是整个脆弱群体还是某一个人。我不会说自己从没犯过错,但我非常在乎把事情做对,因为我在乎这项工作的使命。我很幸运,曾和许多同样在乎、也想尽己所能为人们做好事的同事共事。LLM 不会在乎。当然,它▶ 可以装得很像,但那仍然只是一个把更可能彼此关联的词串在一起的心智仿造品。它不会为自己的错误感到困扰,也不会试图做得更好,因为它没有内在意识,更别说良知了。它永远不可能被追责,因此我也永远不可能把我的道德责任交给它。

LLM 表现好的时候,它就是会取代所有程序员的天才;当 LLM 删除了你的全部基础设施,或者在测试上“撒谎”时,那就是你的错。毕竟,你只需要把提示词和工作流结构化得恰到好处,就能把 LLM 甩到正确输出上。哎呀,重试吧。再来一次。再来一次。我读到的大量 LLM 建议都强调,你必须在一开始就给出所有必要的指令、修正和附加条款,否则系统就会把事情做错。这种心态与敏捷编程有显著不同;敏捷编程强调频繁纠偏、持续反馈,以及相信团队会做正确的事。相反,我们似乎正在退回一种新的使用模式,类似于 20 世纪 50 年代早期计算机的分时模型。只不过这里,不再是带着一叠穿孔卡片走上前交作业,而是孤独的程序员带着法律文书,把它们转成程序。

我是在开玩笑;这里并没有法律责任的问题。考虑到相关人群的相似人口特征,这大概并不令人意外,但 LLM 供应商正在重复特斯拉的那套动态。新功能在没有安全测试的情况下就被推给用户,而更奇怪的是,LLM 拥趸就像特斯拉狂热粉一样,常常把灾难性后果归咎于自己和其他人,说是用户在写提示词时应该做得更好。我真的不知道该怎么理解这一切,但让我不舒服的是,技术正在把一种资本主义标准化:更多风险被转嫁给消费者,而公司和政府都逃避了自己的责任。我们因为草坪飞镖害死一个孩子就禁止了它们,但聊天机器人把用户逼到死亡和精神病发作,却被当作 AI 创新的代价而被接受。当氛围式编码本身真的导致某个人因为系统失败而死亡,而不是死于尴尬时,事情会改变吗?

在困难时期,编码也一直是我的慰藉。有研究表明,玩俄罗斯方块是一种有效的 PTSD 预防方式。这种疗法之所以有效,理论上是因为处理形状排列和旋转的大脑部分会干扰创伤记忆的形成。现在,我很幸运,没有遭受 PTSD(我也不是在轻描淡写那些受其困扰的人),但我确实能理解这种概念。编程就像一道复杂的谜题,在黑暗时期它有时是我的慰藉。正如上面的例子所暗示的,我对 DOGE 了解很多,因为过去一年里我一直在构建并维护一个追踪他们横冲直撞行为的系统。不同于工作项目,这更像是一场把数据集拼装起来、让一个试图保持隐匿的组织变得清晰可见的练习。这是一个有回报的练习,也是我把绝望转化为某种希望能有用之物的一种方式。这也不是我第一次用代码来消化悲伤,而它之所以有效,是因为它确实是工作;如果我只关注成品,这个过程就会被削弱。

还有几个傻乎乎的理由

这篇文章已经比我预想的长得多了,尤其是它最初只是 Bluesky 上的几条短帖。在结束之前,再补几个快速理由!

首先,我非常讨厌 AI 聊天机器人默认带着的▶ 那种谄媚语气。作为一个在东海岸城市长大的人,如果有人在我不认识他们的情况下对我异常热情、好得离谱,我会非常警惕,因为这通常意味着他们要么准备骗我,要么准备向我传教。读 LLM 的聊天记录让我浑身不舒服。是的,我知道我可以让 LLM 采用完全不同的语气,但不知为何,这反而让这个想法更糟了。

和很多开发者一样,我有一个满是未完成业余项目的文件夹。比如,有一个项目我本来打算写一个 Spelling Bee 的克隆版,但它会用 Clojurescript,这样我就能用 Blabrecs 代码生成非单词,让它变得超级令人沮丧。好吧,我猜那对我自己来说可能只是很好笑。你得在场才懂。从 LLM 的角度看,这些都是失败的文件夹,而我确实可以用 LLM 来做什么“一天一个应用”的挑战之类的事。不过,过程远比产品重要得多(又来了!)。并不是每个突发奇想都需要变成现实。很多时候,光是头脑风暴的乐趣,以及学习到足以知道自己不必继续、也不必把活干完的过程,就已经让我收获更多。我们有时很容易忘记这一点。

这本来不是一篇关于我在工作中使用 LLM 的道德性的文章。不是因为我不在乎,而是因为已经有许多人比我更有效地写过这项技术那些棘手的含义。而在此时此刻,LLM 一边在学校里轰炸儿童,一边按需生成儿童色情内容,我真的不觉得自己能安心使用它们。我也不觉得不提这一点会舒服。也许资本主义下没有真正伦理的消费,但我至少会尽力尝试。我们不能用让这么多人受苦的工具去构建一个更好的世界。

奇怪的是,似乎没有谁比 LLM 拥趸更痛苦了。如果开发者真的在把新获得的生产力用于真正过上那个四小时工作周,我或许会更容易被说服——那是十年前那些极客装作崇拜的东西。但讽刺的是,硅谷里很多人似乎是在把工作外包给 AI 代理,然后用▶ 新腾出来的空闲时间做更多工作。他们不是把时间用在放松、艺术或快乐上,而是拥抱996工作制,以及一种高度量化的工作环境,连弗雷德里克·泰勒看了都会惊恐不已。LLM 革命也许最终还是会找到我和我的工作,但我宁愿别先把自己活活累死。

那现在呢?

我不假装自己知道未来。也许这项技术会发展到某个程度,让我后悔自己缺乏经验和熟悉度。也许它会停滞不前,整个金融纸牌屋会轰然倒塌。若真如此,我希望我们能把软件开发重建为一种更有人情味的实践:一行代码一行代码地,把一个更好的世界搭建出来。

评论

(0)
未配置登录方式
暂无评论