首页 › programming › 深模块与浅模块:给软件复杂度记本账

深模块与浅模块:给软件复杂度记本账

为什么是这堂课

程序员圈子里有个残酷的共识:绝大多数项目不是死于「功能做不出来」,而是死于「改不动了」。需求明明只改一行,你却要在八个文件里旅行;接手一个老系统,读三个月代码才能敢加一个 if。这种痛,大多数课程选择回避,因为它听起来「不够工程」——没有框架、没有语法、跑分也测不出来。

但这堂课正面回答了它。演讲者是斯坦福大学教授 John Ousterhout——Tcl/Tk 之父、Raft 共识算法的共同作者、创办过 Electric Cloud 等公司的老兵。他在 Google TechTalks 做的这场演讲《A Philosophy of Software Design》,把他 2018 年出版的同名著作浓缩成大约一小时的内容,讲透了一件事:复杂度不是一个形容词,而是一笔可以记账、可以审计、可以管理的资产。

放在 2026 年重看,这堂课不但没过时,反而更扎心了:AI 生成代码的速度越来越快,单行代码的成本趋近于零,而复杂度的累积速度却在肉眼可见地变快。当写代码不再稀缺,「能不能看懂、敢不敢改」就成了新的瓶颈。这时候,一个花了四十年写系统的人把「复杂度」拆开来给你看,值得你认真听一小时。

视频背景卡片

项目 内容
视频标题 A Philosophy of Software Design — John Ousterhout
频道 Google TechTalks
时长 约60分钟
播放量 百万量级(以YouTube页面实时数据为准,撰稿时未能联网核验)
核心主题 软件复杂度的定义、根因,以及一套系统性的设计对策

核心内容拆解

1. 复杂度终于有了一个「工作定义」

这堂课最大的贡献,是把「复杂」这个人人会用、没人说得清的词,钉成了一个可操作的定义:复杂度,就是理解和修改一个软件系统的困难程度(视频原话大意:任何让软件难以理解、难以修改的东西,都是复杂度)。

它有三个典型症状。第一是变更放大(change amplification):一个概念上的小改动,要在很多地方各改一遍。第二是认知负载(cognitive load):为了完成一个简单任务,开发者必须先装进脑子里一大堆前置知识。第三也是最阴险的,是未知未知(unknown unknowns):你不知道完成这个任务还需要知道什么,直到系统在生产环境替你叫出声来。

而复杂度只有两个根因:依赖(dependencies)——某段代码不弄懂另一段就没法改;晦涩(obscurity)——关键信息不在它该在的地方,藏在命名里、藏在隐式约定里、藏在「隔壁老王知道但代码里没写」的地方。

【延伸】这个定义妙就妙在它是「会计学」式的:复杂度不是审美判断,而是成本科目。三症状两根因,等于给了你一张资产负债表,后面所有设计手段,都是往这张表上「减负债」。

2. 压垮系统的是增量,不是巨石

课程里有个反直觉的论断:项目不是被一个巨大的糟糕决定毁掉的,而是被成千上万个「小小的妥协」压垮的。每次赶工期加一个特判,每次「先这样、以后重构」埋一个坑,单看都无辜——毕竟每个补丁都很小。但复杂度是增量的(complexity is incremental):它像冰山,水面下那部分是几十年、几百个 PR 慢慢冻出来的。

Ousterhout 给这类人起了个外号:tactical tornado(战术龙卷风)。这种人产出惊人,永远在冲刺,每个需求都「做完了」,但他路过之处寸草不生——系统每被他扫一遍,复杂度就上一个台阶。视频原话大意是:战术龙卷风不是坏人,他只是把「能跑」当成了唯一的目标函数。

【延伸】这就是为什么「我们团队都是聪明人,系统还是烂了」如此常见。复杂度账本不记智商,只记决策的对外成本。聪明人加特判的速度,往往比普通人还快。

3. 战略编程:给自己留一到两成的「投资仓位」

既然最大的敌人是增量的妥协,对策就藏在对称的位置上:从「能跑就行」(tactical programming)切换到「战略编程」(strategic programming)——把「代码不仅要工作,还要让未来的改动变便宜」当作首要目标。

具体的仓位建议是:投入大约10%~20% 的开发时间做设计投资(视频给出的大致量级)。这意味着偶尔拒绝最短路径:多花点时间想接口、补注释、收敛一个烂抽象。短期看,你可能比「怎么快怎么来」慢一两个星期;长期看,这些投资会在每一个后续需求上返利。

【延伸】我自己的判断是:这个「一到两成」最实在的用法不是拉长工期,而是给重构一个合法的记账科目。很多团队不是不知道系统在烂,而是「还债」在排期里永远排不上——因为这看起来不是「做功能」。Ousterhout 的框架把它从情怀问题变成了投资回报问题。

4. 深模块:接口与功能的「汇率」

