Essays北极甜虾 · No. 6

λ
ESSAY编程 · 软件工程
重读 SICP:代码不用手写以后

英语是最好的
编程语言吗?

画家、钟表匠和巫师
2026 年 9 月,Ruby on Rails 的作者 DHH 在台上宣布:他的公司不再手写代码,他找到了一门比 Ruby 更好的编程语言,叫英语。他还说,抽象这件事要重新估价。这篇拿他的话,去问一本 1985 年的老书《计算机程序的构造和解释》(SICP)。答案分三段:画家讲 AI 改变了什么,钟表匠讲什么没变(附五条定理),巫师讲 SICP 今天该怎么读。读完你会带走一个小工具:听到“某某在 AI 时代过时了”,先问它付的是哪本账。
画家变了什么钟表匠什么没变λ巫师SICP 怎么读
本文的三个角色。画家讲变了什么,钟表匠讲什么没变,巫师讲 SICP。

2026 年 9 月,奥斯汀,Rails World 大会。Ruby on Rails 的作者 DHH(David Heinemeier Hansson)在开场演讲里问台下:谁每周还在大量手写代码?据他说,举手的大约只有五个人。

他自己已经好几个月没手写代码了。他的公司 37signals(做 Basecamp 和 HEY 邮箱的那家)刚刚决定“放下铅笔”:从此手写代码算异常情况,就像监控里冒出一条报错,要回头查哪里出了问题。

然后他说,他发现了一门比 Ruby 更好的编程语言:

“英语是比 Ruby 更好的编程语言。”DHH,Rails World 2026 开场演讲

他也承认,英语更含糊一点,结果也没那么确定。可他说,用英语写程序是一种纯粹的享受。

这话从 DHH 嘴里说出来,分量不一样。Rails 是靠 Ruby 的优雅起家的,他爱 Ruby 是出了名的。

再往前推四十一年。1985 年,MIT 的 Abelson 和 Sussman 出了一本书,叫《计算机程序的构造和解释》,大家简称 SICP。第一版序言里有一句很有名的话,大意是:程序首先是写给人读的,能在机器上跑只是顺带。整本书讲一件事:怎样用抽象驯服复杂。

DHH 在同一场演讲里说,到了 agent 的时代,抽象不再那么说得通了。如果他是对的,SICP 就该进博物馆了。

我的看法是:DHH 说对了一半。要分清是哪一半,得请三个角色出场:一位画家、两个钟表匠、一个巫师。

PART ONE画家AI 改变了什么
01
THE PAINTER

画了三年的肖像

DHH 的演讲是从一幅画讲起的。

1883 年到 1886 年,丹麦画家 Laurits Tuxen 给国王克里斯蒂安九世一家画了一幅大型群像,前后画了三年。这幅画今天挂在哥本哈根的克里斯蒂安堡宫。据 DHH 说,Tuxen 是他的高祖父。

1900 年 2 月,柯达推出布朗尼相机,一台一美元。给普通人留影,突然便宜到谁都付得起。DHH 的说法是:从那以后,把人画得逼真不再是一门好生意,画家们只好另找出路。

DHH 把 2025 年 11 月一个编程模型的发布,比作程序员的布朗尼相机。从那以后 37signals 发生的事,他在台上列了一长串。下面都是据他说:

6个原生应用HEY 不再只做网页版
99%CPU 用量下降HEY 后端改用 Rust
15 万行代码,一个月以前一年约 3 万行

以前小团队养不起六个平台的原生代码,只好做网页版,现在养得起了。他讨厌 Rust 的长相,可代码既然不用他看,也就无所谓了。今年 8 月他“写”了 15 万行代码,大部分是他从来不看的 Rust。

Ruby 的作者松本行弘(Matz)在另一场对谈里开了个玩笑:他早就是 vibe coder 了,一当就是十五年。身为 Ruby 的掌门人,他提需求,核心提交者写代码,他来审、来合并。现在不过是把“有机智能”换成了 agent。

这些都是实打实的变化,不是噱头。下一节,我先替 DHH 把道理讲足。

02
PRICES, NOT TRUTHS

很多规矩只是价格

程序员有很多规矩,听上去像真理,其实是价格。

小团队为什么做网页、不做原生?因为六套原生代码要六拨人维护,养不起。为什么用 Ruby、不用 Rust?因为程序员的一小时比机器的一小时贵得多,宁可机器多跑,也要人少写。软件为什么越做越臃肿?因为花工程师的时间去抠体积,不划算。

这些规矩付的都是钱账。人工贵、机器便宜,账就这么算。agent 把写代码的人工降了几个数量级,账就得重算。DHH 让 agent 写了一个做演示文稿的软件,二进制只有半兆。以前没人肯为这半兆付工钱。

还有一类规矩付的是组织账。1968 年,Melvin Conway 观察到:一个系统的结构,会照着设计它的组织的沟通结构长。后来大家管这叫康威定律。很多模块边界,其实是团队边界。团队变成一个人加一千个 agent,边界自然要重画。DHH 说,成千上万个 agent 同时改一个应用,抽象会变成卡脖子的点。按康威定律,这话说得通。

第三类付的是人脑账。人的工作记忆只装得下几样东西,所以函数要短,名字要好,一屏要看得完。对谈里 DHH 说得更狠:过去六十年的软件工程,都是在应付人类认知的局限,局限没了,这些办法也就不需要了。

这三类,我大体同意。问题出在最后一步:是不是所有规矩,付的都只是这三本账?

03
FOUR LEDGERS

四本账

我想加上第四本:数学账。

脑
人脑账人的注意力和记忆有限。函数要短、名字要好、一屏看完,付的是这本账。AI 之后:代码不再逐行给人读,这本账大幅缩水。
组
组织账团队之间怎么分工、怎么沟通。模块边界常常就是团队边界。AI 之后:组织变了,边界要重画。
钱
钱账人工和机器的相对价格。做网页不做原生、用 Ruby 不用 Rust、不抠体积,付的是这本账。AI 之后:写代码的人工几乎免费,取舍全部重算。
数
数学账错误会累积,组合会爆炸,有些问题根本不可判定。AI 之后:一分不少。
四本账。前三本被 AI 重新定价,第四本对谁都一样。

数学账和前三本不一样,它约束任何会出错、算力有限的推理者。人是这样,agent 也是这样。

判断工具就一句话:听到“某某在 AI 时代过时了”,先问这条规矩付的是哪本账。付前三本账的,确实可能过时;付第四本账的,换谁来写代码都躲不掉。

人脑组织钱数学AI 之后函数要短,一屏看完贬值小团队只做网页版贬值用 Ruby,不用 Rust贬值按团队划分模块重画逐行做代码评审变形分层、模块化变粗同一事实只存一处留下接口、类型、测试留下并发要加锁留下
九条常见的规矩各付哪几本账(作者的判断)。只付前三本的,AI 之后可能贬值;沾上数学账的,留下来。

拿 DHH 最激进的那句来试。他说,抽象的理由之一是不重复;现在重复几乎免费,保持同步也一样便宜。

“不重复”付的是哪本账?少打字是钱账,这一半他说对了。可重复之后还有一笔账:同一件事存在一千个地方,改的时候要改对一千次。这笔是数学账。

下面请钟表匠出场。

PART TWO钟表匠什么没有变

这一部分有五条定理,每条都附证明。证明可以跳过,看懂结论和图就够了。

04
TWO WATCHMAKERS

两个钟表匠

1962 年,Herbert Simon 写了一篇文章,叫《复杂性的架构》。他后来拿了图灵奖,又拿了诺贝尔经济学奖。文章里有一个寓言。

两个钟表匠,Hora 和 Tempus,做的表都有一千个零件。两人手艺一样好,生意一样好,电话也一样多。每接一个电话,手里没装完的东西就散了。

