一只阿木木

8年前的一本书,让我重新理解 AI 系统复杂度

副标题:Ousterhout 说「战术龙卷风是最危险的程序员」——然后 AI 把每个人都变成了战术龙卷风


引子:8年前的一本书

《A Philosophy of Software Design》第一版出版于2018年。

John Ousterhout,斯坦福教授,Tcl/Tk 的创作者,在这本170页的书里只做了一件事:

把「什么是软件复杂度,以及如何系统性地对抗它」讲清楚。

我第一次读这本书是在2023年。那时我觉得它很好,但不算颠覆性的——许多观点在其他地方也能看到。

我在2026年5月重读了它。

这一次,我读完之后沉默了很久。

因为我意识到:这本书在2018年是一本关于「人类程序员」的书,在2026年它变成了一本关于「AI 时代的工程师应该具备什么能力」的书。

而且,它给出的答案和整个 AI 行业主流的答案,完全相反。

一、先建立框架:Ousterhout 的核心命题是什么?

当构建有相当规模的软件系统时,主要挑战是管理复杂度。这本书对复杂度的定义是:「与软件系统结构相关的、使系统难以理解和修改的任何东西。」

这个定义很精准。注意它说的不是「使系统难以运行」,而是「使系统难以理解和修改」。

一个系统可以正常运行,但如果没有人能理解它是怎么工作的,它就是复杂的——也就是危险的。

Ousterhout 指出,复杂度在程序中以以下方式表现:变更放大——一个看似简单的变更需要在很多不同地方修改代码,随着系统增长,做出变更变得越来越难;认知负荷——程序员需要知道太多东西(相对于我们的工作记忆容量)才能做出变更和修改程序。

变更放大 + 认知负荷——这两个词,在 AI 大量生成代码的2026年,有了全新的分量。

二、「战术龙卷风」:一个2018年的词,在2026年有了新的主人

这是这本书里我认为最重要的一个概念。

在《A Philosophy of Software Design》里,John Ousterhout 创造了「战术龙卷风」这个术语:一个以极端战术方式运作并制造大量技术债的程序员。

原文的描述是这样的:

战术龙卷风是一个极其高产的程序员,他产出代码的速度远超其他人,但工作方式完全是战术性的。在实现一个快速功能时,没有人比战术龙卷风更快。在某些组织里,管理层把战术龙卷风当作英雄。然而,战术龙卷风身后留下的是一片废墟。

2018年,战术龙卷风是组织里的「少数害群之马」——每个团队可能有一两个这样的人。

2026年,情况变了。

Ousterhout 创造了「战术龙卷风」这个术语,描述一个快速添加功能、但以牺牲代码质量为代价的程序员。大多数 AI 编码助手都是战术龙卷风。一旦给它们太长的自由发挥空间,它们就会把代码库变成意大利面条。

AI 把「战术龙卷风」从组织的特例,变成了每个工程师的默认行为模式。

一项2026年的研究给出了冷酷的数据:

一项2026年的研究检查了超过6000个公共仓库的30万多个 AI 生成提交,在每次变更前后运行静态分析来测量模型实际引入了什么。超过15%的提交至少引入了一个新问题,在发现的所有问题中,近十分之九是代码异味:结构性问题,能编译并通过测试,但……

能编译,能通过测试——但在悄悄地增加系统复杂度。

这正是「变更放大」和「认知负荷」的来源:每次 AI 做了一个「局部合理但全局有害」的修改,你的系统就离「没有人能理解」又近了一步。

三、「战术编程」与「战略编程」:这才是 2026 年最重要的工程师分野

Ousterhout 把工程师分成两类:战术程序员和战略程序员。

战术程序员的思维模式:

战术编程使得生产出好的系统设计几乎不可能。战术编程的问题在于它目光短浅。如果你在战术性地编程,你试图尽快完成任务。也许你有一个紧迫的截止日期。结果,为未来规划不是优先项。你不会花很多时间寻找最好的设计;你只是想尽快让某些东西工作。你告诉自己,如果这能让当前任务更快完成,添加一点复杂度或引入一两个小补丁也没关系。

这段话精准描述了 AI 编码助手的工作方式。

AI 不知道你的代码库的历史。它不知道你在三个月前重构了某个模块是因为之前的设计带来了维护噩梦。它不知道你在 DECISIONS.md 里记录的「为什么我们不用 ORM」。

语言模型默认是一个无情的战术程序员。它被训练为生产能运行的代码,而不是同事会乐意继承的代码。所以它会复制一个代码块而不是提取共享思想,添加另一个参数而不是重新思考接口,并采用在孤立情况下有效但悄悄恶化整体的局部修复。

战略程序员的思维模式呢?

