第八课:复杂度控制
用 Ousterhout 的 red flags 审 AI 的代码

≈22 分钟 · AI 时代战略型工程师 · 课程 0008 · 2026-07-06
前置:第七课·云端异步(Review guidelines 伏笔在此收回)· 参考:术语表

前七课让它跑得快,这一课管跑向哪

盘点一下:你的委派阶梯五级全亮了,KMP 重构 merge 了 4 万行 AI 改写的代码。现在问一个前七课都没问的问题:这 4 万行的复杂度,谁在守门?

"A language model, left to its own defaults, is a relentlessly tactical programmer." —— 放任默认状态的大语言模型,是一台不知疲倦的战术型程序员。(Franzone, The Next Web 2026)

第一课引过 Ousterhout 的战术/战略之分,这一课把他的整套复杂度框架精讲一遍,然后干一件所有前课铺垫好的事:把它装进你的 review 链——用第七课的 Review guidelines、第五课的独立 verifier、第六课的对抗验证,让"守复杂度的门"也成为可委派的流水线环节。

Ousterhout 本人的 AI 立场(2025-04 播客,一手):AI 能力扩张会让软件设计更重要而非更不重要——AI 让代码总量更大,而当前工具替代不了高层设计。
引用精确性说明:广为流传的"Ousterhout 说 AI 是战术龙卷风",实际是主持人 Gergely Orosz 对访谈的总结措辞,不是他本人的逐字原话——本课程只把可考证的立场归给他。

框架本体:复杂度的解剖学(原书原文版)

定义 → 两来源 → 三症状

复杂度 = 让系统难以理解和修改的一切结构性因素。它只有两个来源,产生三种症状:

源1
依赖(dependencies):一段代码无法孤立地被理解和修改。→ 导致变更放大(改一处牵动多处)和认知负担(完成任务前必须先知道的东西太多)。
源2
晦涩(obscurity):重要信息不显眼。→ 导致未知的未知(不知道该改哪里、也不知道自己不知道)——三症状里最糟的一种,因为它只在事后爆炸。

一个反直觉推论(原书原文):"有时多写几行代码的方案反而更简单,因为它降低了认知负担。" 行数不是复杂度的度量——读者需要知道多少才是。审 AI 代码时这条尤其重要:AI 爱交"看起来精巧"的短代码。

深模块:接口是成本,功能是收益

原书 §4.4 的原文公式(注意:流行说法"实现是收益"是二手转述,原文是功能):

"The benefit provided by a module is its functionality. The cost of a module (in terms of system complexity) is its interface."
深模块:简单接口 + 强大功能。调用方学得少、得到多。例:Unix 文件 I/O 五个系统调用背后是缓存、调度、权限的全部实现。
浅模块:接口相对其功能来说过于复杂——学习成本接近甚至超过它省下的工作。变体 classitis:"类越多越好"的误解——每个类各自简单,系统整体更复杂(Java 读个序列化文件要叠三个 stream 类)。

信息隐藏 vs 信息泄漏

信息隐藏:每个模块封装几项设计决策(知识),知识只体现在实现里、不出现在接口上。信息泄漏:同一项设计决策同时体现在多个模块——原书称其为"软件设计里最重要的 red flag 之一",且走"后门"的泄漏(不经接口、靠隐式约定)比接口泄漏更险恶,因为它不可见。典型病型:时序分解(temporal decomposition)——按执行顺序拆结构(read/modify/write 拆三个类,其中两个都得懂文件格式)。解法原则(原文):"按每个任务需要的知识组织结构,而不是按任务发生的顺序。"

AI 为什么天然战术:2024-2026 实证链

这不是感觉,是测出来的。四组独立证据(注意 GitClear 是 review 工具厂商,数据有利益相关,但口径连续五年、样本 6.2 亿次变更):