Tempus 一个零件一个零件往上装,一接电话就前功尽弃。Hora 先把十个零件装成一个小组件,十个小组件装成一个大组件,十个大组件装成一只表。一接电话,他只丢掉手上那一个组件。

Simon 设每装一个零件,都有 1% 的可能被电话打断。两人的差距有多大?他自己粗估了一下,Tempus 花的时间大约是 Hora 的 4000 倍。下面用一条定理把它算准。

定理 1钟表匠定理

一段活要连续做完 k 步,每一步独立地以概率 p 被打断,打断了就从头再来。记 q = 1 − p,完成它的期望步数是

T(k) = q−k − 1p

证明先做完前 k − 1 步,期望用 T(k − 1) 步。再做第 k 步:花掉 1 步,以概率 q 成功;以概率 p 被打断,从头再来,还要再花 T(k) 步。所以 T(k) = T(k − 1) + 1 + pT(k),即 T(k) = (T(k − 1) + 1)/q。由 T(0) = 0 递推,代入验证即得。∎

代进数字。Tempus 要一口气做完 1000 步,期望 T(1000) ≈ 231.6 万 步。Hora 要装 111 个组件(100 个小的、10 个大的、1 只表),每个 10 步,一共 111 × T(10) ≈ 1,174 步。Tempus 花的步数是 Hora 的 1,974 倍。比 Simon 的粗估小一半,是因为他用的是粗略的估算办法;Simon 自己也说,这个数不必太当真。差了三个数量级,这一点不变。

Tempus一个一个装,1000 个零件一口气电话!前功尽弃期望231.6 万 步Hora十个一组,分三层一只表大组件 ×10小组件 ×10零件 ×10期望1,174 步少了约 1,974 倍
一千个零件,两种装法。每装一个零件有 1% 的可能被打断,期望步数由定理 1 算出。

Simon 用这个寓言解释,为什么自然界的复杂系统几乎都是分层的:原子、分子、细胞、器官。原因不是造物主的“脑子”不够用,而是有了稳定的中间形态,复杂的东西才造得出来、留得下来。

注意,这里没有人脑什么事。Tempus 和 Hora 一样聪明,记性一样好。差别只来自一件事:会被打断,打断会丢掉进度。agent 也会出错。

推论 1.1最优模块

总共 N 步,每个组件 b 步。p 很小、N 很大时,总期望步数约为

N(1 + 1b + pb2)

它在 b ≈ √(2/p) 时最小,这时约为 N(1 + √(2p))。不分层(b = N)只在整件活不超过约两个最优模块、即 N ≲ 2√(2/p) 时才划得来。

证明组件个数是 (N − 1)/(b − 1)。由定理 1,p 很小时 T(b) ≈ b + b(b + 1)p/2。两者相乘,b 和 N 都较大时约为 N(1 + 1/b + pb/2)。对 b 求导,1/b2 = p/2,得 b = √(2/p),代回得最小值。不分层的期望步数约为 N + N2p/2,它小于 N(1 + √(2p)) 当且仅当 N < 2√(2/p)。∎

这条推论正面回答了 DHH。agent 越来越可靠,p 越来越小,最优模块就越来越大:

1.01.21.41.62101001000平均每个零件实际花的步数组件大小(步,对数坐标)1445142出错率 1%0.1%0.01%
一百万步的活,组件大小不同时,平均每步要付出多少步(按定理 1 精确计算)。三个出错率下,最低点分别在 14、45、142 步。

出错率从 1% 降到 0.01%,可靠性提高 100 倍,最优模块从 14 步变成 142 步,粗了 10 倍。抽象会变粗,不会消失。要让不分层成为最优,整件活得小到只有两个模块那么大。精确地算,出错率 1% 时,只分一层就比不分层省事的分界,在 27 步左右。一个真实的软件系统,离这个条件差得远。

当然,这是玩具模型。真实的错误不独立,也不一定让人从头来过;“一步”在代码里也没有明确的单位。但它抓住了一件实在的事:稳定的中间形态值多少钱,取决于出错率和活有多长,和干活的是人还是机器无关。

DHH 自己讲过一个例子。2026 年春天,37signals 让几位设计师用 vibe coding 给 Basecamp 5 补最后几个功能。据他说,每个 PR 单看都像样,二三十个合起来,架构像一块瑞士奶酪,到处是洞。他事后的结论是:当时的判断错了,再等几个月,模型更强,就没事了。

模型更强,p 确实会变小。可瑞士奶酪的毛病不在哪一步做错了,而在二三十步之间,没有一个稳定的中间形态把它们拢在一起。Hora 的组件,就是这个中间形态。

05
A THOUSAND COPIES

一千份拷贝

回到 DHH 那句:重复免费了,同步也便宜了。

设想有一条规则,比如“会员价打八折”,被写在 n 个地方。每次改价,agent 要把这 n 处全改对。它很可靠,每一处改对的概率都是 r。

定理 2拷贝定理

n 处全部改对的概率是 rn,并且

rn ≤ e−n(1 − r)

证明各处独立,概率相乘得 rn。由 ln r ≤ r − 1,得 n ln r ≤ −n(1 − r),两边取指数。∎

r = 99.9%,已经比大多数人细心了。n = 1000 时,全部改对的概率只有 36.8%;n = 5000 时,只剩 0.67%。

0%25%50%75%100%11010010001 万一次改动全部改对的概率同一条规则抄了几份(对数坐标)36.8%每处改对 r = 99%99.9%99.99%
同一条规则抄了 n 份,一次改动全部改对的概率。三条线是每一处改对的概率 r。

关键是乘积 n(1 − r)。“同步便宜”把 1 − r 变小,“重复免费”把 n 变大。只要 n 涨得比可靠性快,全对的概率照样往零掉。只存一份,这个问题就不存在了:一致性不再是靠细心维持的概率事件,而是结构保证的必然。

数据库领域五十多年前就想明白了这件事。1970 年,IBM 的 Codd 提出关系模型,后来发展出“范式”:同一个事实只存一处,免得改了这里、忘了那里。数据库里执行更新的本来就是机器,机器很可靠,数据库照样讲范式。工程上也常故意冗余存几份,换查询速度,可那是明码标价的取舍:多出来的同步,得有专门的机制负责。

DHH 说的另一半,也碰到了数学账。他说成千上万个 agent 同时改一个应用,抽象会成为卡脖子的点。卡脖子的点为什么存在?SICP 第 3 章专门讲过。

推论 2.1交错数

两个进程分别有 m 步和 n 步,同时运行时,它们的步骤一共有 C(m + n, m) 种交错顺序。

证明每个进程内部的顺序不变。一种交错,就是在 m + n 个位置里挑出 m 个给第一个进程。∎

进程 A 走一步 →进程 B 走一步 →起点终点C(6, 3) = 20 条路A 先走完轮流走各 3 步20各 10 步184,756各 50 步约 1029
两个各 3 步的进程,每一种交错是网格里的一条路,一共 20 条。

两个各 10 步的进程,就有 184,756 种交错;各 50 步,约 1.0 × 1029 种。每一种都可能算出不同的结果。SICP 3.4 节有一道习题(习题 3.38):账户里原有 100 美元,Peter 存 10 美元,Paul 取 20 美元,Mary 取走一半。三人依次办,余额可能是 35、40、45、50;三人同时办、步骤交错,还会冒出别的数。

锁、队列、只能从一个口子进出的模块,都是把交错压回一种顺序的办法。它们确实拖慢并行,这一点 DHH 说得对。可拆掉以后,要验证的就不是一条路,而是天文数字条路。这同样不是人脑的问题,agent 也得面对这些路。

06
THE BLACK BOX

黑箱验收的边界