战略编程的核心是:最重要的是长期结构,让代码今天可以工作是不够的,你必须以不妨碍未来开发的方式投资于系统设计。

战略程序员知道:今天多花20分钟做好设计,会让未来的自己(或同事,或 AI Agent)省下几个小时的困惑。

模型是一个快速的战术程序员,它需要一个在它之上战略性思考的人。从 AI 中获益最多的团队,不是生成最多代码的团队,而是系统保持足够简单、让机器可以快速在其中移动而不破坏它的团队。

这句话是2026年工程师价值的最清晰定义。

你的价值不在于生成代码的速度,而在于保持系统可理解性的能力。

四、深模块 vs 浅模块:这个概念完美解释了 AI 生成代码的核心问题

这是书里我认为最有实用价值的概念。

Ousterhout 把模块分成两种:

深模块(Deep Module):简单的接口,隐藏大量的实现复杂度。

浅模块(Shallow Module):复杂的接口,但实现也相对简单。

Ousterhout 在《A Philosophy of Software Design》里提倡「深模块」:简单接口隐藏大量实现复杂度的组件。不是一个自由地互相导入的浅小模块网络,而是把代码库组织成有清晰边界的实质性块。接口在顶部,实现在墙后。如果测试通过,就不需要看里面。

用一个 RAG 系统的例子:

浅模块设计(AI 容易生成的):

Python

# 外部代码需要知道很多内部细节
retriever = VectorStore(
collection_name="docs",
embedding_model="text-embedding-3-small",
similarity_threshold=0.75,
top_k=5,
rerank=True,
reranker_model="cross-encoder/ms-marco-MiniLM-L-6-v2"
)
results = retriever.search(
query=query,
filters={"metadata.source": "internal"},
include_metadata=True
)

深模块设计(需要人类战略思考的):

Python

# 外部代码只需要知道一件事:「给我相关文档」
retriever = DocumentRetriever(config)
results = retriever.find_relevant(query, context=user_context)

第一种设计里,每次调用都暴露了大量内部决策(哪个 embedding 模型、阈值是多少、是否 rerank)。如果你想换一个 embedding 模型,每一个调用点都要改。这是经典的「变更放大」。

第二种设计里,所有这些细节被隐藏在 DocumentRetriever 内部。你换 embedding 模型只需要改一个地方。

为什么 AI 默认生成第一种?

因为 AI 在「让代码立刻工作」的压力下,会把所有需要的参数直接暴露出来——这是战术有效的,但战略上是有害的。

如果你必须追踪实现细节才能理解一个组件做什么,你的架构就失败了——无论读者是2002年的初级开发者,还是2026年的语言模型。

这一句话,我认为是整本书在 AI 时代最重要的一句话。

你的代码不只是给人类读的,也是给 AI Agent 读的。 如果你的代码很难让 AI 理解,那意味着你的 AI Agent 在每次修改时都要承担巨大的「认知负荷」——而认知负荷会导致错误。

五、「战略性注释」:另一个被 AI 时代重新定义的概念

Ousterhout 在书里花了大量篇幅讲注释的重要性——这是书里争议最大的部分,因为它与「Clean Code」的「代码应该自解释」哲学有直接冲突。

他的核心观点是:

注释应该解释「为什么」,而不是「是什么」。

「是什么」通过代码本身来表达。「为什么」只能通过注释来传递。

在 AI 时代,这个观点有了新的紧迫性。

想象一个 AI Agent 在你的代码库里工作。它读到了这段代码:

Python

# 不好的注释
# 设置重试次数为 3
MAX_RETRIES = 3

AI 从代码本身就能知道这是「设置重试次数为3」,这个注释没有增加任何信息。

Python

# 好的注释(战略性注释)
# 重试次数设为3是因为:
# 1. 内部测试显示,>95%的瞬时网络错误在2次重试后会恢复
# 2. 第4次及以上重试与持久性故障的相关性更强,继续重试会掩盖真实错误
# 3. 这个值已经过2026年3月的负载测试验证(见 tests/load/retry_analysis.py)
MAX_RETRIES = 3

第二个注释里有三个信息:背后的推理、具体的数据依据、以及验证记录的位置。

这些信息,AI Agent 在修改代码时会直接读取。如果一个 Agent 被要求「优化错误处理」,它看到第一个注释什么都不知道,很可能把 MAX_RETRIES 改成 5 或者 10。它看到第二个注释,会理解为什么是3,并且知道任何改动都需要参考 tests/load/retry_analysis.py。

「战略性注释」在 AI 时代,就是你给未来 AI Agent 留下的上下文。它是一种特殊形式的 Context Engineering。

