读完《Programming as Theory Building》,我第一反应居然是想起特德·姜的《软件体的生命周期》。 这篇文章想讲的和小说里表达的那种 软件慢慢长成一条生命 感觉很相似: 软件的价值,从来不只躺在代码里。 如果我们只盯着语法、框架和架构图看,其实是看不到最关键那一层东西的。

Naur 在《Programming as Theory Building》里给了一个很扎心但又很实际的答案:

程序的核心,不是源代码,而是写程序那群人脑子里的“理论(theory)”。

下面就借这篇文章,围绕架构,聊聊我对“软件演变”的几条新理解。

程序不是代码,架构不是图,而是“集体潜意识的脑补”

Naur 的核心观点可以一句话概括: 编程的本质,是程序员逐渐形成一套关于“世界如何被程序支持”的理论; 代码只是这种理论的一个不完整、带损耗的记录。

他举了两个很典型的故事:

  1. 编译器案例(Group A / Group B): Group A 做好了一个结构很优雅的编译器。多年后 Group B 想在此基础上扩展语言,拿到了“完整文档+源码+设计说明”,还可以请教 A。 只要 A 还在,扩展就能优雅融入原结构。但再往后,A 完全离场之后,这个编译器在各种“补丁式修改”下慢慢失去原来的力量——结构还在, 但已经被层层堆砌弄得“有其形,无其神”。

  2. 实时系统运维案例: 厂商内部那波长期跟系统打交道的“安装与排障程序员”,几乎只靠脑子里的理解和少量注释就能定位问题。 而拿到全套文档的客户侧程序员,常常看不懂、改不好,最后还得求助厂商那群“有理论的人”来点拨。

这两个例子其实都在说一件事: 程序真正的“架构”,其实是团队共享的一套心智模型,而不是那几页 UML 图、几份 markdown 文档。

当我们说“这个系统架构不错,很清晰”、“这个系统已经烂尾、难以维护”,很多时候指的不是代码行数、文件布局,而是 这套理论是不是还在被好好延续。

从“程序即理论”看软件架构的演变:变的是图,还是变的是“脑子”?

如果把 Naur 的观点套到软件架构上,会发现一个挺颠覆的视角: 软件架构的演进,本质上是团队内“理论”的演进,而不是图从单体变微服务、从 MVC 变 CQRS 这么简单。

架构图只是“理论的一张快照”

每次做架构设计,我们都会产出一堆东西, 分层图、组件图、时序图、ADR…… 这些都很重要,但它们最多只是某个时间点的“理论截图”

  • “为什么这段逻辑放在领域层,而不是服务层?”
  • “当初为什么选事件驱动,而不是 RPC?”
  • “这块缓存为什么宁愿多查一次 DB,也不在网关层做?”

这些关键的 为什么,常常只存在于

  • 几次争论和白板推演;
  • 两三位核心开发者脑子里;
  • 偶尔散落在 PR review、issue 评论里。

也就是说, 架构图能告诉你“现在长什么样”,真正指导你演进的,是“当时是怎么想的”。

这和 Naur 所说“程序的生命依赖于掌握其理论的那批程序员”完全对上, 只要“掌握理论的人”还在,系统就还活着。人一散,架构再漂亮,也只是个壳。

重构到底在“救代码”,还是在“救理论”?

很多团队会有这样的经历:

  • 刚上线时,大家对系统边界、核心对象、关键流程都很熟
  • 过了两三年,业务变复杂、人员换了一拨又一拨
  • 某天你突然发现, “我们现在其实已经没人能讲清这套系统整体是怎么回事了。”

这时候做 “架构重构”,从 Naur 的视角看,其实是在做两件事:

  1. 重新梳理并更新团队的“理论”

    • 现在业务的核心对象到底是谁?
    • 哪些约束还成立?哪些已经被现实干废了?
    • 新的复杂度主要卡在什么地方?
  2. 让代码和这种新的理论重新对齐

    • 划清界限、拆解模块、调整依赖;
    • 把过去那种“乱入的补丁”和“不合时宜的抽象”清理掉。