DHH 还说,Rust 他一行不看,只把它当黑箱,从外面验收,就像历史上所有花钱请程序员干活的老板。

这话没错。可是从外面验收,能验到什么程度?

先看测试。测试只能试有限个输入,而任何有限的测试,都有无穷多个程序能全部通过,它们在测试之外的行为各不相同。Dijkstra 在 1969 年说过一句很有名的话,大意是:测试能证明有 bug,不能证明没有 bug。

那就打开黑箱看代码,或者再请一个 agent 来审呢?SICP 第 4 章写完一个解释器以后,留了一道习题,让读者自己证明:不可能写出一个通用的程序,判断任意程序会不会停下来。

定理 3巫师的习题

不存在一个程序 halts?,对任意程序 p 和输入 a,都能正确回答“p 在 a 上会不会停”。

证明假设有。造一个程序 try:输入一个程序 p;如果 halts?(p, p) 说会停,就进入死循环,否则立刻停下。问 try 在输入 try 上会不会停。如果会停,按定义它该进死循环;如果不停,按定义它该立刻停下。两头都矛盾,所以 halts? 不存在。∎

这个结论的源头是图灵 1936 年的论文,“停机问题”这个名字是 1950 年代才有的。SICP 把它放成第 4 章的一道习题,用 Scheme 写五行就够了。

输入P1P2P3P4P5程序P1停∞停停∞P2∞∞停∞停P3停停停∞∞P4∞停∞停停P5停∞∞停∞try∞停∞∞停停 = halts? 说会停 ∞ = 说不停 try 的第 i 格与对角线第 i 格相反
对角线论证:把所有程序排成一张表,try 专门和对角线唱反调,它就不可能出现在表里任何一行。

1953 年,Henry Rice 把它推到了头。

定理 4Rice 定理

程序行为上的任何非平凡性质,都不可判定。“行为上”指只看程序算出什么,不看代码长什么样;“非平凡”指有的程序有这个性质,有的没有。

证明梗概设永不停机的程序没有这个性质(否则改问它的反面),再取一个有这个性质的程序 Y。给定任意 p 和 a,造一个新程序 Q:先运行 p(a),跑完以后,再照 Y 的样子工作。p(a) 会停,Q 就和 Y 行为相同,有这个性质;不会停,Q 就永不停机,没有这个性质。所以,能判定这个性质,就能判定停机问题,和定理 3 矛盾。∎

“不会泄露用户数据”“余额永远算对”“和旧系统行为一致”,都是行为上的非平凡性质。没有哪个审查者,人也好,agent 也好,能对任意代码判定它们。这个约束对谁都一样,人脑也逃不出 Rice 定理,这不是比谁更聪明的问题。

那软件是怎么做对的?靠不让代码“任意”。我们事先放进结构:类型、接口、规格、可以对照的标准答案。具体的程序常常是查得了的,难的只是“任意”的程序。讲机器证明的那一篇说过类似的事:判定一个命题难,检查一份证明容易。结构,就是程序随身带的证明。

DHH 身边就有一个好例子。Rails 社区的 Sam Ruby 今年做了一个项目,叫 Roundhouse:把一个 Rails 应用当成规格,编译成十来种语言的版本。怎么知道翻译对了?同一个网址,在原来的 Rails 和新版本上各请求一次,返回的页面要一个节点一个节点地对上。原来的应用,就是判据。

Sam Ruby 在回应 DHH 的文章里说,有了 agent,严谨并没有消失,它搬到了上游的规格和下游的测试里。我同意。黑箱可以不看,判据不能没有。

07
CONSERVATION OF BITS

比特守恒

现在可以回答标题里的问题了:英语是最好的编程语言吗?

先讲控制论里的一条定律。1956 年,Ross Ashby 提出“必要多样性定律”:要把结果控制在你想要的范围里,调节者手上的多样性,至少要抵得上外界干扰的多样性。他的说法大意是,只有多样性能对付多样性。下面这条,是它在信息论里最简单的一个版本。

定理 5比特守恒

要从 V 种可能的结果里确定一种,至少需要 log2 V 比特的信息。这些比特总得有人付:你写进去的,约定替你写的,或者模型替你猜的。

证明s 比特的消息最多只有 2s 种,最多区分 2s 种结果(鸽笼原理)。所以 s ≥ log2 V。∎

一个应用里,你在乎的区别成千上万:按钮放在哪,钱怎么四舍五入,出错时提示什么,删除能不能撤销。设其中有 u 个,你没写,约定也没覆盖,模型只能凭经验猜。每猜一个,和你想的一样的概率是 a,那么全部猜中的概率是 au。又是一个指数。

你写的约定和语义模型猜的手写代码60%40%Rails 约定 + 写清要紧处18%64%18%裸英语 vibe coding18%72%猜的部分越大,全部猜中的概率 au 掉得越快
同一个应用、同样多的区别,三种写法下比特由谁来付(示意,非实测数据)。

这条定理能解释好几件事。

第一,DHH 说,Rails 的“约定优于配置”直接带来了 token 效率。约定,就是双方事先说好的那部分比特:表名怎么对应类名,网址怎么对应控制器。说好了,就不用写,也不会猜错。vibe coding 也在用先验,只不过是没说好的先验。

第二,Matz 在对谈里说,很多人 vibe coding 一碰真实应用就失败,Ruby 核心团队的人试下来,成功率却高得出奇。他的解释是:老手知道怎么给 agent 定目标。用比特守恒的话说,老手知道哪些区别要紧,就把那几个比特亲手写下来。

第三,英语。英语的表达力确实最强,因为它背后有人类共享的巨大先验,一句话能调动很多比特。可这份先验,模型和你并不完全共享。DHH 承认英语更含糊,含糊的地方,就是 u。

所以我的回答是:英语是最好的前端语言,不是最好的事实来源。用英语说清你要什么,再好不过。可事实来源要有一套事先说好的语义,规定每一句话是什么意思。这正是编程语言的本分,也是 SICP 最关心的事。

Dijkstra 在 1978 年写过一篇短文,专门批评“用自然语言编程”。大意是:形式记号窄,正是它的好处,它逼你把话说死;自然语言让你说得轻松,也让你轻易说错。

λPART THREE巫师SICP 怎么读
08
ABSTRACTION MOVES

抽象搬家了

SICP 的外号叫“巫师书”,因为封面上画着一个巫师,手里捧着一个球,球上写着 eval 和 apply,那是解释器的两个核心过程。这个外号很贴切,整本书都在教你造咒语,也就是造语言。

回头看 DHH 的做法,会发现一件有意思的事:他嘴上说抽象过时了,手上处处是抽象。

?
黑箱Rust 一行不看,只从外面验收。黑箱的边界,就是 SICP 第 2 章的抽象屏障。
>_
CLI每个应用都该提供命令行,让用户自带 agent。这是给 agent 用的接口。
阅读视图让 agent 用 Ruby 给他讲 Rust 代码。给人读的版本和给机器跑的版本分开了。
约定约定优于配置,省 token。约定是事先说好的先验。
规格和判据对谈里提到的 Roundhouse,把 Rails 应用当规格,目标语言只是一个编译选项。
演讲和对谈里提到的五个做法,每一个都是一种抽象。

抽象没有消失,它搬了家:从代码里面,搬到了盒子之间。以前的抽象是函数、类和模块;现在是黑箱的边界、给 agent 用的接口、用英语或 Rails 写下的规格,以及判断对错的判据。推论 1.1 说模块会变粗,这里看到的就是变粗了的模块。

2005抽象住在代码里面一个 Rails 应用模块类类模块类类模块类类2026抽象住在盒子之间英语我要什么规格和判据RustSwiftKotlinCLI 接口阅读视图:用 Ruby 讲解朱红 = 抽象所在的位置
抽象搬家。2005 年,抽象住在代码里面;2026 年,它住在盒子之间。