六、「设计两次」原则:AI 时代最被忽视的工程实践

Ousterhout 提倡「设计两次」。例如当他设计 Tk Toolkit 的 API 时:第二个设计被证明更优越。

「设计两次」的意思是:在开始实现之前,至少想出两种不同的设计方案,然后仔细比较它们的优劣,再选择最好的那个。

在 AI 时代,这个原则面临一个新的压力:

AI 让「从想法到代码」的速度极度加快,快到你根本来不及「设计两次」。

有了 Claude Codex,你可以在10分钟内就有一个工作的原型。这很好。但问题是:你的第一个想法,几乎永远不是最好的设计。

Ousterhout 给出了一个残酷的但真实的规律:

如果你在第一个可行的设计上就停下来,你实际上是在接受「所有设计方案中的第一个」——而不是经过比较之后的最好的那个。

在 AI 可以快速生成任何设计的时代,「设计两次」变成了:

让 AI 帮你生成第一个设计,然后你自己(或者和 AI 一起)真正思考一个完全不同的替代方案,然后比较。

具体操作:

text

提示:帮我设计一个 RAG 系统的检索模块。
→ AI 给出设计A
提示:现在忘掉你刚才的设计,给我一个基于完全不同架构假设的设计B。
→ AI 给出设计B
我:对比A和B,在以下场景下哪个更好:
1. 数据量增加10倍
2. 需要支持实时更新
3. 团队里有新成员需要理解代码
→ 这才是真正的设计决策

AI 加速了「生成方案」,但没有替代「比较方案」的思考过程。

七、书里有一个我不完全同意的地方:关于注释与 AI 生成代码的张力

我在写这篇文章时,遇到了一个需要诚实说明的地方。

Ousterhout 在书里对「TDD(测试驱动开发)」的态度相对保守,他认为测试不应该驱动设计。

作者对「现代」软件实践(他称之为「软件趋势」)的看法,主要是关于敏捷和 TDD,清楚地显示了学术背景而不是工业经验。

在 AI 时代,我对这个观点有不同的看法:

测试(特别是集成测试)在 AI 时代变成了 Loop Engineering 的「验证层」——它是 AI Agent 用来判断「任务是否完成」的信号。

一个没有完善测试的代码库,不只是对人类工程师不友好,更是对 AI Agent 不友好:Agent 没有可靠的反馈信号,就没有办法构建有效的 Loop。

所以我的观点是:

•
Ousterhout 关于「复杂度管理」的核心框架:完全正确,在 AI 时代更重要
•
他关于「注释优先于测试」的观点:在 AI 时代需要修正——两者都重要,但测试在 AI 时代有了新的战略价值

好的书应该让读者产生自己的观点,而不只是接受书中的观点。《A Philosophy of Software Design》做到了这一点。

八、把这本书的框架应用到 AI 系统架构设计

现在把 Ousterhout 的框架,直接应用到你日常构建的 AI 系统上:

应用1:RAG 系统的「复杂度管理」

变更放大风险点:把 embedding 模型的参数散落在代码库的10个地方。

解法(深模块):

Python

# config/rag_config.py
# 所有关于 RAG 的「为什么」都在这里
class RAGConfig:
"""
RAG 系统的中央配置。
设计决策记录(2026-04-10):
- 选择 ChromaDB 而非 Pinecone:客户数据合规要求本地存储
- top_k=5:测试显示>5时准确率不再提升但延迟增加
- chunk_size=512:在测试集上 F1 最优,见 evals/chunk_size_study.py
"""
EMBEDDING_MODEL = "text-embedding-3-small"
TOP_K = 5
CHUNK_SIZE = 512
SIMILARITY_THRESHOLD = 0.75

所有「为什么」集中在一处,AI Agent 修改任何 RAG 相关代码时都会读到这里的推理。

应用2:Multi-Agent 系统的「深模块设计」

浅模块(AI 容易生成):

每个 Agent 直接暴露内部的 client.messages.create() 调用,外部代码依赖内部实现细节。

深模块(战略设计):

Python

class SecurityReviewAgent:
"""
安全审查 Agent 的深模块接口。
外部只需要知道:
- 输入:代码 diff
- 输出:安全评分(0-10)+ 发现列表
内部实现(外部不应该依赖):
- 具体用哪个模型
- 如何构建 prompt
- 重试逻辑
"""
def review(self, code_diff: str) -> SecurityReview:
"""
这是唯一的公开接口。
无论内部怎么改,这个接口保持稳定。
"""
...

应用3:Loop Engineering 里的「认知负荷管理」

最容易被忽视的 Loop Engineering 反模式:把所有逻辑塞进一个单一的巨型 while 循环。

高认知负荷版本:

