遵从性 vs 理解
摘要
作者回顾了他在软件标准和开源领域的职业生涯,然后描述了使用 Claude 构建一个名为 Roundhouse 的编译器,该编译器将 Rails 应用程序转换为静态类型语言,如 Rust、Crystal 和 TypeScript。
<p><a href="https://lobste.rs/s/lgutwh/conformance_vs_comprehension">评论</a></p>
查看缓存全文
缓存时间: 2026/07/27 11:44
# 合规性与理解力
来源:https://intertwingly.net/blog/2026/06/27/Conformance-vs-Comprehension.html
### 合规性与理解力 (https://intertwingly.net/blog/2026/06/27/Conformance-vs-Comprehension.html)
---
2026-06-27T07:56:05Z
1991年,一位来自芬兰的年轻学生改变了世界。
90年代中期,我带领一个团队为IBM开发软件配置管理工具——大致相当于一个本地部署、互联网出现之前的GitHub,包含版本控制、问题跟踪和持续集成。在当时过于雄心勃勃;以今天的标准看却微不足道——很大程度上是因为那位来自芬兰的学生在2005年再次出手,将版本控制本身变成了一种基础工具,即Git。先记住这点,我后面会再提到。
我们的目标市场是企业级,因此我们支持当时所有主流的Unix系统:Solaris、HP-UX,当然还有IBM自家的AIX。我们的代码是用C写的,每个源文件开头都有一堆`#ifdef`来掩盖操作系统之间的差异。我看到了一个叫Linux的新东西,于是花了一个周末把我们的代码移植到了上面。我的管理层很欣赏这种主动性,而且碰巧有一位来访的高管要来——她让我向他展示了我所做的。
简单来说,他并不感冒。他看不到开源有什么未来,至少对企业客户而言。我本可以在这里结束这个轶事,说他是傲慢的傻瓜,或者指出我碰巧选择的发行版是Red Hat。但这两点都不是我想说的。
考虑到当时的信息,他的判断完全合理。开源曾经——并且现在仍然是——混乱且不可预测的。企业需要治理。
我想说的是,他对开源并没有判断错。他是错在*看错了地方*。他治理的是工件——代码,那个你检查、认证和支持的东西——而工件并不是持久性企业价值所在之处。十多年后,IBM以340亿美元收购了Red Hat。Red Hat卖的不是内核。它卖的是合规性、认证和赔偿:一种无需逐行检查代码就能治理开源的方式。资产已经转移,从代码转移到了代码所服从的东西上,而旧模式因为忙于守护旧位置,所以看不到新位置。
我职业生涯中有相当一部分时间都站在那个转移的附近,处于构建代码所服从的东西的那一边。退休前,我在标准领域工作了多年——在W3C共同主持HTML,在IETF担任Atom的秘书,在TC39任职,并在Ecma召集C#和CLI工作组。最后这一点对我今天想说的最为重要,因为标准化一种语言和一个运行时,并不是标准化一个工件。而是标准化一种*行为*:实现必须重现的一系列精确行为,才能算作合规。ECMA-334和335的存在,使得Mono可以在不接触一行微软源代码的情况下成为C#和CLI的合法实现。规范是中立的仲裁者。实现只是它的下游。
所以,测试驱动的合规性是我习以为常的环境。当我注意到某件事时,通常就是这种形态。我告诉你这些,并不是为了宣称对结论拥有权威——一个只有一个项目且偏见强的人不应该宣称对任何事拥有权威——而是为了让你理解为什么某件具体的事会触动我,以及为什么尽管我的样本量只有一,但这件事可能值得再看一眼。
## 项目
这就是触动我的那件事。在几个月的时间里,退休的我,与Claude作为共同作者,构建了一个名为**Roundhouse** (https://rubys.github.io/roundhouse/demo/)的编译器。它读取一个Rails应用程序——无类型Ruby——并生成几个静态类型目标的独立项目:Rust、Crystal、TypeScript、Go、Python、Elixir,外加一个Ruby往返。生成的项目编译通过并测试达标。我知道它们正确的方式是依靠一个合规性仲裁者:从Rails和每个目标获取的相同URL会产生字节完全相同的响应,并通过三种方式检查——对固定预期值运行的单元测试、针对实时Rails的差分比较门控,以及针对静态差异无法触及的动态行为的端到端浏览器测试。
这里我要小心,因为那段话里混杂了两个不同的主张,并且在这个场合下,它们的含义截然不同。让我把它们分开。
## 第一个主张:轴线可能划错了
此次静修会(retreat)的结论,在关于语言用于智能体(agents)的部分,得出了一个听起来合理的结论:偏好表达性而非安全性的语言,使得智能体生成和人工审查都变得更困难。与会者一致认为强静态类型是智能体输出的护栏——让错误代码无法被表示。
Roundhouse是一个小小的证据,表明这条轴线可能划错了地方。它对一个无类型的Rails应用程序执行全程序类型推断,并生成到具有安全属性的类型目标中——且无需任何人编写一个类型注解。安全性是真实的;注解成本为零。
不过,我不想过度推销这一点,因为如果我说得过分,在座的类型语言拥趸会合理地给我一个反例。类型推断消除注解成本并非新闻——ML传统已经展示了四十年。我的主张更狭隘的版本是,而且我认为更站得住脚:Rails*本来就已是类型化的*。`has_many :comments`就是一个类型声明;框架的约定是隐式的类型信息,只是从未被写下来。全程序推断可以恢复它。所以我想放回会议桌上的轴线不是“表达性与安全性”。而是“注解与推断”——以及项目因存在而提出的一个相关问题:智能体生成的工件*到*必须与人类编辑的工件相同吗?在Roundhouse中不是。我编辑的是密集的、描述意图的源文件;编译器生成的是冗长的、描述机制的目标准入代码。两方都不必承担另一方的冗长成本。
这是第一个主张,它确实是可证伪的、与会议一个结论的分歧。但这不是我今天认为最重要的那个。
## 第二个主张:成本已经坍塌
真正重要的是构建那个东西的成本。
对一个真实框架进行全程序类型推断,并配备多目标合规性仲裁者,这类事情过去需要一支有资助的编译器团队、或一个供应商、或一个研究实验室,或者——在我最了解的那个案例中——一个拥有多个成员公司和多年流程的标准机构。标准化CLI以使Mono和微软服从于同一个规范,需要一个工作组和多年时间。特别是合规性仲裁者——机械地决定实现是否正确的那部分*可执行代码*——向来是标准化中昂贵的一半,那部分常常从未被完全构建出来。JavaScript的Test262在散文规范之后很久才出现,而且是一项真正艰巨的工作。
Roundhouse作为一个单一样本所证明的是,昂贵的那一半已经变得便宜了。一个退休人员和一个人工智能智能体,在几个月内,就生成了合规性仲裁者以及服从于它的多目标编译器。这就是我想请这个房间思考的观察——不是“AI写代码更快”,这在座各位早已消化——而是“AI已经将过去需要机构才能编写的工件的成本压缩到了极低”。
而当你意识到某样东西变得更便宜时,在座的各位都会想到和我一样的想法:杰文斯(Jevons)。当你让一种资源的产出成本更低时,总消费量会上升而非下降——更高效的蒸汽机并没有减少煤炭用量,反而让煤炭在更多事情上经济可行,于是我们烧了更多煤。将这个道理应用到这里,安慰人的一半结论就不言自明了。更便宜的软件生产并不意味着更少的软件或更少的工程师;而是意味着远更多的软件,以及对其方向判断的更多需求。既然编写一个仲裁者现在很便宜,我们不会编写得更少。我们会编写数量级的更多。这就是我接下来要说的每件事背后的需求侧驱动力——这就是为什么对“一个人建成了这个”的回答不是“多么稀奇”而是“那么会有很多这样的人”。
注意这如何与标准视角形成闭环。Roundhouse中持久的东西不是编译器——我写不了编译器,也读不懂它产生的Rust代码生成。持久的东西是仲裁者:每个目标都要测试的参考行为,那个我真正编写的东西。当下一个模型写下更好的发射器时,它仍然必须通过我的仲裁者。这正是合规性测试套件与其实现之间的关系,也正是那位IBM高管在Red Hat的认证和Linux内核之间看不到的关系。资产是实现所服从的规范。从1990年代到2026年间改变的,并不是这一点变为事实——它一直就是事实。改变的是,编写这个资产不再需要机构了。
## 严谨性去了哪里:合规性,而非理解力
此次静修会最大的问题——那个它说几乎在每个分会上都浮现的问题——是当智能体编写代码后,工程严谨性去了哪里。与会者给出了五个答案:向上游进入规范、进入测试套件、进入类型系统、进入风险分层,以及进入“持续理解力”。我将毫不犹豫地签署其中两个,搁置一个,然后花时间在我认为方向画反的那一个上——因为我构建的项目正好落在它上面。
先说规范和测试,因为这里我只是同意——在座各位中,有人在智能体出现之前很久就倡导测试优先开发。报告中最尖锐的一句话是关于TDD的:在代码之前编写的测试避免了“一种特殊的心理错误,即智能体编写了一个验证了错误行为的测试”。这完全正确。值得一说的是,这种信念在会议之外并非普遍认同——Linus,那个这次演讲一直绕回来的芬兰学生,就以对测试优先开发持怀疑态度著称,而大多数日子里我宁愿站在他那边而不是反对他。但在这一点上,我站在会议这边反对他。Roundhouse的每一次迭代都是测试优先驱动的,而这个纪律恰恰做到了你们中许多人多年来一直主张的那样:它让智能体无法通过悄悄降低标准来宣布胜利。*测试本身就是标准*,智能体不能移动它。
但注意*哪些*测试,因为我认为会议低估了这里的区别,而这正是整个核心。有两种测试,我们往往混为一谈。一个**规范测试**断言一种行为是因为规范如此规定——它源自书面权威。一个**合规性测试**断言一种行为是因为参考实现,或者其下游的真实消费者,实际上依赖于它——它源自观察到的行为。Test262是一个规范套件:它的存在是因为ECMA-262这么说。Roundhouse的仲裁者是一个合规性套件:它的存在是因为Rails做了某件事,而正确的目标必须做同样的事,字节完全一致。真实系统中大部分的严谨性存在于第二种。大部分的声望则附着于第一种。
让我用我能想到的最小项目来论证。Roundhouse需要一个真实的Rails应用,而我最想要的是Mastodon,而Mastodon的视图是用HAML写的——所以“支持Mastodon”悄然变成了“实现HAML”。这是整个方法:我调查了Mastodon实际使用的HAML特性——不是整个语言,而是Mastodon依赖的那个子集。我实现了那些特性。我以Mastodon自己的源代码作为语料库来测试它们。测试立即暴露了差距——那些我略错或未曾涵盖的构造——而每个差距都变成了一个小的、快速的迭代:失败的案例、修复、变绿、下一个。
而这就是我希望在座各位思考的部分,因为它直接反驳了你们五个答案中的一个——而且我猜,也反驳了你们许多人比那五个答案更珍视的东西。报告关于持续理解力的部分引用某人说结对编程“解决了所有问题”——如果理解系统很重要,你应该“一直这样做”,而不是分成小阶段。这在会议中并非边缘观点;对你们许多人来说,结对和持续的理解共享几乎是最根本的承诺,是从漫长职业生涯中诚实地挣来的。所以让我精确地说出我在哪里分道扬镳,因为这取决于一个词。我的主张的舒适版本是,合规性*补充*了理解力——测试让我可以稍微少理解一点代码。但实际情况并非如此。三个月来,在整个项目中,合规性已经*取代了*理解力。我并没有构建一个关于HAML降级的薄心理模型然后依赖套件来弥补其余部分;我构建了零个。我从未读过生成的代码,一次也没有,即使读了也无法评判,因为我不写编译器。没有我时刻保持更新的架构,没有我脑中的系统,没有理解力发生转移的环节。关于代码在做什么的持续模型——正是那一节想要保留的东西——从未进入过循环。
这听起来鲁莽,但只有当你没看到理解力并未消失、它只是转移了时才会如此。我完全理解的是*问题*——Mastodon依赖哪些HAML特性,什么是一个公平的子集,正确的输出逐字节看起来如何。我编写的是*仲裁者*:语料库、预期值、比较门控。实现是那个仲裁者的输出——智能体编写代码以满足我编写的规范——所以阅读代码以让自己放心正确,就等于检查机器输出是否匹配我交给机器的输入。我的控制权和智能体的自由权在仲裁者处交汇,别无它处。对我来说,这一天不再是理论的那天,是我注意到一旦我完成编写定义“正常工作”的测试,立刻就知道某件事是否工作,而不是在运行时才知道:一个我以整个项目为目标的基准数字,我在写下这些文字前几分钟才第一次执行它,而结果完全如我所料——因为仲裁者已经在每一个重要的层面检查过了它,我自己的运行只是一个形式。这就是从内部感觉到的取代。第一人称确认因构造而变得多余。
这也是为什么我对会议在其他地方提出的决策疲劳担忧无动于衷——智能体产出的速度超过了人类说“是”的速度。在HAML的工作中我几乎从不说“是”。没有判断性决策让我疲劳,因为语料库已经决定了我在意的每一个案例:输出匹配Mastodon的,或者不匹配。决策疲劳是人类充当仲裁者时才会出现。把仲裁者移到测试套件中,疲劳也随之而去。
所以合规性测试被低估了——这是肯定性的主张。但我欠你们一个边界,因为合规性套件有一个真正的失败模式,假装没有就是欺骗。它编码了参考实现碰巧做的任何事——它的bug、它的意外,以及你从未想到去测试的输入情况。它告诉你,在你测试的案例中,你匹配了仲裁者;它不能告诉你仲裁者是正确的那一个。这正是书面规范发挥价值的地方。当合规性测试保持沉默,或者当合规性与规范不一致时,规范胜出——不是因为它有威望,而是因为它是唯一能裁决参考实现未定义的那些案例的东西。诚实的层级关系:规
相似文章
当你的最爱编程语言面临法西斯主义问题时,你该怎么办?
作者因创建者 DHH 的争议性观点,对使用 Ruby 和 Rails 感到矛盾,探索了 Hanami 和 Gleam 等替代品,但未找到完美替代。
从Rust到Ruby
开发人员描述使用LLM将一个15,000行的Rust Web应用转换为Ruby on Rails,发现Ruby版本明显更短,并评估了开发速度、安全性和可测试性方面的权衡。
关于编译器构建的观点
本文分享了关于现代编译器构建的个人观点,主张使用如OhmJS进行解析和Prolog进行类型检查的工具,重点强调简洁性和迭代开发。
重返Zig
作者描述了从Zig到Rust再回到Zig的历程,探讨了编程语言中稳定性与表达力之间的权衡。
为什么多年来 Ruby 依然让人有家的感觉
作者回顾了使用 Ruby 的 15 年经历,称赞了其隐藏特性,如 refinements、delegation 以及新的 ZJIT JIT 编译器,并指出 Ruby 搭配 ZJIT 正在缩小与 Go 和 Rust 等更快语言的性能差距。