证据源关键数字对应的 Ousterhout 病理
GitClear 2025/2026
(1.5→6.2 亿行/变更)
复制粘贴占比 9.4%→15.7%;重构性移动 21%→3.8%(粘贴:重构 ≈ 5:1);≥5 行重复块 +81%;跨文件函数调用(复用信号)-35%;错误掩蔽结构 +47%Repetition(复制而非抽象)、信息泄漏(同一知识散落多处)、Nonobvious code
arXiv 2510.03029(2025)LLM 代码 code smell 比人类基线高 63%;任务越复杂恶化越重浅模块、命名类 red flags
arXiv 2605.02741(2026)模型越强、代码越臃肿越耦合(Volume-Quality 反比律);"详细 prompt 救不了架构退化"(原文:neither functional correctness nor detailed prompting mitigates this decay)换更强模型/写更细 prompt 都不是解药 → 只能靠设计边界 + review 门禁
DORA 2024/2025 · SO 调查 2025AI 采用度每 +25%,交付稳定性 -7.2%(2025 仍负相关);开发者头号挫败感(66%):"almost right, but not quite"战术龙卷风的组织级代价

把三条线索拧成一句话:AI 的默认行为模式是复制代码块而非抽象共性、加参数/加分支而非重想接口、堆浅包装层而非挖深模块——而且模型更强、prompt 更细都不能自动治好它。解药只在你手里:预先给设计边界(第二、三课的 spec 与方案冻结),事后用 red flags 门禁(本课)。

武器库:14 条 red flags × AI 典型表现

原书书末的 red flags 全表——判据是原书原文的浓缩,第三列是它在 AI 生成代码里的高发形态(本课程综合实证整理)。这张表就是你 Review guidelines 的原材料

#Red Flag一句话判据AI 代码典型表现
1浅模块 Shallow Module接口相对功能过于复杂一个 prompt 一层 wrapper:只转发调用的 service/util/helper 堆叠
2信息泄漏 Information Leakage同一知识出现在多处同一数据格式/协议的解析逻辑散落多个生成片段,各写各的
3时序分解 Temporal Decomposition执行顺序反映在代码结构上按 prompt 步骤直接拆 step1/step2/step3 函数,而非按知识边界
4过度暴露 Overexposure常用功能的 API 逼用户了解罕用功能内部选项全部提升为参数,调用方被迫理解不相关配置
5传声筒方法 Pass-Through Method除了原样转发参数什么都不做为"分层"而分层:controller→service→repository 同签名逐层转发
6重复 Repetition同一段代码反复出现最强实证:复制相邻代码块而非抽象共性(GitClear 5:1)
7通用特化混杂 Special-General Mixture通用机制里混入特定用例的代码往通用函数里加 if 特判/新参数,而非重想接口
8连体方法 Conjoined Methods不看另一个方法就无法理解这一个多个生成函数共享隐式状态/约定,单看任何一个都不完整
9注释复读代码 Comment Repeats Code注释信息从旁边代码一眼可得LLM 高发:// increment counter 式逐行复读
10实现污染接口文档接口文档描述使用时不需要的实现细节docstring 大段复述内部算法步骤而非使用契约
11模糊命名 Vague Name名字宽到能指代很多东西data/result/process()/Manager 泛滥
12起名困难 Hard to Pick Name找不到简单而形象的名字processDataAndValidateAndSave = 职责没切干净的信号
13难以描述 Hard to Describe写不出简单而完整的注释可反向工具化:让 AI 给自己的函数写一句话文档,写不清=设计问题探针
14意图不明的代码 Nonobvious Code快速阅读无法理解含义和行为能跑但意图不明;空 catch、宽泛 fallback 等错误掩蔽结构(+47%)

工具化:把 red flags 装进 review 的枪膛

第七课的伏笔现在收回。你的 review 链上有四个位置可以装这张表——而且调研确认:把 red flags 写成 Review guidelines / REVIEW.md 的公开现成例子还没有,你做完作业就是先行者:

Codex:AGENTS.md 的 ## Review guidelines
官方机制:@codex review 会搜仓库里的 AGENTS.md 并遵循其中 Review guidelines;就近生效——离被改文件最近的一层优先,KMP 模块可以有自己的一份。写法就是自然语言规则条目(见作业模板)。配合第七课的 @codex fix,发现→修复闭环。
Claude Code:REVIEW.md + /code-review
托管 Code Review 服务会把 REVIEW.md 逐字注入每个 review agent 的 system prompt(最高优先级);本地 /code-review 的 effort 分级是 low→max(注意:ultra 不是 effort 级别,是云端多 agent 模式),低档少而准、高档广而可能含不确定项;--fix 直接把 findings 修进工作区。发版级用 /code-review ultra(每条发现独立验证)。
一条铁律(呼应第五、六课):生成代码的模型不应同时做它的 reviewer——同一解释框架看不见自己的盲区(Greptile 的论证,与官方"fresh context verifier"一致)。Claude 写的让 Codex 审、Codex 写的让 Claude 审,或至少换一个 fresh context。
现成起点:GitHub 上已有把 APOSD 全书做成 Claude Code skill 的项目(software-design-philosophy-skill,MIT),review 模式明确扫浅模块/传声筒/信息泄漏/时序分解。可以直接装,再用你的 Review guidelines 收窄到你仓库的痛点。

落地任务:给你的 KMP 代码库装复杂度门禁(约 50 分钟)

三步,做完你就有了自己的复杂度防线