Python

while True:
# 200行混合了状态管理、API调用、错误处理、记忆更新的代码
...

低认知负荷版本(Ousterhout 会认可的):

Python

# 每个函数做一件事,名字解释它是什么,注释解释为什么
while not is_goal_achieved(state):
state = read_current_state(vault)       # 读取状态
action = decide_next_action(state)      # 决策
result = execute_action(action)         # 执行
state = update_memory(state, result)    # 更新记忆
if should_escalate_to_human(state):     # 人工介入检查
notify_human(state)
break

每行代码像一个英语句子。任何工程师(或 AI Agent)读到这段代码,不需要看实现细节就能理解整体逻辑。

设计已经成为限制性试剂,而它是工作中仍然属于我们的那部分。

九、「信息隐藏」在 AI 时代的新含义

Ousterhout 在书里反复强调一个原则:信息隐藏(Information Hiding)。

好的模块设计,应该把「关于如何实现某个功能的决策」隐藏在模块内部,不让外部代码依赖这些细节。

在 AI 时代,这个原则有了一个新的维度:

你不只是在向其他人类工程师隐藏复杂度,你也在向 AI Agent 隐藏它不需要知道的东西。

一个 AI Agent 在修复某个 bug 时,如果它能看到整个系统的所有实现细节,它可能会做出「局部正确但全局危险」的修改。

好的模块边界,让 AI Agent 只能「看到它的工作范围」,减少了它做出范围外修改的机会。

这是「信息隐藏」在 AI 协作时代最重要的新价值:它是对 AI Agent 的护栏,不只是对人类工程师的抽象。

十、我真正改变的一件事

读完这本书之后,我改变了一件具体的事情。

我开始在每个新项目里,强制自己写一份「反复杂度声明」:

Markdown

# COMPLEXITY-CONTRACT.md
# 这个项目对抗复杂度的具体承诺
## 深模块边界(这些接口不应该暴露内部细节)
- DocumentRetriever:外部只知道「给我文档」
- EvaluationEngine:外部只知道「给我评分」
- MemoryManager:外部只知道「记住这个/告诉我你记住了什么」
## 注释规范(只解释「为什么」,不解释「是什么」)
- 每个重要的设计决策必须有注释解释原因
- 每个「奇怪」的实现必须解释为什么不用显而易见的方式
## 变更放大检查点(每次 PR Review 前问自己)
- 这个变更是否需要在多于2个地方修改?
- 如果是,说明设计有问题,考虑先重构
## AI Agent 友好性检查
- AI Agent 读到这个文件是否能理解它的工作边界?
- 如果不能,说明接口设计需要改进

这个文件,存在 Obsidian Vault 的项目文件夹里,是 DECISIONS.md 的姊妹文件。

它不只是给「未来的我」看的,也是给「未来的 AI Agent」看的。

结语:设计是属于人类的最后阵地

Ousterhout 在写这本书时没有想到 AI 会在六年后写出大量代码。

但他准确地描述了一件在 AI 时代变得更加关键的事:

复杂度是所有软件项目的真正敌人,而对抗复杂度需要的是战略思维——这种思维不会因为 AI 能写代码就变得不重要,反而因为 AI 默认是战术的,而变得更稀缺。

AI 编码助手快速、高效,给人进步的幻觉——一股以极快速度飞出的代码旋风。但就像任何龙卷风一样,如果不加以控制,它是混乱的,通常缺乏可持续增长所需的架构和韧性。

你是那个控制龙卷风方向的人。

设计已经成为限制性试剂,而它是工作中仍然属于我们的那部分。

理解复杂度,设计简单系统,为 AI Agent 留下清晰的边界和理由——这是你在 AI 时代最不可替代的工程价值。

一本2018年的书,在2026年比它出版时更重要。

这本身就说明了一件事:真正的第一性原理,不会随着工具的变化而过时。


普通人如何用 AI 搭建自己的知识操作系统?

一个程序员出身的知识工作者,公开记录自己如何用 AI 工具搭建个人知识系统、把读过的书和做过的项目变成可复用资产的全过程。

我是【一只阿木木】——公开建造我的 AI 第二大脑。

我们的方向是——AI + Obsidian 的结合。但请记住:Obsidian 的灵魂不是效率,是自由。不是自动化,是代理力。不是工具帮你想,而是你借工具想得更好。
在一个许多工具承诺代替用户思考的市场中,Obsidian 赌的是我们仍然想要一个可以自己思考的地方。

欢迎加入行动营👇获取更多Obsidian + AI数字大脑实践

Image

我相信:在 AI 时代,每个普通人都该拥有一个自动生长的知识系统

欢迎关注【一只阿木木】🌊