SICP 1.1 节开头说,每一门强大的语言都有三样东西:基本元素,组合的手段,抽象的手段。放到今天,这三样在新的一层又出现了一遍。基本元素是一个个由 agent 完成的任务;组合的手段,是怎样把这些任务编排起来;抽象的手段,是给一堆任务划边界、起名字、定判据。

SICP 第 4 章讲“元语言抽象”。对付复杂问题,一个强大的办法是造一门新语言;而决定这门语言里每句话什么意思的求值器,本身也只是一个程序。Rails 就是一门藏在 Ruby 里的小语言。DHH 说英语是更好的编程语言,问的其实也是第 4 章的问题:这门语言的求值器是谁?它的语义写在哪里?

英语的求值器是一个模型,语义是模型的先验。Roundhouse 的做法,是给这个求值器配一份标准答案:用 Rails 写下说好的语义,让模型的翻译去对答案。

Rails 应用(规格)带类型的中间表示RustSwiftTypeScriptCrystalGoElixirKotlinC#Python浏览器包Ruby(Spinel)判据:同一个网址,原应用和每个目标各请求一次,返回必须一样
一份结构,多种解释。Roundhouse 把 Rails 应用编译到十来种目标(这里按项目说明列出 11 种),每个目标都要和原应用对答案。

这正是书名里的两个词。Structure,是那份事先说好的结构;Interpretation,是同一份结构的多种解释:给人读的,给机器跑的,换成另一种语言的。

09
HOW TO READ SICP

今天怎么读 SICP

MIT 用 SICP 开的入门课 6.001,2007 年秋天上完了最后一个学期。2009 年,Sussman 在国际 Lisp 大会上解释过为什么换课。据 Andy Wingo 的转述,他的意思是:1980 年的工程师,用自己完全理解的小零件搭系统;如今的工程师,得对着文档不全的库做实验,看它怎么反应。

1985
SICP 出版。程序员用自己完全理解的零件搭程序,书里的求值器要亲手写出来。
2009
Sussman 解释 MIT 为什么换课:如今的工程师得对着文档不全的库做实验,看它怎么反应。(据 Wingo 转述)
2026
DHH:自己项目里的 Rust 一行不看,只从外面验收。
理解的退潮:程序员对自己运行的代码,懂得越来越少。

DHH 的黑箱是这条路的尽头:连自己项目里的代码,也只从外面试。照这个趋势,SICP 似乎更没用了。

用四本账再看一遍,结论就不同了。SICP 里有些东西付的是人脑账:亲手写出每一个过程,熟练掌握 Scheme 的写法。这些会贬值,可以交给 agent。另一些付的是数学账,反而更值钱了。

精读付数学账 通读可以让 agent 多代劳
1
构造过程抽象
1.1 代换模型1.2 过程与增长的阶1.3 高阶过程
2
构造数据抽象
2.1 抽象屏障2.2 层次数据与分层设计2.3 符号数据2.4–2.5 通用操作
3
模块化、对象和状态
3.1–3.2 赋值与环境模型3.3 可变数据3.4 并发3.5 流
4
元语言抽象
4.1 元循环求值器习题 4.154.2–4.4 语言的变体
5
寄存器机器里的计算
5.1–5.5 机器、存储与编译
SICP 五章,按付哪本账上色(作者的判断)。朱红是要精读的数学账。
✎ 今天值得精读的五处
  1. 1.1 代换模型。不运行,就推出程序算什么。审 agent 写的代码,先在脑子里跑一遍。
  2. 1.2 过程的形状和增长的阶。一个月 15 万行代码,哪里会慢到天荒地老,要一眼看出来。
  3. 2.1 抽象屏障、2.2.4 分层设计。Hora 的组件从哪里来。
  4. 3.1–3.4 状态、时间和并发。一千个 agent 同时动手时,那些交错从哪里来。
  5. 4.1 求值器和习题 4.15。语言是什么,语义是什么,黑箱验收的边界在哪里。

读法也要变。以前做习题是为了练手;今天可以让 agent 帮你写,但写之前先自己预测它会输出什么,写完再让它给你举反例。把 agent 当助教,别当代笔。

Matz 在对谈里说,老手为什么能把 vibe coding 用好,这种本事得想办法形式化,教给下一代。SICP 就是四十年前的一次尝试:把老手心里知道、嘴上说不清的东西,写成一本书。

10
CODA

写在最后

DHH 的演讲里,我最喜欢的是一张老照片。Tuxen 画了一辈子肖像,晚年坐到相机前,让新技术给自己留了影。

后来我查到一个细节。Tuxen 画那幅王室群像,本来就是照着照片和分组写生拼出来的。相机没有先抢走他的活,而是先成了他的工具:照片管逼真,构图和取舍归他。

照相术到底怎样影响了十九世纪的绘画,艺术史家至今还在争,这里不展开。至少在 Tuxen 身上,相机拿走的是逼真这门苦工,没拿走画家的眼睛。

agent 拿走的,是敲代码这门苦工。没拿走的,是钟表匠的分层,是只存一份的事实,是事先说好的语义和判据。SICP 一直讲的是后面这些。只是它用 Scheme 来讲,我们以前误以为它在教 Scheme。

“相机拿走了逼真,没拿走眼睛。
agent 拿走了打字,没拿走结构。
✎ 留言区聊聊

你写过、守过的编程规矩里,哪一条付的是数学账?哪一条其实只是价格?

计算口径
  • 定理 1 和推论 1.1:每步独立地以概率 p 被打断,打断后当前组件从头再来;已经装好的组件不受影响。曲线按 T(k) 的精确公式计算,N = 1,000,000,组件大小取整数。
  • 定理 2:各处改对与否相互独立。真实的错误往往相关,这时结论的方向不变,具体数字会变。
  • 定理 5 的图:三种写法的比特分配是示意,不是实测。
  • 视频里的数字:举手人数、代码行数、CPU 下降比例等,都是 DHH 在演讲里自己说的,本文没有独立核实。
  • 文中所有数字都用脚本按上面的公式算出,并做过自检。
参考资料
  1. Abelson, H. & Sussman, G. J., with Sussman, J., Structure and Interpretation of Computer Programs, MIT Press, 1985;第二版 1996
  2. DHH,Rails World 2026 开场演讲(2026 年 9 月,YouTube)
  3. Matz、DHH、Jeremy Daer,Rails World 2026 对谈(2026 年 9 月,YouTube)
  4. Sam Ruby,Partly in the Right(2026 年 9 月,intertwingly.net);Roundhouse 项目(GitHub)
  5. Simon, H. A. (1962), The Architecture of Complexity, Proceedings of the American Philosophical Society 106(6)
  6. Conway, M. E. (1968), How Do Committees Invent?, Datamation
  7. Codd, E. F. (1970), A Relational Model of Data for Large Shared Data Banks, CACM 13(6);(1971), Further Normalization of the Data Base Relational Model
  8. Turing, A. M. (1936), On Computable Numbers; Rice, H. G. (1953), Trans. AMS 74
  9. Ashby, W. R. (1956), An Introduction to Cybernetics
  10. Dijkstra, E. W., Notes on Structured Programming (EWD249);On the foolishness of "natural language programming" (EWD667)
  11. Andy Wingo, international lisp conference -- day two(2009 年 3 月 24 日,wingolog.org)
注:文中插图均为本文自制示意。视频内容为转述,唯一的直接引语是 DHH 关于英语的那句话。
犹剑在心
北极甜虾(剑心犹在!)
— 全文完 —
Translated from the Chinese original, “英语是最好的编程语言吗?画家、钟表匠和巫师”.
λ
ESSAYProgramming · Software Engineering
Rereading SICP once code is no longer written by hand