如果只做第 2 件事,大改代码、重画架构图,但没有完成第 1 件事(也就是团队对系统的共同理解没有同步更新), 那这次重构很快会再次 “腐化”,因为新的修改又会基于各自不同的 “私有理论” 继续胡乱生长。

Naur 对“可维护 / 可扩展”的重新定义:不是能改,而是有理论可改

文章里有一段对 修改成本灵活性 的讨论,特别值得重新回味一下。

修改成本的核心,不在修改这几行代码有多难

我们习惯说要做 “可扩展架构”,希望未来 “改得动”。 Naur 泼的一盆冷水是:

  • 你别拿“建筑改造”来类比软件,以为软件修改必然便宜;
  • 对很多复杂系统来说,大规模修改本来就应该是 昂贵的
  • 只有当你把编程等同于“编辑文本”时,才会幻想修改很便宜。

在 Theory Building 这套视角下: 修改成本的真正大头,是: 新的业务需求能否自然嵌入现有理论,而不是单纯地插入几段 if/else。

如果团队对系统原有设计的理论已经模糊乃至消失,那么所谓可维护就变成了: 反正加个开关、加一层 if,又不是不能用。

长此以往,系统就会走向文章中说的 结构还在,但力量尽失 那条路。

灵活性不是白给的,每一个可配置都是一块债

我们常说为未来预留扩展点,搞一堆 配置项、插件机制、预留字段、预留 topic、预留接口

Naur 的态度很朴素:每一份灵活性,都是提前做好的功能。 你要为它付出:设计、实现、测试、文档的全套成本。

而这些东西未来到底用不用,全看运气。

从架构演进的角度看,真正健康的做法反而是,把今天能确认的“变化模式”设计清楚,比如:“以后产品种类会增多,但结算逻辑大体相同”。

对其他模糊的未来,不要强行“留口子”,而是 接受未来要重建/重塑部分理论的事实

换句话说:when in doubt, leave it out!

对软件架构实践的一些直接启发

读完这篇文章,再回头看日常的架构设计和演进,我觉得有几件事值得刻意关注。

架构评审会上,多讲“故事”,少讲“图本身”

传统评审很容易变成:

  • 这里是 gateway,这里是 user-service,这里是 order-service……
  • 我们有三层缓存,这里是 Redis,这里是本地缓存……

如果站在 Theory Building 的角度,更有价值的可能是:

  1. 讲清楚关键隐喻和心智模型
  • 我们把整个系统当成一个『流水线』,有几个关键工位……
  • 对于订单这一块,我们其实是『账本』思维,而不是『事件流』思维。
  1. 讲清楚边界决策背后的理由
  • 为什么风控逻辑坚决不放到下游服务里?
  • 为什么搜索系统要单独拆出去,而不是做成一个库表?

这些东西,才是真正的 理论。图只是帮大家记住理论的一个手段。

顺带一提,Naur 在文末提到的 隐喻(metaphor) 的作用,本质上就是帮助团队共享一套理论意象, 比如“这套系统就像一条装配线”这样的隐喻能极大帮助多人协作时保持设计一致性。

文档写什么

少写 有哪些,多写 为什么这样

很多团队已经在做 ADR(Architecture Decision Record),这其实非常贴近 Naur 的主张:

不只是记录采用了哪种架构/中间件/模式”, 更要记录:

  • 当时看到了哪些现实约束;
  • 比较过哪些方案;
  • 为什么放弃 A、选择 B;
  • 我们认为这套方案最关键的“适用前提”是什么。

这些都是在为未来的理论维护打地基。 几年之后,新人可能对当年的代码已经不感兴趣,但只要还能读懂这套理论演化史,就还有机会做出不那么违和的改动。

新人上手一个系统,不是看文档和看代码,而是跟着有理论的人一起改

Naur 把程序的“生命期”定义为: 有掌握其理论的程序员在场,且持续参与修改。

一旦这批人解散,程序就进入“死亡状态”,之后所谓的“复活”几乎不可能完全成功。