挑 5 条写成 Review guidelines(20 分钟):从 14 条里挑你的 KMP 重构最可能踩的 5 条(建议:#1 浅 wrapper、#2 信息泄漏、#5 传声筒、#6 重复、#7 特化混杂——正对"复用基建、抽共用层"的目标)。每条按这个模板写:
## Review guidelines
- 浅模块:新增的类/函数若只是转发调用、接口复杂度接近其功能价值,标记并建议内联或加深。
- 重复:发现 ≥5 行的相似代码块(哪怕不完全相同),标记并指出应抽象的共性;
  禁止用"再复制一份"通过 review。
- 信息泄漏:同一数据格式/协议知识出现在两个以上文件时标记,指出应收敛到哪个模块。
(每条 = 判据 + 该怎么报告;只报影响正确性与可维护性的项,不报风格偏好)
装进两条 review 链实跑(20 分钟):写进仓库 AGENTS.md 的 ## Review guidelines(Codex 侧)和 REVIEW.md(Claude 侧),然后对你 KMP 重构里已 merge 的一个单元/code-review(记得换 fresh context 或换工具审,别让改代码的会话自审)。验收:至少产出 3 条带 red flag 归类的发现
给自己的仓库测个"GitClear 指标"(10 分钟):派一个 agent:"统计重构后代码里 ≥5 行的重复代码块数量、只做转发的 pass-through 方法数量,与重构前的 native 版本对比"。这两个数字是你的复杂度基线——下次大改造后再测一次,看趋势。

把 ② 的发现清单和 ③ 的两个数字发我——它们决定你的 Review guidelines 第二版怎么改,也是第九课(常驻自治:让 Routine 每周自动跑这套审计)的输入。

自测(先回忆,再点选)

1. 按原书原文,模块的收益和成本分别是什么?

原文:"The benefit provided by a module is its functionality. The cost of a module is its interface."——流行的"实现是收益"是二手转述。深模块 = 简单接口(低成本)+ 强大功能(高收益)。行数根本不在公式里:多几行但降认知负担的方案反而更简单。

2. 复杂度三症状里,Ousterhout 认为最糟的是哪个?为什么?

原文:"Of the three manifestations of complexity, unknown unknowns are the worst."——变更放大和认知负担至少看得见、能估计成本;未知的未知让你不知道该改哪里、也不知道自己遗漏了什么,只能靠事故来发现。它的来源是晦涩(obscurity),所以"重要信息不显眼"的代码比"难写的代码"更危险。

3. 2024-2026 实证里,AI 生成代码最强的一条病理证据是?

GitClear(6.2 亿次变更):复制粘贴行数史上首超 moved(重构)行数,2026 上半年粘贴:重构 ≈ 5:1,≥5 行重复块 +81%——正中 Ousterhout 的 Repetition 与信息泄漏 red flag。性能/编译不是主要病理:AI 代码通常能跑,病在结构("almost right, but not quite")。

4. 为什么生成代码的模型不应该同时做它的 reviewer?

它不是"护短",是结构性盲区:生成时的理解框架有什么缺口,审查时同一框架还是有同样的缺口——第五课的 self-grading 问题、第六课对抗验证的存在理由都是它。解法:fresh context 的独立 verifier,或干脆换一家(Claude 写 Codex 审、反之亦然)。

首选阅读(一本 + 一篇)

《A Philosophy of Software Design》第 2-5 章(约 50 页)——复杂度定义、战术/战略、深模块、信息隐藏的原始出处,AI 时代读比 2018 年更切身。时间紧就先读 Complexity Is the Ceiling(15 分钟)——它把整套框架直接接到了 LLM 行为上,"复杂度是你能委派给机器多少工作的天花板"。

引用文献

🧠背诵区

点卡片翻面。记的是能用的判断核心,不是定义。

闪卡 1 / 解剖学
复杂度的定义、两来源、三症状是什么?哪个症状最糟?深模块的原文公式?
翻面
复杂度 = 让系统难以理解和修改的结构性因素。两来源:依赖(→变更放大 + 认知负担)和晦涩(→未知的未知,最糟——只在事后爆炸)。深模块公式(原文):收益是功能,成本是接口——不是"实现是收益"。推论:行数不是度量,读者需要知道多少才是。
闪卡 2 / AI 病理
AI 为什么天然战术?证据链的三个关键数字?为什么"更强模型/更细 prompt"不是解药?
翻面
AI 默认复制而非抽象、加参数而非重想接口、堆浅层而非挖深模块。数字:粘贴:重构 ≈ 5:1(GitClear 2026)、smell 密度比人类+63%、交付稳定性-7.2%(DORA)。2026 研究:模型越强越臃肿,详细 prompt 救不了架构退化——解药只有预先的设计边界(spec/方案冻结)+ 事后的 red flags 门禁。
闪卡 3 / 工具化
red flags 怎么装进 review 链?最重要的一条铁律是什么?
翻面
挑最痛的 5 条(浅模块/泄漏/传声筒/重复/特化混杂),每条写成"判据 + 怎么报告",装进 Codex AGENTS.md ## Review guidelines(就近生效)和 Claude REVIEW.md(逐字注入 reviewer system prompt)。铁律:生成代码的模型不做自己的 reviewer——同一解释框架看不见自身盲区,用 fresh context 或换一家审。
⏱ 间隔复习:明天扫一遍,3 天后再来。交织:翻一张第二课「可执行规格」的旧卡——spec 把判断前置,red flags 门禁把判断后置,一前一后夹住 AI 的战术惯性。
🗣复述区

能讲清楚,才是真懂——比能回忆高一层。

复述任务
用一段话讲清楚:为什么 AI 时代 Ousterhout 的框架比 2018 年更重要?你打算怎么给自己的代码库搭复杂度防线?
参考表述
因为 AI 把写代码的边际成本打到近零,代码总量暴涨,而实证显示它的默认行为是战术性的:复制而非抽象(粘贴与重构之比约 5:1)、加参数而非重想接口、smell 密度比人类高六成,并且模型越强代码反而越臃肿、写更细的 prompt 也救不了架构退化——所以复杂度成了我能把多少工作委派给机器的天花板,守门的设计判断反而成了稀缺品,这正是 Ousterhout 本人的立场:AI 让设计更重要而不是更不重要。我的防线是前后夹击:前置用规格和方案冻结给出设计边界;后置把 red flags 里我的代码库最痛的五条(浅模块、信息泄漏、传声筒、重复、特化混杂)写成 Review guidelines,装进 Codex 的 AGENTS.md 和 Claude 的 REVIEW.md,让每个 PR 自动过这道门,并遵守一条铁律——生成代码的模型不审自己的代码,用独立上下文或另一家来审。复杂度的门禁本身也委派出去了,但判据是我定的。

讲不顺的地方就是还没真懂的地方 —— 发给我,我帮你补上。