Is English the Best
Programming Language?

The Painter, the Watchmakers, and the Wizard
In September 2026, DHH, the creator of Ruby on Rails, announced from the stage that his company no longer writes code by hand, and that he had found a programming language better than Ruby: English. He also said that abstraction has to be repriced. This essay puts his claims to an old book from 1985, Structure and Interpretation of Computer Programs (SICP). The answer comes in three parts: the painter on what AI has changed, the watchmakers on what it hasn't (with five theorems), and the wizard on how to read SICP today. You will come away with a small tool: when you hear that something “is obsolete in the age of AI,” first ask which ledger it is charged to.
Painterwhat changedWatchmakerwhat stayedλWizardreading SICP
The essay's three roles. The painter covers what has changed, the watchmakers what hasn't, and the wizard SICP.

September 2026, Austin, the Rails World conference. In his opening keynote, DHH (David Heinemeier Hansson), the creator of Ruby on Rails, asked the audience: who here still writes a lot of code by hand every week? By his account, only about five people raised their hands.

He himself had not written code by hand for months. His company, 37signals (the people behind Basecamp and the HEY email service), had just decided to “put down the pencil”: from now on, writing code by hand counts as an anomaly, like an error popping up in monitoring, something to trace back to find out what went wrong.

Then he said he had found a programming language better than Ruby:

“English is a better programming language than Ruby.”DHH, opening keynote, Rails World 2026

He admitted that English is a bit vaguer, and its results less deterministic. But writing programs in English, he said, is a pure joy.

Coming from DHH, this carries extra weight. Rails was built on the elegance of Ruby, and his love of Ruby is famous.

Now go back forty-one years. In 1985, Abelson and Sussman of MIT published a book called Structure and Interpretation of Computer Programs, SICP for short. The preface to the first edition contains a famous line: “programs must be written for people to read, and only incidentally for machines to execute.” The whole book is about one thing: how to tame complexity with abstraction.

In the same keynote, DHH said that in the age of agents, abstraction no longer makes as much sense. If he is right, SICP belongs in a museum.

My view is that DHH is half right. To tell which half, we need three roles to take the stage: a painter, two watchmakers, and a wizard.

PART ONEPainter    what AI has changed
01
THE PAINTER

A portrait three years in the making

DHH's keynote began with a painting.

From 1883 to 1886, the Danish painter Laurits Tuxen worked on a large group portrait of King Christian IX and his family, three years in all. Today it hangs in Christiansborg Palace in Copenhagen. According to DHH, Tuxen was his great-great-grandfather.

In February 1900, Kodak launched the Brownie camera, at one dollar apiece. Capturing an ordinary person's likeness suddenly became something anyone could afford. As DHH tells it, from then on painting people true to life was no longer a good business, and painters had to find another way to make a living.

DHH compared the release of a coding model in November 2025 to the programmer's Brownie camera. On stage he reeled off a long list of what has happened at 37signals since then. All of what follows is by his account:

6native appsHEY is no longer web-only
99%less CPU usageHEY's backend rewritten in Rust
150,000lines of code in a monthpreviously about 30,000 a year

A small team used to be unable to afford native code for six platforms, so it settled for the web; now it can afford it. He hates the way Rust looks, but since he doesn't have to look at the code, that no longer matters. This August he “wrote” 150,000 lines of code, most of it Rust he has never read.

In a separate conversation, Yukihiro Matsumoto (Matz), the creator of Ruby, joked that he has been a vibe coder for fifteen years already. As the head of the Ruby project, he says what he wants, the core committers write the code, and he reviews and merges it. All that has changed is that the “organic intelligence” has been swapped for agents.

These are real changes, not gimmicks. In the next section, I'll make DHH's case as strongly as I can.

02
PRICES, NOT TRUTHS

Many rules are just prices

Programmers live by many rules that sound like truths but are really prices.

Why does a small team build for the web rather than native? Because six native codebases need six crews to maintain them, and it can't afford that. Why use Ruby rather than Rust? Because an hour of a programmer's time costs far more than an hour of a machine's, so it is better to let the machine run longer and have people write less. Why does software keep getting more bloated? Because spending engineers' time on trimming it down isn't worth the cost.

Every one of these rules is charged to the money ledger. Labor is expensive and machines are cheap, so that is how the sums come out. Agents have cut the labor cost of writing code by orders of magnitude, so the sums have to be redone. DHH had an agent write an app for making presentations, and its binary is only half a megabyte. In the past, nobody would have paid the wages to save that half megabyte.

Another class of rules is charged to the organization ledger. In 1968, Melvin Conway observed that the structure of a system grows to mirror the communication structure of the organization that designs it. This later became known as Conway's law. Many module boundaries are really team boundaries. When a team turns into one person plus a thousand agents, the boundaries naturally have to be redrawn. DHH says that when thousands of agents are changing one application at once, abstractions become choke points. By Conway's law, that makes sense.

The third class is charged to the brain ledger. Human working memory holds only a few things at a time, so functions should be short, names should be good, and code should fit on one screen. In the conversation, DHH went further: the past sixty years of software engineering have all been about coping with the limits of human cognition, and once those limits are gone, these methods are no longer needed.

On these three, I broadly agree. The trouble comes at the last step: are all rules charged only to these three ledgers?

03
FOUR LEDGERS

Four ledgers

I want to add a fourth: the math ledger.

B
Brain ledgerHuman attention and memory are limited. Short functions, good names, code that fits on one screen: these are charged here. After AI: code is no longer read line by line by people, so this ledger shrinks a lot.
O
Organization ledgerHow teams divide up work and talk to each other. Module boundaries are often team boundaries. After AI: the organization changes, and the boundaries have to be redrawn.
$
Money ledgerThe relative prices of labor and machines. Web rather than native, Ruby rather than Rust, not bothering to trim size: these are charged here. After AI: the labor of writing code is almost free, and every trade-off is recalculated.
M
Math ledgerErrors accumulate, combinations explode, and some questions are simply undecidable. After AI: not a cent less.
Four ledgers. AI has repriced the first three; the fourth is the same for everyone.

The math ledger is different from the other three: it constrains any reasoner that makes mistakes and has limited computing power. That holds for people, and it holds for agents.

The test fits in one sentence: when you hear that something “is obsolete in the age of AI,” first ask which ledger the rule is charged to. Rules charged to the first three may well become obsolete; rules charged to the fourth can't be escaped, no matter who writes the code.

BrainOrg$MathAfter AIOne-screen functionsFadesSmall teams: web onlyFadesRuby, not RustFadesModules by teamShiftsLine-by-line reviewMorphsLayers and modulesCoarserOne fact, one placeStaysInterfaces, types, testsStaysLocks for concurrencyStays
Which ledgers nine common rules are charged to (my own judgment). Rules charged only to the first three may lose value after AI; those that touch the math ledger stay.

Let's try it on DHH's most radical claim. He said one reason for abstraction is to avoid repetition; now repetition is almost free, and keeping the copies in sync is just as cheap.

Which ledger is “don't repeat yourself” charged to? Typing less is the money ledger, and on that half he is right. But repetition leaves another entry on the books: when the same thing lives in a thousand places, every change has to be made correctly a thousand times. That entry belongs to the math ledger.

Enter the watchmakers.

PART TWOWatchmaker          what didn't change

This part has five theorems, each with a proof. You can skip the proofs; the statements and the figures are enough.

04
TWO WATCHMAKERS

Two watchmakers

In 1962, Herbert Simon wrote a paper called “The Architecture of Complexity.” He later won the Turing Award, and then the Nobel Prize in economics. The paper contains a parable.

Two watchmakers, Hora and Tempus, make watches with a thousand parts each. They are equally skilled, equally successful, and get equally many phone calls. Every time a call comes in, whatever unfinished piece is in their hands falls apart.