这对团队协作有两个直接含义:

  1. Onboarding 新人最有效的方式: 不是一口气把所有文档丢给他,而是让 TA 在有经验的人陪着的情况下, 实际做几次真实修改和查错, 并在过程中不断解释:

    • “为什么我们当初这样设计?”
    • “这类需求我们一律在这层解决。”
    • “这种改法会和我们原先的假设冲突。”
  2. 避免边缘化那几个懂系统的人: 如果一个系统的核心理论只有 1 ~ 2 个人真正掌握, 那么这个系统的“架构演进能力”就高度绑定到这几个人的流动风险上了。

接手一个死系统:重构前先问一句,要不要干脆重写?

Naur 有个挺激进但很诚实的结论:对一个已经失去理论支持的老系统,从文档和代码里复活原有理论几乎是不可能的, 与其如此,不如承认它已经死亡,重新组织团队、从头解决问题。

这和实际经验也蛮吻合,有些系统你一看就知道,任何在这上面做的修修补补,只会让它死得更难看。 真正能拯救场面的,不是再加一层抽象,而是重新建立一套简洁可讲述的理论,然后用新的代码去承载它。

一篇 1985 年的老文,为何在今天还这么“新”

再回头看一下时间——这篇文章写于 1985 年。 如果不看出处,只看内容,很容易以为是哪个关注开发者体验的架构师,最近几年在跟微服务、平台工程、AI 编码助手打交道之后写下的反思。

它经得住时间,有几个原因:

  1. 它几乎不谈当时的技术细节,只谈人和理解。 编译器也好、实时系统也好,在文中都只是载体。真正被拿出来反复打磨的,是程序员如何理解系统、如何在脑子里搭建一套理论这件事。只要软件还是人写的,这个主题就不会过时。

  2. 它反复强调的,是协作中的心智模型,而不是某种流行方法论。 敏捷、DDD、微服务、DevOps……这些词在 1985 年还不存在,但我们今天做的很多实践,其实都在绕着同一个核心打转: 让更多人共享同一套系统的理论。这一点,Naur 在很朴素的项目故事里已经说得足够透。

  3. 它对文档、代码、重构的怀疑态度,放在今天依然刺耳却真实。 在 2025 年,我们有更强的 IDE、更自动化的测试和 CI/CD、有 AI 可以帮我们总结文档和代码,但 Naur 提醒的那句话依然振聋发聩: 果没人真正掌握这套理论,再好的工具也只能帮你更快地把系统搞得更乱

也正因为这样,这篇 1985 年的小文,反而非常适合拿来对照今天的实践: 当我们讨论“AI 能不能自动生成架构图”“能不能从代码里自动恢复设计意图”时,Naur 会在背后轻轻补一句:

这些都很好,但别忘了:真正的架构,仍然长在人脑子里,而不是长在工具里。

也许这就是《Programming as Theory Building》在 40 年后读起来依然常看常新的原因: 技术在变,框架在变,云原生、Serverless、AI 编程 … 一波接一波,但“软件其实是人和人之间共享的一套理论”这一点,至今没人能真正绕开。

总结

软件架构的演变,是人的演变

读完《Programming as Theory Building》, 我对“软件架构”这件事的看法大概变成了这样:

  1. 架构不是一张一直更新的图,而是一群人共享的理论。
  2. 演进的难点,不是“改哪几行代码”,而是“新需求能不能自然长在原有理论上”。
  3. 好的文档、会议、评审,是在培养和传播这套理论,而不是单纯记录现状。
  4. 一个系统能活多久,看的是掌握其理论的那些人是否持续在场。

如果一定要用一句稍微人话一点来收尾,我会这么说:软件架构这回事,其实从来不是“画图的艺术”, 而是“让一群人对同一个复杂世界,尽量形成同一套好用的理解”的过程。

只要这套理解还在不断被更新、被讨论、被传递,“软件架构的演变”就不是一串技术名词的升级史,而是一支团队和它的系统共同成长的故事。