[音乐] 大家好,我是加布里埃尔·马丁内斯。大家都叫我加博。我是一名工程经理,也是 G2I 的“垃圾代码”监管员。我来这里是为了谈谈“Slop”。我之所以谈论这个话题,是因为我对它充满热情;而说到热情,我的意思是愤怒。“Slop”这个词已经被用滥了,但它所指代的现象早已存在。只是现在它以极快的速度发生着。好吧,“Slop”是指当生成速度快于判断力时发生的事情。“Slop”并不是指 AI 生成的代码。我不想把它定义为某种可以轻易被识别出来的具体客观事物,尽管确实有一些迹象可循。“Slop”是指那些在思考尚未完成时看起来就已经完工的输出。它是这样的产品:模糊性是通过猜测来消除的,权衡是被留给偶然性的,所有权是没有被承担的。确实产出了输出,但并没有产生理解。真正的危险不仅仅在于机器做出了错误的选择。危险在于工程师从未意识到这里存在一个需要做出的选择。他们从未触及模糊性,未曾感受到约束条件,也没有发现那些会改变设计的边缘情况。工作成果已经变得平滑无碍地出现了,随之而来的是,“未知的未知”依然保持为未知。
但软件是选择的记录。技术选择、产品选择、运营选择、面向用户的选择。如果我们看不到这些选择,我们就无法拥有它们。而如果我们不拥有它们,我们就不算是真正的工程工作。我们只是在接受输出,只是像代码工人一样点击合并按钮。我不是那种来这里告诉你们 AI 很糟糕的原教旨主义者。AI 并不坏,好吗?我来这里是为了描述一些导致我们陷入 AI 醉酒状态的模式。鉴于自12月25日以来取得的进步,我们会走到今天这一步是完全可理解的。但我们需要调整节奏,需要慢下来,需要清醒过来。代理(Agents)似乎能施展魔法。互联网会让你相信确实如此。它们可以编写代码,搭建屏幕框架,经过适当调整后甚至可以构建整个功能或系统。但它们无法取代人类在决定什么重要、理解正在构建的内容、解读模糊性、长期拥有系统、在系统架构层面行使判断力和克制力,以及 arguably 最重要的一点——知道不构建什么方面的努力。Slop 并非关于 AI 作为工具的问题,而是关于判断力的缺失。
那么,让我们谈谈是什么把我们带到了这里。在一个一切皆有可能的世界里,决定构建什么比以往任何时候都更重要。人们变得过于兴奋,构建了如怪物般的、由意大利面条式代码组成的产品废话。这几乎就像他们忘记了每一行你编写的代码都是一项负债,最好的代码就是根本没有代码。创建代码的成本正在下降,但理解软件程序的成本并没有降低。我想在这里澄清一点:我并不是说每一行代码都需要被个人阅读、祝福并孤立地理解。这从来就不是标准做法。大型系统始终包含超过任何一个人所能容纳在脑海中的细节。但是逻辑必须被理解,主要流程、边界、数据模型和产品行为都必须被理解。好的软件具有一种和谐感。不是在某种珍贵的审美意义上,而是在实用层面上。代码应该反映领域的形状。AI 可以生成代码,但它无法神奇地赋予混乱的系统以连贯性。代理可以在具有结构、模式、清晰边界和理由的代码库中更有效地运作。但如果系统已经是一团乱麻,代理并不能把你从泥潭中救出来;它只是让这团球变得更大、更快。仅仅因为你可以生成另一个组件,并不意味着产品需要另一个组件。Dystra 说过一句很棒的话:如果我们能计算代码行数,我们就不会把它们视为生产的行,而是视为花费的行。
这是构建的成本。AI 使得花费行数变得更加容易;它使得在文件、抽象和复杂性上的花费变得更容易。但最终,你拥有这张账单。这张账单表现为困惑、脆弱的流程、没有人愿意触碰的功能(因为人们不完全理解它们)。它表现为在技术上能做所有事情但在感觉上却像迷宫一样的产品界面。想想那种特殊的、臃肿的 B2B 软件,其中每个任务都需要培训,每个屏幕都有12个逃生出口,没有人能解释这种复杂性是因为用户需要它还是因为团队只是不断添加东西。团队应该为他们生产的工作承担责任。“问责制”是这里的关键词。对自己正在构建的产品拥有所有权,并建立一种将其与正向或负向强化联系起来的方法。仅仅知道会有其他人审查你的工作,就会迫使工程师在更细微的细节上多花一点注意力,变得更加一丝不苟,特别是如果可以为每个工作成果块制定可见的问责指标的话。我们正进入一个产品臃肿、UX 像意大利面条一样的时代,其糟糕程度达到了你可能见过的最差的 B2B ERP 系统的水平。
如今,任何人都可以在一小时内购买代码或应用程序,最终得到一个可工作的、可点击的原型。这改变了人们的期望,因为一旦人们看到软件如此迅速地出现,他们就开始相信软件可以这么快构建出来。他们会看到几个提示词变成了屏幕;看到一个终端会话产生了看起来真实的东西。公平地说,有时它确实足够真实以至于有用处。有时候那个原型是探索想法的好方法。但问题在于,利益相关者并不生活在代码库中;那些不生活在代码库中的利益相关者经常将那个原型视为“最难的部分已经结束”的证据。他们没有考虑到那些真正让经验丰富的工程师发挥作用的环节:即非理想路径的情况、如何连贯地集成新功能集的工作,澄清需求所花费的精力,以及实际手动测试功能以确保其按人类预期工作且 UX 不至于完全糟糕的工作。他们完全没有考虑到最耗时的部分,即人性化部分、最后一公里——那里居住着判断力。在这里,必须有人阅读代码,测试行为,承担权衡的责任,注意缺失的需求,并决定这个事物是否有资格成为系统的一部分。这里有一条推文强调了我想在此表达的观点。它引用了 SQLite 的创建者关于拉取请求(Pull Request)的话题说:“你说,‘哦,这是免费的。”
不,这并不免费。你实际上是在要求我为你维护它、记录文档、测试代码,并在未来25年里持续维护。这绝非免费。”这句话深深触动了我,我无法表达得更好了。提交一个PR(拉取请求)很容易,但谁来拥有它?谁要为由此产生的后果负责呢?
来自利益相关者的外部压力,加上与那些“插槽先生”(指代某种低质或投机行为)竞争的内在压力,导致了大量的草率工作。我相信大多数人都见过这种情况:那些不审查细节、不明确规范、缺乏细致关注的“糟糕工程师”,往往能产出更多且更快。而那些关注细节并超越期望的人,反而看起来做得少、速度慢。
这种状况的蔓延会导致绩效焦虑。在团队环境中,这会迫使工程师在未充分判断的情况下快速合并PR,导致代码在未完全理解时被合入。短期思维——即尽快完成PR的冲动——压倒了长期的代码健康考量。
我亲身经历过这种焦虑,身边的工程师以惊人的速度合并代码,我感到持续的压力,觉得自己不够快。我承认这导致我在某些时候合入了未经过通常那样细致审查的代码,而这本是我应该做的。我见过许多恐怖故事:有人合入了他们不完全理解甚至未亲自测试的代码。
归根结底,我们都是人类,受激励机制驱动。如果激励措施驱使人们去快速提交PR,那么我们终将面临残酷的现实。奖励应围绕产生价值展开:优雅、结构良好且务实的系统设计,以及清晰的测试和评估工具。
看到数千行的PR总让我感到极度不适。绝无可能让人类完全理解如此大规模变更中的所有重要部分。作为工程师,我们的职责是优化这个新世界:将需求分解为最简单的单元以促进理解;简化代码表面并控制我们的代码库。
AI本应取代工作,工程师的速度似乎应与令牌生成速度同步。因此存在巨大压力去生产更多、更快。AI确实在做到“生产更多、更快”,但这些多出来的字符消耗了更多的人类时间。
我见过最平凡的方式:你给出10个要点范围、几个用户旅程,意图是轻量级的,结果对方却带回一份20页的文档。也许它看起来很精致、专业,但我不得不花数小时去判断其中是否有真正的思考内容。这就是最糟糕意义上的AI生成工作:它没有澄清问题,而是扩大了问题;它将本应由执行者解决的模糊性,作为更大的工件交还给其他利益相关者去处理。
在代码中,含糊的任务变成了大型PR;一个小功能变成了一堆文件。输出呈现出完成的样子,但真正的工作转移到了审查者身上。这是我们需要诚实面对的部分:生成是廉价的,而评估是昂贵的。如果AI产生的内容超过人类能评估的极限,它并未加速团队,而是制造了瓶颈。
有一件事让我特别恼火:推特上充斥着自封的专家和标题党,他们总结某些演讲或宣扬我们早已知晓的道理,仿佛那是某种新颖的天才创意。“Ralph循环”就是其中之一。它被包装成一种新颖的智能体突破,但实际上只是一个while循环。
我们似乎完全忘记了“代码行数”是衡量生产力的垃圾指标。但随后我们又决定,消耗最多令牌的工程师就一定产出最多。我们过去会将功能分解为可推理的小原子单元,小到足以在脑海中把握并实际审查。现在,我们输入两句话,就期望一次性返回一个未经深思熟虑的完整功能集,而并不真正理解我们在要求什么。
我想强调的关键点是:我们不应忘记那些始终存在的真理。六十年的软件工程智慧并未被AI否定。可组合的代码、良好的模式和扎实的实践依然至关重要。
最近一位朋友质疑我,为何花那么多时间思考智能体的护栏、编码规范、架构风格以及系统塑造的约束。他的问题基本是:如果智能体写代码,我们为何还要关心代码的样子?在某种程度上我同意,最终会有一个世界,大部分实现细节对人类完全抽象化。这一直是软件发展的方向:从机器码到汇编(增加了一层抽象),再到C、Java,然后是像Ruby这样以程序员快乐为主要设计目标的语言。
正如在语言中我们为垃圾回收和手动指针构建抽象一样,AI正在将命令式部分抽象化。但这并未消除理解系统如何 cohesive(连贯)地结合起来的必要性。这就是为什么图表如此重要:它们不是事后装饰性的文档,而是使系统可读的手段。
当智能体能生成数千行实现细节时,人类需要一个压缩的审查表面:服务边界、数据流、状态转换和故障模式。好的图表让我们以代码往往掩盖的方式看到系统的形状。我们可以将主流程或工作流作为可审查的表面,而将实现细节推至叶子节点。
这就是为什么我推崇声明式代码和架构模式,它们创造出人类真正能够推理的层级。目标不仅仅是生成代码;目标是人类的认知、理解和审查。我们需要优化这一人类层面。
认知开销是每位工程师在进行有意义变更前必须支付的税赋:这是花费在弄清楚代码归属、遵循何种模式以及什么是正确实现上的时间。AI并未移除这种税赋,除非加以控制,否则它会加剧这一负担。一个智能体可以用五种不同风格生成五个合理的模式。现在人类审查者不仅要逆向工程代码,还要逆向工程其背后的意图;有了适当的规范,意图更容易被把握。答案在于规范。
标准文档化、流程清晰、审查面小且所有权边界明确,以及“完成”的定义。路径越明显,“垃圾代码”藏身的空间就越少。在此我要特别致敬 Rails 及其在我心中的地位。Rails 的重要性在于它展示了当框架积极减少决策面时会发生什么:通过约定来降低你的开销。
与其最终出现十种不同的方式来构建同一个概念,不如只保留一种方式。它可能在每个人的主观看法中并非完美无缺,但它被广泛理解且行之有效。在我的职业生涯中,在第一次和第二次使用 Rails 的四年间隔期之间,我休整了四年。
在那段空白期后回归时,看到事实上的标准库模式依然如故,这真是一种美好的体验。我可以立刻跳入代码库并理解其意图,而无需解析复杂的架构设计。这使得工程师能够以最小的上手成本在不同项目间横向流动。
其他语言生态系统则无法做到这一点。拥有单一正确做事方式的 JavaScript 环境更有利于智能体开发,因为智能体不会被数十种相互竞争的方法所困扰。这一点得到了 Rails 官方信条的有力支持,它明确将“优化程序员的幸福感”和“约定优于配置”作为核心支柱。
并将约定描述为消除重复决策、降低入门门槛的方式,这正是为了减少认知开销。我今天所谈论的一切,正是促使我们构建 ORC 的原因。
这不仅仅是因为我们认为答案就是更多的智能体,更不是因为我们认为工程判断可以被自动化取代。我们构建 ORC 是因为我们反复看到相同的失败模式:AI 可以生成代码,但周围的纪律性仍然缺失。
包括规划、审查、对抗性检查以及上下文管理。久而久之,我们发现自己不得不重复相同的人工流程来对抗“垃圾代码”:分解工作、管理上下文、权衡选项,并确保输出以团队仍能推理的方式进入代码库。
ORC 正是将这些经验教训融入工作流中。它不是一个让正确性作为人类责任消失的黑盒魔法,而是一个在允许智能体承担更多工作的同时,保留这一责任的系统。
目标不是交付更多的生成代码,而是确保在生成交付后,软件依然保持连贯、经过测试、可维护且有人负责。因为未来不是靠“感觉编程”变得更好或更快,而是依靠带有判断力的编排。感谢大家的时间,请记住:不要变得马虎。