Tempus builds a watch one part at a time, and every call wipes out all his work. Hora first assembles ten parts into a small component, ten small components into a large component, and ten large components into a watch. When a call comes in, he loses only the component in his hands.

Simon supposed that each time a part is added, there is a 1% chance that a call interrupts. How big is the gap between the two? His own rough estimate was that Tempus takes about 4,000 times as long as Hora. Below, a theorem pins it down exactly.

Theorem 1The watchmaker theorem

A piece of work takes k consecutive steps. Each step is interrupted independently with probability p, and an interruption sends you back to the start. Writing q = 1 − p, the expected number of steps to finish is

T(k) = q−k − 1p

ProofFirst finish the first k − 1 steps, which takes T(k − 1) steps in expectation. Then do step k: it costs 1 step and succeeds with probability q; with probability p it is interrupted, and you start over, needing another T(k) steps. So T(k) = T(k − 1) + 1 + pT(k), that is, T(k) = (T(k − 1) + 1)/q. Unrolling the recursion from T(0) = 0 and substituting confirms the formula.∎

Now plug in the numbers. Tempus has to do 1,000 steps in one go, which takes T(1000) ≈ 2.316 million steps in expectation. Hora has to assemble 111 components (100 small ones, 10 large ones, and the watch itself), 10 steps each, for a total of 111 × T(10) ≈ 1,174 steps. Tempus takes 1,974 times as many steps as Hora. That is about half of Simon's figure, because he used a rough method of estimation; Simon himself said the number shouldn't be taken too seriously. The gap of three orders of magnitude stands.

Tempusone by one, all 1,000 parts in one goA call! All lostavg2.316M stepsHoragroups of ten, three levels1 watchlarge ×10small ×10parts ×10avg1,174 stepsabout 1,974× fewer
A thousand parts, two ways to put them together. Adding each part carries a 1% chance of interruption; the expected step counts come from Theorem 1.

Simon used this parable to explain why nearly all complex systems in nature are hierarchical: atoms, molecules, cells, organs. The reason is not that the Creator's “brain” isn't big enough, but that complex things can only be built, and only last, when there are stable intermediate forms.

Notice that the human brain plays no part here. Tempus and Hora are equally clever and have equally good memories. The difference comes from one thing only: they get interrupted, and an interruption loses progress. Agents make mistakes too.

Corollary 1.1The optimal module

A piece of work has N steps in all, with b steps per component. When p is small and N is large, the total expected number of steps is about

N(1 + 1b + pb2)

This is smallest at b ≈ √(2/p), where it is about N(1 + √(2p)). Not layering at all (b = N) pays off only when the whole job is no bigger than about two optimal modules, that is, when N ≲ 2√(2/p).

ProofThe number of components is (N − 1)/(b − 1). By Theorem 1, when p is small, T(b) ≈ b + b(b + 1)p/2. Multiplying the two gives about N(1 + 1/b + pb/2) when b and N are both fairly large. Differentiating with respect to b gives 1/b2 = p/2, so b = √(2/p), and substituting back gives the minimum. Without layering, the expected number of steps is about N + N2p/2, which is less than N(1 + √(2p)) if and only if N < 2√(2/p).∎

This corollary answers DHH head-on. As agents get more reliable, p gets smaller, and the optimal module gets larger:

1.01.21.41.62101001000average steps per partcomponent size (steps, log scale)1445142p = 1%0.1%0.01%
For a job of one million steps, how many steps each step really costs on average, by component size (computed exactly from Theorem 1). At the three error rates, the minimum falls at 14, 45, and 142 steps.

Cut the error rate from 1% to 0.01%, making agents 100 times more reliable, and the optimal module grows from 14 steps to 142, ten times coarser. Abstraction gets coarser; it doesn't disappear. For no layering to be optimal, the whole job has to be as small as two modules. Computed exactly, at a 1% error rate, the point beyond which even a single layer of components beats no layering is around 27 steps. A real software system is nowhere near that small.

Of course, this is a toy model. Real errors aren't independent, and they don't always send you back to the start; nor is there a clear unit for a “step” in code. But the model captures something real: what a stable intermediate form is worth depends on the error rate and on how long the job is, not on whether a person or a machine does the work.

DHH told a story of his own. In the spring of 2026, 37signals had a few designers use vibe coding to add the last few features to Basecamp 5. By his account, every PR looked fine on its own, but put twenty or thirty of them together and the architecture looked like Swiss cheese, full of holes. His conclusion afterward: it had been the wrong call at the time; wait a few more months for stronger models, and it would be fine.

Stronger models will indeed make p smaller. But the trouble with Swiss cheese isn't that some step went wrong; it is that nothing stable sits between those twenty or thirty steps to hold them together. Hora's components are exactly that intermediate form.

05
A THOUSAND COPIES

A thousand copies

Back to DHH's line: repetition is free now, and syncing is cheap too.

Imagine a rule, say “members get 20% off,” written down in n places. Every time the price changes, an agent has to update all n places correctly. It is very reliable: it gets each place right with probability r.

Theorem 2The copy theorem

The probability that all n places are updated correctly is rn, and

rn ≤ e−n(1 − r)

ProofThe places are independent, so the probabilities multiply to rn. Since ln r ≤ r − 1, we have n ln r ≤ −n(1 − r); exponentiate both sides.∎

Take r = 99.9%, which is already more careful than most people. With n = 1000, the chance of getting everything right is only 36.8%; with n = 5000, it is just 0.67%.

0%25%50%75%100%110100100010kchance one change gets every copy rightcopies of the same rule (log scale)36.8%per copy, r = 99%99.9%99.99%
The chance that one change updates all n copies of a rule correctly. The three lines are for different probabilities r of getting each copy right.

The key is the product n(1 − r). “Syncing is cheap” makes 1 − r smaller; “repetition is free” makes n larger. As long as n grows faster than reliability improves, the chance of getting everything right still falls toward zero. Keep only one copy, and the problem disappears: consistency is no longer a matter of probability kept up by carefulness, but a certainty guaranteed by structure.

Databases figured this out more than fifty years ago. In 1970, Codd at IBM proposed the relational model, which later grew the “normal forms”: store each fact in one place only, so that you never change it here and forget it there. In a database, the updates are carried out by machines to begin with, and machines are very reliable, yet databases still insist on normal forms. Engineers often store redundant copies on purpose to make queries faster, but that is a trade-off with a price tag attached: the extra syncing needs a dedicated mechanism in charge of it.

The other half of what DHH said also runs into the math ledger. He said that with thousands of agents changing one application at once, abstractions become choke points. Why do choke points exist? Chapter 3 of SICP deals with exactly this.

Corollary 2.1Counting interleavings

When two processes of m and n steps run at the same time, their steps can be interleaved in C(m + n, m) different orders.

ProofThe order within each process is fixed. An interleaving amounts to choosing m of the m + n positions for the first process.∎

A takes a step →B takes a step →startendC(6, 3) = 20 pathsA finishes firsttaking turns2 × 3 steps202 × 10 steps184,7562 × 50 steps≈ 1029
Two processes of 3 steps each. Every interleaving is a path through the grid, 20 in all.

Two processes of 10 steps each have 184,756 interleavings; with 50 steps each, about 1.0 × 1029. Each of them may compute a different result. Section 3.4 of SICP has an exercise (Exercise 3.38): an account starts with $100; Peter deposits $10, Paul withdraws $20, and Mary withdraws half of what is there. If the three take turns, the balance can end up at $35, $40, $45, or $50; if they act at the same time, with their steps interleaved, still other numbers turn up.

Locks, queues, and modules with a single way in and out are all ways of squeezing the interleavings back into one order. They do slow down parallel work; on that, DHH is right. But take them away, and what you have to verify is no longer one path but an astronomical number of them. Again, this is not a problem of the human brain: agents have to face those paths too.