全课最核心的设计原则只有一个词:深(deep)。最好的模块,是用最简单的接口,封装最强大的功能——接口描述成本除以功能价值,这个「汇率」越高越好。

反面教材是浅模块(shallow module):接口又长又啰嗦,实现却薄得像纸。课程里用了 Java IO 的经典例子:想读一个文件,你可能要自己拼 FileInputStream、BufferedReader 一层层包装,每层的构造函数和异常都在向你收费——接口面积巨大,实际功能却只是「读文件」。正面对照是 Unix 文件系统的抽象:open、read、write、close、seek 五个调用,背后藏着磁盘驱动、缓存、块分配一整座冰山。另一个极端例子是垃圾回收器:接口面积几乎为零(你根本感知不到它),承担的却是内存管理这种重量级功能。

由此推出两条红线:pass-through method(传话筒方法)——方法只是原样转调下一层,不增加任何价值,是浅模块的典型气味;以及类不是越小越好——为了「一个类只干一件事」把系统切成大量碎片,往往是在批量生产浅模块。

5. 不同层,不同抽象

深模块原则还有一个纵向版本:每一层都应该提供与其他层不同的抽象。调用者往下一层走,看到的世界应该换一套词汇,而不是同一套概念的复读。

视频里点名了常见的反模式:一个层只是机械转发(调 A 变成调 A'),或者一层「夹心饼干」同时暴露两层的概念,逼着调用者同时理解两层才能干活。这和上一条是同一枚硬币:信息隐藏(information hiding)——每个模块把最难的设计决策锁在自己肚子里,对外只露一个稳定的、小而深的接口。模块之所以「深」,正是因为它替你扛住了会变的细节。

【延伸】这在微服务时代反而更重要也更难:跨网络的调用把「接口」变成了永久契约,一个浅的远程接口就是一笔带利息的债务。很多团队的「服务拆分」,拆出来的其实是一堆网络化的浅模块。

6. 注释是设计工具,不是道歉信

这是课程里最有争议、也最有个人风格的部分。Ousterhout 直接对立了一种流行教条——「好代码不需要注释」(后来那场著名的论战就埋在这里)。他的主张恰恰相反:注释是消除「晦涩」这个复杂度根因的第一工具,因为依赖和晦涩二者之中,「晦涩」才是更隐蔽的杀手——它制造未知未知。

他的实操建议非常具体:注释要描述代码本身说不出来的东西——为什么这样做、参数的约束和单位、调用的前提条件;并且先写注释、后写代码(write the comments first)。注释在这个过程中不是文档,而是低成本的「设计草稿」:如果一段逻辑你用注释都讲不清楚,那多半是设计有问题,代码只会把讲不清放大成讲不清还跑得动。

【延伸】把这条和深模块原则连起来看:接口注释就是你模块的「对外报价单」。写不出简短清晰的接口注释,基本可以断定这是个浅模块——你收的费比提供的功能多。

7. 把错误「定义掉」,再谈「设计两遍」

课程的后半段是两个务实的收尾。第一是错误处理:异常处理是复杂度的重要来源,而高手的第一反应不是「如何处理错误」,而是「如何让这个错误根本不存在」——把错误定义掉(define errors away)。他举的例子是 Tcl:读取一个不存在的变量,直接返回空字符串而不是抛错,于是调用方永远不需要写那几行防御代码。设计接口时,先想想哪些错误场景可以语义上消除,再谈怎么报错。

第二是设计两遍(design it twice):拿到一个设计问题,别满足于第一个想到的方案——至少做两个截然不同的设计,摆在一起比较。第一个方案往往是你最熟悉的路径,而第二方案会逼你发现第一方案的隐含假设。视频原话大意:两版对比下来,最终方案常常比第一直觉好得多,而花的时间远比重构省。

8. 【延伸】一场迟到四十年的算总账,和一个新变量

说点课程之外的事,帮读者把这堂课放进 2026 年的坐标系。

其一,这堂课背后那本书,这几年在软件圈掀起了一轮不小的争论:它和 Uncle Bob 的《Clean Code》在函数长度、注释价值、TDD 等问题上几乎处处对着干。据 Hacker News 和多家技术媒体的报道,2025 年初,Ousterhout 与 Robert C. Martin 还在 GitHub 上进行了一场公开的多轮辩论,把「深函数还是小函数」「注释是不是必要的evil」摆上台面,引发社区大量讨论(辩论原文可在 GitHub 上搜索两位作者的名字找到,细节未经本人逐一核验)。我的看法:这场争论没有裁判,但 Ousterhout 的框架更容易「记账」——他给的是可执行的判断标准(接口/功能汇率),而不是风格戒律。

其二,也是我更想强调的:AI 编程让这套框架从「重要」变成了「保命」。当代码生成的边际成本趋近于零,产出速率不再由打字速度决定,而由「人类能理解多少、敢改多少」决定。AI 最擅长批量生产的东西恰恰是浅模块——一层层 wrapper、一次次机械转发。让 AI 写实现、人类守住接口的深浅,可能是未来几年工程师最重要的分工线。这笔账,Ousterhout 十年前就替你列好了科目。

知识结构图

flowchart TD
    A["软件复杂度:难理解也难修改"] --> B["三大症状"]
    A --> C["两大根因"]
    B --> B1["变更放大:一处改,处处改"]
    B --> B2["认知负载:读懂一行要备一摞课"]
    B --> B3["未知未知:不知道自己不知道"]
    C --> C1["依赖:模块互相咬住"]
    C --> C2["晦涩:关键信息藏在暗处"]
    C1 --> D["战术编程:能跑就交付"]
    C2 --> D
    D --> E["增量式腐烂:每个补丁都无辜"]
    E --> F["战略编程:留一到两成时间做设计投资"]
    F --> G["深模块:小接口,大功能"]
    G --> G1["信息隐藏:细节关在门里"]
    G --> G2["不同层,不同抽象"]
    G --> G3["把错误定义掉:让异常无处发生"]
    F --> H["注释先行:注释是设计工具"]
    H --> I["设计两遍:第二版常好一倍"]
    E -.->|AI时代加速| J["代码越生成越快,账欠得越快"]

实战启示:给团队装一套「复杂度记账法」

这堂课最值钱的地方,是它给出的每一条都能变成例行公事。给你一套可以直接抄的清单:

第一,每周五做 15 分钟「复杂度三问」。 这周我改某个需求时,动了几处才算完?(变更放大)新同事接手时,需要先弄懂哪几件本不必懂的事?(认知负载)有没有哪次线上问题,根因信息藏在没人想到的地方?(未知未知)三问都不疼,这周的账就是平的。

第二,给核心模块做一次「深浅审计」。 打开你最常改的模块,数一数:调用它需要理解几个参数、几个返回值、几个异常?它实际替你扛住了多少细节?接口行数远超功能描述,就是浅模块实锤——别急着拆,先想想能不能把细节收回门内。

第三,试一周「注释先行」。 下一个新模块,强迫自己先把接口注释写完再写第一行代码。写不顺的那些接口,多半设计有问题——这时候改设计,成本是一杯咖啡;上线后改,是一次发版事故。

第四,改一个 code review 的问题。 把「这个 PR 逻辑对吗?」升级成两问:「它让系统更深了还是更浅了?」「它新增的每个错误分支,能不能在语义上定义掉?」——第一条管账本,第二条管利息。

第五,也是 AI 时代的分工姿势。 让 AI 写实现、写测试、写文档,但接口的深浅必须由人签字。AI 可以替你搬砖,不能替你记账。

延伸学习路径

  1. 书本体《A Philosophy of Software Design》——课程只是索引,书里有完整的案例和反例(已有中译本《软件设计的哲学》,可在豆瓣读书搜索)。
  2. 《Clean Code》对读——把两本书的观点摆在一起各打五十大板,比单读任何一本收获都大;两派分歧的现场版见下一条。
  3. Ousterhout 与 Uncle Bob 的 GitHub 公开辩论——2025 年初进行的多轮交锋,GitHub 搜索「Ousterhout Uncle Bob discussion」可定位原文(据社区报道,细节以原文为准)。
  4. raft.github.io——Raft 共识算法官网,Ousterhout 团队的工程作品;对照课程看,体会「深接口」在分布式系统里长什么样。
  5. 课程视频本体——YouTube 搜索「A Philosophy of Software Design Ousterhout」,Google TechTalks 频道的第一条结果即该视频。

一句话总结

深模块不是天才的灵感,而是一笔笔记出来的账:接口越小、扛住的功能越多,你和团队未来每一次改动的成本就越低——而 AI 越强大,这门记账手艺越值钱。

参考来源

  1. A Philosophy of Software Design — John Ousterhout(Google TechTalks) — 本文分析的核心视频(检索直达链接,打开后第一条即该视频;撰稿环境未能联网核验直链与实时播放数据)
  2. John Ousterhout 斯坦福个人主页·书页 — 书本体信息与作者背景
  3. Raft 官网 — Ousterhout 团队代表作,深模块的分布式系统样本
  4. 豆瓣搜索:软件设计的哲学 — 中译本信息
  5. GitHub:johnousterhout — 与 Uncle Bob 的公开设计辩论等讨论所在(据社区报道)

本文由「51学通信」技术团队出品。51学通信由通信行业18年老兵爱卫生(微信ID: gprshome201101)维护,聚焦5G/6G核心网、3GPP标准解读、通信前沿技术深度分析。 出版作品有《5G核心网原理与实践》《IMS原理与实践》等4本通信专业书籍。知识星球「51学通信」提供深度技术答疑和行业洞察。

[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。

51学通信知识星球海报-扫码加入