06
THE BLACK BOX

The limits of black-box checking

DHH also said he doesn't read a single line of the Rust. He treats it as a black box and checks it from the outside, just like every boss in history who has paid programmers to do the work.

Fair enough. But how far can checking from the outside go?

Start with testing. Tests can only try finitely many inputs, and for any finite set of tests, infinitely many programs pass them all while behaving differently everywhere else. Dijkstra put it in a famous remark in 1969: roughly, testing can show the presence of bugs, but never their absence.

Then why not open the black box and read the code, or have another agent review it? After building an interpreter in Chapter 4, SICP leaves an exercise asking readers to prove for themselves that no general program can decide whether an arbitrary program will halt.

Theorem 3The wizard's exercise

There is no program halts? that, for every program p and input a, correctly answers “does p halt on a?”

ProofSuppose there is one. Build a program try that takes a program p as input: if halts?(p, p) says it halts, it goes into an infinite loop; otherwise it halts at once. Now ask whether try halts on the input try. If it halts, then by definition it should loop forever; if it doesn't, then by definition it should halt at once. Either way we have a contradiction, so halts? does not exist.∎

The result goes back to Turing's paper of 1936; the name “halting problem” only appeared in the 1950s. SICP turns it into an exercise in Chapter 4, and five lines of Scheme are enough.

inputP1P2P3P4P5programP1halt∞halthalt∞P2∞∞halt∞haltP3halthalthalt∞∞P4∞halt∞halthaltP5halt∞∞halt∞try∞halt∞∞halthalt = halts? says yes · ∞ = says no · try flips the diagonal
The diagonal argument: list all programs in a table. try deliberately contradicts the diagonal, so it cannot be any row of the table.

In 1953, Henry Rice pushed this as far as it goes.

Theorem 4Rice's theorem

Every nontrivial behavioral property of programs is undecidable. “Behavioral” means it depends only on what a program computes, not on what its code looks like; “nontrivial” means some programs have the property and some don't.

Proof sketchAssume the program that never halts lacks the property (otherwise ask about its negation), and take a program Y that has it. Given any p and a, build a new program Q: it first runs p(a), and once that finishes, it behaves the way Y does. If p(a) halts, Q behaves exactly like Y and has the property; if not, Q never halts and lacks it. So a way to decide the property would decide the halting problem, contradicting Theorem 3.∎

“Never leaks user data,” “always gets the balance right,” “behaves the same as the old system”: these are all nontrivial behavioral properties. No reviewer, human or agent, can decide them for arbitrary code. The constraint is the same for everyone; the human brain can't escape Rice's theorem either. This is not a question of who is cleverer.

So how does software get built right? By not letting the code be “arbitrary.” We put structure in beforehand: types, interfaces, specifications, reference answers to check against. Particular programs can often be checked; only “arbitrary” programs are hard. My essay on machine proof made a similar point: deciding whether a statement is true is hard, but checking a proof is easy. Structure is the proof a program carries with it.

DHH has a good example close at hand. This year Sam Ruby, of the Rails community, built a project called Roundhouse: it takes a Rails application as the specification and compiles it into versions in a dozen or so languages. How do you know the translation is right? Request the same URL once from the original Rails app and once from the new version, and the pages that come back must match node by node. The original application is the criterion.

In a post responding to DHH, Sam Ruby wrote that with agents, rigor hasn't disappeared; it has moved upstream into specifications and downstream into tests. I agree. You may leave the black box unread, but you can't do without a criterion.

07
CONSERVATION OF BITS

Conservation of bits

Now we can answer the question in the title: is English the best programming language?

First, a law from cybernetics. In 1956, Ross Ashby proposed the “law of requisite variety”: to keep outcomes within the range you want, the variety a regulator has at hand must be at least as great as the variety of the disturbances. In his own words, “only variety can destroy variety.” What follows is the simplest version of it in information theory.

Theorem 5Conservation of bits

To single out one outcome among V possible ones takes at least log2 V bits of information. Someone has to pay for those bits: you write them in, conventions write them for you, or the model guesses them for you.

ProofThere are at most 2s messages of s bits, so they can tell apart at most 2s outcomes (the pigeonhole principle). Hence s ≥ log2 V.∎

An application involves thousands of distinctions you care about: where a button goes, how money is rounded, what an error message says, whether a deletion can be undone. Suppose u of them were neither written down by you nor covered by conventions, so the model can only guess them from experience. If each guess matches what you had in mind with probability a, the chance that every guess is right is au. Another exponential.

youconventionsmodel's guesseshandwritten code60%40%Rails conventions + key points spelled out18%64%18%vibe coding in bare English18%72%The more the model guesses, the faster au (all correct) falls
The same application with the same number of distinctions: who pays for the bits under three ways of writing it (illustrative, not measured).

This theorem explains several things.

First, DHH says Rails's “convention over configuration” translates directly into token efficiency. Conventions are the bits both sides have agreed on in advance: how table names map to class names, how URLs map to controllers. Once agreed, they need not be written down, and they can't be guessed wrong. Vibe coding relies on priors too; they are just priors nobody agreed on.

Second, Matz said in the conversation that many people's vibe coding falls apart the moment it meets a real application, yet when members of the Ruby core team tried it, their success rate was surprisingly high. His explanation: veterans know how to set goals for an agent. In terms of conservation of bits, veterans know which distinctions matter, and they write those few bits down by hand.

Third, English. English really does have the most expressive power, because behind it lies a huge prior that humans share, so a single sentence can call up a great many bits. But the model doesn't fully share that prior with you. DHH admits English is vaguer, and wherever it is vague, that is u.

So my answer is this: English is the best front-end language, not the best source of truth. For saying clearly what you want, nothing beats English. But a source of truth needs semantics agreed in advance, fixing what each sentence means. That is exactly the job of a programming language, and exactly what SICP cares about most.

In 1978, Dijkstra wrote a short essay attacking “natural language programming.” The gist: the narrowness of formal notation is precisely its virtue, because it forces you to pin down exactly what you mean; natural language makes it easy to say things, and just as easy to say them wrong.

λPART THREEWizard    how to read SICP
08
ABSTRACTION MOVES

Abstraction has moved house

SICP is nicknamed the “Wizard Book,” because its cover shows a wizard holding a ball inscribed with eval and apply, the two core procedures of an interpreter. The nickname fits: the whole book teaches you to make spells, which is to say, to make languages.

Look back at what DHH actually does, and you'll notice something interesting: while he says abstraction is obsolete, his hands are full of abstractions.

?
Black boxDoesn't read a line of the Rust, only checks it from the outside. The edge of the black box is the abstraction barrier of SICP Chapter 2.
>_
CLIEvery app should offer a command line, so users can bring their own agents. That is an interface for agents.
Reading viewHas an agent explain the Rust code to him in Ruby. The version for people to read and the version for machines to run have come apart.
ConventionsConvention over configuration saves tokens. Conventions are priors agreed in advance.
Specs and criteriaRoundhouse, mentioned in the conversation, takes a Rails application as the spec; the target language is just a compiler option.
Five practices mentioned in the keynote and the conversation. Each one is a form of abstraction.

Abstraction hasn't disappeared; it has moved house, from inside the code to between the boxes. Abstraction used to mean functions, classes, and modules; now it means the edges of black boxes, interfaces for agents, specs written in English or in Rails, and criteria that decide right from wrong. Corollary 1.1 said modules would get coarser, and these are what the coarser modules look like.

2005abstraction lives inside the codea Rails appmoduleclassclassmoduleclassclassmoduleclassclass2026abstraction lives between the boxesEnglishwhat I wantSpec& criteriaRustSwiftKotlinCLIreading view in Rubyvermilion = where abstraction lives
Abstraction moves house. In 2005 it lived inside the code; in 2026 it lives between the boxes.

Section 1.1 of SICP opens by saying that every powerful language has three mechanisms: primitive expressions, means of combination, and means of abstraction. Today all three appear again, one level up. The primitives are individual tasks carried out by agents; the means of combination is how those tasks are orchestrated; the means of abstraction is drawing boundaries around groups of tasks, giving them names, and setting their criteria.

Chapter 4 of SICP is about “metalinguistic abstraction.” One powerful way to deal with a complex problem is to create a new language; and the evaluator, which determines what each expression in that language means, is itself just another program. Rails is a small language hidden inside Ruby. When DHH says English is a better programming language, he is really asking a Chapter 4 question: who is this language's evaluator? Where are its semantics written down?

The evaluator of English is a model, and its semantics are the model's priors. What Roundhouse does is give this evaluator an answer key: write the agreed semantics down in Rails, and check the model's translations against it.

Rails app(the spec)typedIRRustSwiftTypeScriptCrystalGoElixirKotlinC#Pythonbrowser bundleRuby (Spinel)Criterion: every target answers each URL as the original does
One structure, many interpretations. Roundhouse compiles a Rails application to a dozen or so targets (11 are shown here, as listed in the project's description), and each target has to give the same answers as the original.

These are exactly the two words in the book's title. Structure is the structure agreed on in advance; Interpretation is the many interpretations of that one structure: one for people to read, one for machines to run, one in another language.

09
HOW TO READ SICP

How to read SICP today

6.001, the introductory course MIT taught from SICP, ran for the last time in the fall of 2007. In 2009, at the International Lisp Conference, Sussman explained why the course had been replaced. As Andy Wingo reported it, his point was that engineers in 1980 built systems out of small parts they understood completely, whereas engineers today have to run experiments on poorly documented libraries to see how they behave.

1985
SICP is published. Programmers build programs from parts they fully understand, and the book's evaluator is something you write yourself.
2009
Sussman explains why MIT replaced the course: today's engineers have to run experiments on poorly documented libraries to see how they behave. (As reported by Wingo)
2026
DHH: doesn't read a single line of the Rust in his own projects, only checks it from the outside.
The ebb of understanding: programmers understand less and less of the code they run.

DHH's black box is the end of this road: even the code in his own projects he only tries out from the outside. On this trend, SICP looks even less useful.

Look again through the four ledgers, and the conclusion changes. Some of what SICP teaches is charged to the brain ledger: writing out every procedure by hand, becoming fluent in the idioms of Scheme. That will lose value and can be handed over to agents. The rest is charged to the math ledger, and it is worth more than ever.

Read closelycharged to the math ledger Read throughagents can take on more
1
Building Abstractions with Procedures
1.1 The substitution model1.2 Procedures and orders of growth1.3 Higher-order procedures
2
Building Abstractions with Data
2.1 Abstraction barriers2.2 Hierarchical data and stratified design2.3 Symbolic data2.4–2.5 Generic operations
3
Modularity, Objects, and State
3.1–3.2 Assignment and the environment model3.3 Mutable data3.4 Concurrency3.5 Streams
4
Metalinguistic Abstraction
4.1 The metacircular evaluatorExercise 4.154.2–4.4 Variations on the language
5
Computing with Register Machines
5.1–5.5 Machines, storage, and compilation
SICP's five chapters, colored by the ledger each part is charged to (my own judgment). Vermilion marks the math ledger, worth reading closely.
✎ Five places worth reading closely today
  1. 1.1 The substitution model. Work out what a program computes without running it. When you review code an agent wrote, run it in your head first.
  2. 1.2 The shapes of processes and orders of growth. With 150,000 lines of code a month, you need to spot at a glance where something will be slow until the end of time.
  3. 2.1 Abstraction barriers, 2.2.4 stratified design. Where Hora's components come from.
  4. 3.1–3.4 State, time, and concurrency. Where all those interleavings come from when a thousand agents set to work at once.
  5. 4.1 The evaluator and Exercise 4.15. What a language is, what semantics are, and where black-box checking reaches its limits.

The way of reading has to change too. Exercises used to be for practice. Today you can have an agent write them for you, but before it does, predict for yourself what it will output, and afterward ask it for counterexamples. Treat the agent as a teaching assistant, not a ghostwriter.

Matz said in the conversation that the knack veterans have for making vibe coding work needs to be formalized somehow and taught to the next generation. SICP was such an attempt forty years ago: a book that writes down what veterans know in their bones but can't quite put into words.

10
CODA

In closing

My favorite moment in DHH's keynote was an old photograph. Tuxen, who painted portraits all his life, sat in front of a camera in his later years and let the new technology take his picture.

Later I came across a detail. Tuxen's royal group portrait was itself pieced together from photographs and sketches of the sitters made in small groups. The camera did not take his work away first; it became his tool first. The photographs took care of likeness, while composition and choices stayed with him.

Just how photography affected nineteenth-century painting is still debated by art historians, and I won't go into it here. In Tuxen's case, at least, what the camera took away was the drudgery of likeness, not the painter's eye.

What agents take away is the drudgery of typing out code. What they don't take away is the watchmaker's layering, the fact stored in one place only, and the semantics and criteria agreed on in advance. These are what SICP has been about all along. It just used Scheme to talk about them, and we used to mistake it for a book that teaches Scheme.

“The camera took likeness, not the eye.
Agents take the typing, not the structure.
✎ Let's talk in the comments

Of the programming rules you have written or kept, which one is charged to the math ledger? And which is really just a price?

How the numbers were computed
  • Theorem 1 and Corollary 1.1: each step is interrupted independently with probability p; an interruption restarts the current component, and finished components are unaffected. The curves use the exact formula for T(k), with N = 1,000,000 and whole-number component sizes.
  • Theorem 2: whether each place is updated correctly is independent of the others. Real errors are often correlated; then the direction of the conclusion holds, but the specific numbers change.
  • Figure for Theorem 5: the split of bits among the three ways of writing is illustrative, not measured.
  • Numbers from the videos: the show of hands, the lines of code, the drop in CPU usage and so on are all as DHH stated them in his keynote; this essay has not verified them independently.
  • Every number in the essay was computed by a script from the formulas above and double-checked.
References
  1. Abelson, H. & Sussman, G. J., with Sussman, J., Structure and Interpretation of Computer Programs, MIT Press, 1985; 2nd ed. 1996
  2. DHH, opening keynote, Rails World 2026 (September 2026, YouTube)
  3. Matz, DHH, and Jeremy Daer, conversation at Rails World 2026 (September 2026, YouTube)
  4. Sam Ruby, Partly in the Right (September 2026, intertwingly.net); the Roundhouse project (GitHub)
  5. Simon, H. A. (1962), The Architecture of Complexity, Proceedings of the American Philosophical Society 106(6)
  6. Conway, M. E. (1968), How Do Committees Invent?, Datamation
  7. Codd, E. F. (1970), A Relational Model of Data for Large Shared Data Banks, CACM 13(6); (1971), Further Normalization of the Data Base Relational Model
  8. Turing, A. M. (1936), On Computable Numbers; Rice, H. G. (1953), Trans. AMS 74
  9. Ashby, W. R. (1956), An Introduction to Cybernetics
  10. Dijkstra, E. W., Notes on Structured Programming (EWD249); On the foolishness of "natural language programming" (EWD667)
  11. Andy Wingo, international lisp conference -- day two (March 24, 2009, wingolog.org)
Note: all illustrations are the author's own schematic drawings. The video content is paraphrased; the only direct quotation is DHH's line about English.
犹剑在心
北极甜虾(剑心犹在!)
— The End —

← All essays