Matt Pocock 演讲:AI 时代,软件基本功反而更重要 | AI Engineer Europe

深入交流 454 views 3 replies
#1 ·

Matt Pocock(Total TypeScript / AI Hero 作者)在 AI Engineer Europe 的演讲:教了 18 个月"用 AI agent 做真实工程"之后,他得出结论——AI 编程工具既被高估、又确实强大,决定成败的不是工具,而是几十年没过时的软件基本功。这场演讲讲的就是这套方法论。

· 作者:Matt Pocock
· 原视频:"Software Fundamentals Matter More Than Ever" — Matt Pocock
· 频道:AI Engineer(https://www.youtube.com/_0_)
· 配套仓库:github.com/mattpocock/skills
· 时间:2026.4.23

https://www.bilibili.com/video/BV1pq8B64EFH/

#2 ·

AI 时代,软件基本功反而更重要

作者:Matt Pocock


我有一个消息想告诉你,希望它能带来一些安慰——尤其是如果你曾觉得自己的技能在这个新时代已经毫无价值的话。

我认为软件基础现在依然重要。比以往任何时候都更重要。

我是一名教师。最近我正在教授一门名为《Claude 代码实战工程师》的课程,非常吸引人,也极其发人深省。在准备这门课程的过程中,我不得不制定一个关于 AI 编程的课程大纲。这简直是噩梦般的困难——因为事物总在不断变化。AI 代表全新的范式,似乎我们需要摒弃所有旧规则,才能引入新知识。

但我想告诉你:别急。那些旧书里的智慧,恰恰是现在最需要的东西。


"从规格到代码"的幻灭

围绕 AI 编程趋势,出现了一种运动——"从规格到代码"(Spec to Code)。它主张:你可以编写应用程序应如何运作的规格说明,然后用 AI 将其转化为代码。如果应用出现问题,你只需返回修改规格,无需查看代码,只需调整规格,再次运行"编译器",最终生成更多代码。

我听说过这个。我也尝试过。

我注意到的是:我会运行它,尽量不看代码。但我忍不住会去看。然后我发现——首先得到代码,运行它,得到更差的代码。再次尝试,代码变得更差且重复出现。持续运行编译器,不断运行编译器,最终生成一堆垃圾代码。

如果你也经历过这个,你就知道我在说什么。

这行不通。认为我们可以忽略代码、让代码自行管理的想法,本质上就是另一种逃避编程。

我当时想:我得怎么修复这个"编译器"?我得怎么让它不再每次生成错误代码,或者更糟糕的代码?

于是我思考:好吧,我需要用英语向 LLM 解释,什么是良好的代码库。

我翻出了一本旧书——John Ousterhout 的《软件设计哲学》(A Philosophy of Software Design)。去亚马逊看看,买下它。他定义了什么是糟糕的代码。他称之为"复杂代码"——复杂性涉及软件系统的结构,让系统难以理解和修改。糟糕的代码库就是难以修改的代码。如果你修改代码却引发新 bug,那就是糟糕的代码库。优秀的代码库易于修改。

然后我又翻了《实用程序员》(The Pragmatic Programmer)。去亚马逊看看,买下它。他们有一章讲"软件熵"——这正是我看到的现象。熵指事物趋向混乱和崩溃。大多数软件系统就是这样运行的。每次修改代码库时,只关注局部改动而忽略整体设计,代码库就会越来越差。

这正是反复运行"编译器"时发生的事情。

"Spec to Code"运动的核心理念是:代码廉价。

我不认同这个观点。我认为代码并不廉价。事实上,糟糕的代码成本最高。因为如果你有一个难以修改的代码库,你就无法充分利用 AI 提供的全部价值。AI 在良好的代码库中表现极为出色。

这意味着优质代码库比以往更重要。这意味着软件基础比以往更重要。

这就是这篇文章的核心观点。


你可能遇到的 AI 失败模式(以及如何用经典智慧解决它们)

接下来我要讨论你可能遇到的 AI 相关失败模式——或者你尚未遇到但迟早会遇到的——以及如何通过回归经典书籍并遵循良好实践来避免它们。


失败模式 #1:AI 没有按我预期执行

你以为自己脑海中有一个好主意,但 AI 却做了完全不同的事情。或者你生成了一些类似规格说明的东西,结果它制作了你并不想要的东西。

《实用程序员》中有一句话说得好:没有人确切知道自己想要什么。 你与 AI 之间存在沟通障碍。当你与 AI 交流时,这类似于 AI 在收集需求——它正在从你这里梳理需求,明确你需要什么。

这让我想到了 Frederick P. Brooks 的《设计原本》(The Design of Design)。书中提到一个概念叫设计概念(Design Concept):当多人共同设计某事物时,会有一个想法在你们之间流转——这个正在构建的事物的瞬时概念。它不是资产,无法写入 Markdown 文件。它是构建事物的无形理论。

所以我想明白了:我和 AI 缺乏共同的设计概念。

我总结出一个技巧,非常简单,叫做 GRIME。方法是:不断深入询问计划的每个细节,直到达成共识。逐层分析设计树的每个分支。这是 Brooks 的另一个观点——逐个解决决策之间的依赖关系。

这项技能放在我的 GitHub 仓库里,已经超过一万三千颗星。它突然失控了,迅速走红,人们非常喜欢。这几行提示词意味着 AI 会向你提问四十次、六十次问题。我曾遇到它连续提问一百次,直到满意为止,直到双方达成共同理解。

这意味着将 AI 转化为一种对手——它不断抛出想法,试图达成共识。而你生成的对话内容,可以直接转化为产品需求文档(PRD)。如果是小改动,可直接生成任务工单,然后你的异步代理会接手处理。

我觉得这比 Claude Code 的默认"计划模式"更好。Claude Code 的计划模式非常急于创建资产,迫切想要制定计划立即执行。而我认为,先达成设计共识更佳。


失败模式 #2:AI 过于啰嗦

你和 AI 几乎在不同频道沟通。AI 在长篇大论,用太多词汇解释自己的行为。你们的语言体系不同。

如果你长期从事开发工作,并与领域专家协作开发应用——假设领域专家要求你构建一个关于"微芯片"的东西,你完全不了解微芯片——你需要建立共同语言。否则他们会使用专业术语,你无法理解;你将其转化为代码,可能自己也不懂,领域专家更不会懂。你与专家之间存在语言鸿沟。

于是我回归了领域驱动设计(DDD)。这是我仍在探索的边缘领域,但所有我读到的内容都让我觉得——这就像我耳朵里的天籁之音,我简直爱死它了。

DDD 有一个核心概念:通用语言(Ubiquitous Language)。通过通用语言,开发者之间的对话、代码中的表达、以及与领域专家的交流,都源自同一领域模型。

在我们的场景中,它本质上是一个 Markdown 文件——包含你和 AI 共同理解的术语列表。你真正聚焦这些术语,确保它们与实际含义一致,并在代码中频繁使用。当你谈论代码时,当你与领域专家交流时,或者在我们的情况中——与 AI 对话时。

所以我开发了一个技能:通用语言技能。简单扫描代码库,识别术语,然后生成一个 Markdown 文件——包含大量术语的 Markdown 表格。然后我将其传递给 AI。我也能阅读它。我甚至一直保持它打开,在与 AI 对话和规划时。

通过阅读 AI 的思考轨迹,我发现它不仅提升了规划效率,还让 AI 以更简洁的方式思考,实际上使实现更贴近原计划。

这绝对是个强力工具。效果惊人。

与 AI 建立共享语言。


失败模式 #3:代码无法运行

假设你已与 AI 达成共识,清楚自己要构建的目标,AI 已正确构建——但它无法运行。运行效果不佳。

显然我们可以改进反馈循环:

  • 使用静态类型。 如果你不使用 TypeScript,那太疯狂了。
  • 给 LLM 访问浏览器的能力。 如果你在开发前端应用,绝对需要让它能查看环境。
  • 自动化测试。 显然你也需要这个。

但我注意到一个问题:即使有这些反馈循环,语言模型并没有很好地利用它们。它并没有像经验丰富的开发者那样充分利用反馈循环。它所做的往往是同时做太多事情——生成大量代码,然后才想"哦,我应该进行类型检查",或者"或许应该为这部分写测试"。

这在《实用程序员》中被描述为 "超出行车灯范围"——本质上是行驶过快。因为反馈速度就是你的限速。这意味着你应该边写代码边测试,采取小步谨慎推进。而 AI 默认在这方面表现很差。

所以,第三个技能是 TDD(测试驱动开发)。你应该使用测试驱动开发,因为 TDD 迫使语言模型采取小步操作:先编写测试用例,让测试通过,然后重构代码使其更优雅并考虑设计。

但问题在于,测试本身非常困难。测试向来都很困难。原因在于需要做出大量不同决策:需要确定测试单元的大小,需要决定如何模拟,需要确定要测试哪些行为。所有这些决策相互关联。如果你测试一个大单元,比如整个庞大应用,可能会非常不稳定,可能不需要测试太多行为。如果你只测试一个小单元,需要模拟很多东西。这些都是相互关联的。

多年来,我的整个开发生涯都在思考这个问题。我们发现:好的代码库更容易测试。

没错,我们现在开始回到代码的重要性。代码库越好,反馈循环就越有效,因为你能给语言模型提供更好的反馈,它就能生成更好的代码。


深度模块 vs. 浅层模块

于是我开始思考:一个好的代码库应该是什么样的?可测试的代码库应该具备哪些特征?

我们去找 John Ousterhout。他谈到代码库中应有深度模块(Deep Modules),而不是浅层模块。不是那种包含大量函数的模块。应有相对较少的大型深度模块,搭配简单接口。

快速对比一下:

  • 深度模块: 大量功能隐藏在简单接口背后。隐藏复杂性。你可以查看深度模块内部,如果需要的话,但不需要这样做。只需使用接口即可。
  • 浅层模块: 功能较少,复杂接口。

代码库中的浅层模块看起来像许多不同微小的模块块。AI 需要遍历和导航这些模块,这对 AI 探索来说非常困难。当你遇到这样的代码库时——讽刺的是,AI 其实很擅长创建这类代码库——会导致 AI 无法理解代码逻辑。它会尝试探索代码,但由于结构混乱,充斥着浅层模块,可能无法及时找到正确模块,或无法理清所有依赖关系。

而充满深度模块的代码库呢?同样是这些代码,但结构被边界划分清晰,顶部有简单的接口。这些接口需要精心设计并严格控制,否则可能会破坏设计。但实现部分可以部分交给 AI。

我掌握了一个"优化代码架构"的技能。事实证明实现起来相当复杂,但这是一系列可重复使用的步骤:探索代码库,寻找机会,在相关代码处将所有内容封装在深层模块中。

这是一个可测试的代码库,因为代码边界非常简单——在接口处进行测试,通过该接口验证,即可投入使用。这是一个鼓励 TDD 的代码库。


失败模式 #4:大脑跟不上节奏

假设你的反馈循环正常运作,事情开始运转,你能比以往更快交付代码。但大脑跟不上节奏。

如果你在开发生涯中感到前所未有的疲惫——我也是。真是累人。

我认为浅层模块的代码库实际上让大脑更难处理。你和 AI 都需要记住所有信息。而深度模块对你阅读和理解代码更简单。

同时,你可以将这些深层模块视为灰盒。你可以说:我只需设计接口,不必过多担心或审查实现细节。这在应用中非关键部分显然可行(在金融等关键场景中可能不行)。但在应用的许多模块中,无需过多考虑实现细节,只要模块外部有可测试边界,只要理解其目的并从外部设计。

这确实拯救了我的大脑。 因为我只需说:"好的,AI,让我处理内部细节。这个大块,我只需从外部测试并验证。"

设计接口,委托实现细节。


每天投资于系统设计

但这意味着每当我们修改代码时,每当我们进行规划时,我们需要考虑并意识到应用程序中的模块。我们需要非常熟悉这些模块。这必须成为我们的通用语言,我们也需要将其融入规划能力中。

所以我在 PRD 中编写需求时,会明确说明模块变更及模块内的接口是如何被修改的。我时刻都在思考这些。

这源于 Kent Beck 的理念:每天投资于系统设计。

这才是核心所在。因为"规格到代码"的理念中,我们并未投资于系统设计。我们正在剥离系统设计,我们正在摒弃这些。而我认为这绝对至关重要。


结语:代码并非廉价

这是我希望你记住的信息:代码非常重要。

如果我们认为 AI 是优秀的现场程序员——一种战术型程序员,前线的中尉,进行代码修改——那么你需要更高层的人。你需要进行战略层面的思考。

而这正是你的职责。

这需要我们二十年来一直在使用的软件基础技能。


如果你对这里提到的任何技能感兴趣,请查看 GitHub 仓库 mattpocock/skills。如果你对我的培训课程感兴趣,或任何免费资源,我在 YouTube,我在 Twitter。我也是 AI Hero 的开发者,我有一个你可以订阅的新闻简讯。

非常感谢你读到这里。希望这能增强你在 AI 时代的信心。

你确实能产生积极影响。

💯
1
#3 ·

架构设计出问题 软件熵带来的技术债确实难评

❤️
1
#4 ·

软件工程学习纲领

摘要

现代软件工程教育经常以语言、框架和工具为组织单位:JavaScript、React、Spring、Go、Rust、MySQL、Kafka、Kubernetes。这样的学习路径具有较高的短期生产效率,却容易产生一种结构性问题:工程师掌握了大量 API、配置方法和框架惯例,却没有建立足够稳定的、跨语言和跨系统的知识模型。

当技术栈发生变化时,这类知识需要被大规模重建;当业务复杂度超过框架默认模型能够自然处理的范围时,工程师也容易陷入局部修补、抽象失控、状态混乱和依赖扩散。

生成式 AI 进一步放大了这一问题。AI 显著降低了代码生成、API 查询、语法迁移和样板代码生成的成本,但同时降低了项目的可维护性。于是,工程能力的瓶颈正在从:

Can you write the code?

转向:

Do you understand what code should exist?
Where should it exist?
What should depend on what?
What is the data model?
What are the invariants?
What is the concurrency model?
What representation should cross the boundary?

1. 软件工程学习的对象不应该是框架本身

前端框架、后端框架、数据库 ORM、RPC 框架、状态管理工具,本质上都是解决特定工程问题的工具。

React 解决 UI 状态到界面结构之间的映射与更新问题。

Spring 处理对象生命周期、依赖组织、Web 请求处理等工程问题。

Kafka 提供一种持久化分布式日志及其消费模型。

PostgreSQL 提供关系数据模型、事务、索引、查询执行以及持久化能力。

它们当然都值得学习。

问题在于:

它们不应该成为整个知识结构的根节点。

框架具有明显的历史性。

某一种框架可能流行十年,也可能几年后被新的抽象取代。

而另一些问题却长期稳定存在:

  • 数据如何表示?
  • 状态由谁拥有?
  • 一个模块应当隐藏什么?
  • 两个组件为什么发生依赖?
  • 同一个行为如何具有不同实现?
  • 同步调用和异步调用有什么区别?
  • 内存中的对象如何变成网络上的字节?
  • 网络失败以后应该如何恢复?
  • 数据如何从原始记录变成业务聚合?
  • 程序如何从源代码变成执行中的进程?
  • 并发修改如何保持正确性?
  • 一个系统如何在节点失效后继续运行?

这些才构成软件工程真正稳定的知识骨架。

因此,一个合理的学习优先级是:

稳定的计算机科学概念
        ↓
软件设计与系统设计概念
        ↓
语言对这些概念的表达机制
        ↓
框架对这些机制的工程封装
        ↓
具体业务实现

而不是:

Framework A
↓
Framework B
↓
Framework C
↓
不断重新学习

2. AI 改变的不是软件工程基本问题,而是实现成本

生成式 AI 对软件开发最大的影响之一,是大量降低了局部实现问题的成本。

例如:

  • 编写排序和遍历代码;
  • 生成 SQL;
  • 编写 JSON 转换;
  • 创建 DTO;
  • 编写 CRUD;
  • 编写 HTTP Client;
  • 生成测试样例;
  • 将一种语言翻译为另一种语言;
  • 查询一个不熟悉的 API;
  • 阅读陌生代码;
  • 解释框架内部调用链。

这些任务过去需要大量记忆、编码熟练度和搜索能力。

现在越来越多可以交给 AI 完成。

但是有一类问题并没有因此消失:

应该抽象什么?

例如:

这个数据应该属于 User 还是 Order?

这个字段应该存储还是计算?

这里应该使用事件还是直接调用?

这个对象应该拥有数据库访问能力吗?

这个缓存是否允许短暂不一致?

这里是否要求事务?

这个操作应该幂等吗?

这个异步任务失败后如何恢复?

业务状态机有哪些非法状态?

一个服务的边界到底在哪里?

AI 可以给出候选答案,但如果工程师没有自己的系统模型,就很难判断这些答案是否成立。

因此,AI 时代反而提高了以下能力的重要性:

Abstraction
Modeling
Decomposition
Invariant Reasoning
Dependency Management
Representation Design
Concurrency Reasoning
Failure Reasoning

换句话说:

AI 降低了代码的生产成本,因此提高了判断“什么代码值得存在”的价值。


3. 软件系统可以统一理解为表示、状态与转换

如果试图寻找前端、后端、数据库、编译器、数据工程之间的共同结构,会发现一个极其稳定的模式:

Representation
    ↓
Transformation
    ↓
Representation

软件系统不断将一种表示转换成另一种表示。

例如,一个 Web 应用可能具有如下数据路径:

HTTP Bytes
↓
UTF-8
↓
JSON
↓
DTO
↓
Domain Model
↓
Business Rules
↓
View Model
↓
DOM
↓
Pixels

数据仓库则可能表现为:

Operational Data
↓
Raw Layer
↓
Staging
↓
Normalized Model
↓
Warehouse
↓
Data Mart
↓
Aggregate
↓
Dashboard

编译器也是如此:

Source Text
↓
Token Stream
↓
AST
↓
IR
↓
Machine Instructions

网络协议同样如此:

Application Object
↓
Serialization
↓
Bytes
↓
Protocol Frame
↓
Packet
↓
Bytes
↓
Deserialization
↓
Application Object

因此,可以把软件工程的一大部分归结为:

Representation Design and Transformation.

这也是为什么数据结构、类型、编码、序列化、数据库、编译原理和 API 设计之间存在比表面上更深的联系。


4. 另一个核心问题:状态属于谁

软件系统的复杂性往往不是来自函数数量,而是来自状态。

任何复杂系统都可以不断追问:

What state exists?

Who owns the state?

Who may mutate it?

When may it change?

Who observes it?

How is consistency maintained?

How is it persisted?

How is it replicated?

What happens after failure?

这组问题横跨整个计算机系统。

在前端中,它表现为:

Component State
Global State
Server State
Derived State

在数据库中,它表现为:

Transaction
Isolation
MVCC
Durability

在操作系统中,它表现为:

Process State
Memory
File Descriptor
Thread State

在分布式系统中,则表现为:

Replicated State
Consensus
Eventual Consistency
Conflict Resolution

因此,对状态的理解应当成为软件工程课程的主线之一。


5. 第三个核心问题:依赖

如果数据和状态决定“系统拥有什么”,依赖则决定“系统如何组织”。

复杂代码最终往往表现为一个依赖图:

A → B → C
    ↓
    D

一个良好的架构,本质上是在控制:

  • dependency direction;
  • dependency visibility;
  • dependency lifetime;
  • dependency stability;
  • dependency fan-in;
  • dependency fan-out。

设计模式也可以从这一视角重新理解。

例如 Strategy 并不首先意味着“创建一个 Strategy 接口”。

它解决的是:

Stable Caller
    ↓
Variable Behavior

通过引入抽象:

Stable Caller
    ↓
Abstraction
    ↓
Implementation A
Implementation B

Adapter 解决的是接口表示不兼容:

Expected Interface
        ↓
      Adapter
        ↓
Existing Interface

Observer 解决的是一对多传播关系:

State Producer
      ↓
    Event
 ↓    ↓    ↓
A     B     C

Dependency Inversion 解决的是依赖方向与实现细节之间的关系。

因此:

设计模式不是应该记忆的代码模板,而是对常见依赖结构、变化结构和协作关系的命名。


6. 为什么设计模式不能成为真正的第一课

传统的软件工程学习经常把设计模式放在非常早的位置。

这存在一个问题。

如果学习者尚未理解:

  • module;
  • information hiding;
  • abstraction;
  • interface;
  • implementation;
  • coupling;
  • cohesion;
  • composition;
  • dependency;

那么设计模式容易退化成模式匹配:

看到 new → Factory

看到不同算法 → Strategy

看到通知 → Observer

这种学习方式最终可能增加而不是降低复杂度。

更合理的依赖顺序应该是:

Module
  ↓
Interface / Implementation
  ↓
Information Hiding
  ↓
Coupling / Cohesion
  ↓
Composition
  ↓
Dependency
  ↓
Change Boundary
  ↓
Design Principle
  ↓
Design Pattern

设计模式只是这一系列概念的后续表达。


7. 语言不是知识边界,而是概念的具体实现

软件工程师当然需要熟悉语言。

但语言更适合作为:

抽象概念的实验环境。

例如“多态”并不属于 Java。

不同语言只是采用不同方式表达多态。

Java:

interface Storage {
    void save(Data data);
}

Go:

type Storage interface {
    Save(Data) error
}

Rust:

trait Storage {
    fn save(&self, data: Data);
}

TypeScript:

interface Storage {
  save(data: Data): void;
}

这些语法不同,但背后的问题相同:

Caller
↓
Stable Contract
↓
Replaceable Implementation

同样,闭包也不是 JavaScript 特有的概念。

JavaScript、Python、Go、Rust、Swift、Kotlin 等现代语言都提供某种形式的闭包。

异步也不是 JavaScript 专属知识。

不同语言可能分别使用:

JavaScript → Promise / async-await
Java       → Future / CompletableFuture
Go         → goroutine / channel
Rust       → Future / async-await
Python     → asyncio

理解这些机制的目标不是记住 API,而是理解:

Task
↓
Suspend
↓
Other Work Executes
↓
Resume

以及它和线程、事件循环、协程、调度器之间的关系。


8. 多语言学习的真正意义

因此,本教材推荐有限度地进行多语言比较。

目的不是“掌握更多语言”,而是识别:

哪些东西属于计算模型,哪些东西只是语言语法。

典型比较维度包括:

概念 TypeScript Java Go Rust
抽象接口 Structural Interface Interface Interface Trait
参数多态 Generic Generic Generic Generic
行为多态 Structural Subtyping Interface Trait
闭包 Closure Lambda Closure Closure
错误 Exception / Value Exception Error Value Result
空值 null / undefined null nil Option
并发 Promise Thread/Future goroutine Future
内存 GC GC GC Ownership
生命周期 Runtime Runtime Runtime Static Lifetime

这样的比较最终帮助学习者形成语言无关的认知:

Concept
↓
Language Mechanism
↓
Library / Framework

9. 数据处理能力是通用工程能力

大量业务程序归根结底都在处理数据。

最基础的数据处理操作实际上非常有限:

map
filter
reduce
group
join
sort
partition
aggregate

这些操作不断以不同形式出现。

JavaScript Array API:

map / filter / reduce

SQL:

SELECT
WHERE
GROUP BY
JOIN
ORDER BY

数据框架:

map
filter
groupBy
join
aggregate

分布式数据处理框架:

map
shuffle
reduce

因此,所谓“业务数据处理能力”不应只理解成会写 SQL 或 JSON 转换。

更核心的是能够判断:

输入数据是什么表示?

输出数据需要什么表示?

中间存在什么不变量?

哪些字段是原始事实?

哪些字段是派生结果?

在哪一层应该聚合?

在哪一层应该过滤?

哪些计算应该提前完成?

哪些计算应该延迟到查询阶段?

这实际上连接了:

Data Structure
↓
Data Modeling
↓
Database
↓
Warehouse
↓
Data Architecture

10. 数据必须分层

原始数据直接进入业务和展示层,通常意味着未来的高耦合。

一个稳定系统往往具有多层表示。

典型业务系统:

External Representation
↓
Transport Model
↓
Domain Model
↓
Persistence Model
↓
Query Model
↓
Presentation Model

数据仓库:

Source
↓
Raw
↓
Staging
↓
Warehouse
↓
Mart
↓
Aggregate
↓
Presentation

这些层级不一定全部需要真实存在。

关键思想是:

不同边界需要不同表示。

如果一个对象同时承担:

  • 网络协议;
  • 数据库结构;
  • 业务模型;
  • UI 展示;

那么任意一层变化都可能向整个系统传播。

这实际上又回到了设计中的核心问题:

Information Hiding
Boundary
Coupling
Representation

11. 编码与序列化不是边缘知识

软件系统持续跨越各种边界:

Memory
↓
File
↓
Network
↓
Process
↓
Machine

一旦跨越边界,内存中的对象就必须被转换。

典型路径:

Object
↓
Serialization
↓
Bytes
↓
Transport / Storage
↓
Bytes
↓
Deserialization
↓
Object

因此,每一个软件工程师都应该理解:

Character
Code Point
Encoding
Byte
Serialization
Schema
Compatibility

并能够解释:

ASCII
Unicode
UTF-8
UTF-16
Endianness
Integer Encoding
Floating Point
JSON
Protocol Buffers
Avro
MessagePack

进一步还必须理解:

Backward Compatibility
Forward Compatibility
Schema Evolution

因为生产系统中的数据不会随着代码部署自动消失。


12. 操作系统是理解运行时的基础

大量高级框架能力最终建立在操作系统提供的几个核心抽象之上:

Process
Thread
Memory
File
Socket
Scheduler

因此,所谓同步、异步、并发,并不能只停留在:

await someFunction()

需要进一步理解:

Process
├── Address Space
├── File Descriptor
└── Threads

线程又涉及:

Thread
├── Scheduling
├── Context Switch
├── Race Condition
├── Mutex
├── Semaphore
├── Condition Variable
└── Atomic Operation

IO 又进一步导向:

Blocking IO
Non-blocking IO
IO Multiplexing
Asynchronous IO

这些机制最终产生不同并发模型:

Thread Pool
Event Loop
Coroutine
Actor
CSP

只有理解这一层,才能真正理解不同语言中的异步机制为什么具有不同性质。


13. 网络是分布式软件的基础运行环境

现代软件几乎不可避免地通过网络协作。

因此,一个工程师不应只知道 HTTP 方法,而应该建立基本协议栈:

Application
↓
HTTP / RPC
↓
TLS
↓
TCP / QUIC
↓
IP

至少需要理解:

DNS
Connection
Socket
Handshake
Timeout
Retry
Keep-Alive
Multiplexing
Congestion Control
Proxy
Reverse Proxy
Load Balancing

尤其重要的是理解:

网络调用与本地函数调用具有根本不同的失败模型。

本地函数调用通常是:

success / failure

远程调用则可能出现:

request never arrived

request arrived but response lost

request executed twice

server executed but client timed out

partial network failure

connection reset

retry causes duplicate effect

这正是:

Timeout
Retry
Idempotency
Deduplication

等分布式系统概念产生的基础。


14. 编译原理训练的是表示转换能力

编译原理的长期价值不只体现在“实现编译器”。

它给出了一个非常清晰的复杂系统分层范例:

Source
↓
Lexer
↓
Token
↓
Parser
↓
AST
↓
Semantic Analysis
↓
Intermediate Representation
↓
Optimization
↓
Code Generation

从这个结构中可以理解大量现实软件。

例如:

SQL Parser
GraphQL Parser
Template Engine
Babel
ESLint
TypeScript Compiler
Configuration Language
DSL

它们本质上都包含某种:

Parse
↓
Represent
↓
Analyze
↓
Transform
↓
Emit

因此,学习编译原理不仅是学习 compiler。

它实际上是在训练:

如何设计中间表示,以及如何将复杂问题分解成多个稳定转换阶段。


15. 架构不是模式的堆积,而是约束关系

很多工程师在基础不足时直接学习:

DDD
Clean Architecture
Hexagonal Architecture
CQRS
Event Sourcing
Microservices

容易形成另一个误区:

架构是一组应该套用的高级模板。

事实上,架构更接近:

对大型系统中依赖、状态、数据、边界和变化关系施加长期约束。

因此架构的前置知识应该包括:

Module
+
Abstraction
+
Dependency
+
Data Modeling
+
Persistence
+
Concurrency
+
Network
+
Failure Model

在这些知识之上,才能正确理解:

Layered Architecture
Hexagonal Architecture
Clean Architecture
Event-Driven Architecture
CQRS
Microservices

架构模式最终解决的仍然是前面那些基本问题,只不过作用范围更大。


16. DDD 的本质首先是业务模型边界

领域驱动设计同样不应该从术语开始。

DDD 最重要的问题是:

现实业务中的概念
↓
如何形成稳定的软件模型?

其核心概念:

Ubiquitous Language
Bounded Context
Entity
Value Object
Aggregate
Repository
Domain Service
Domain Event

实际上围绕两个问题展开:

Business Boundary
+
State Invariant

例如 Aggregate 的关键并不是“对象集合”,而是:

哪一组状态需要在一个一致性边界中维护业务不变量?

Bounded Context 的关键也不是目录结构,而是:

某个业务术语在哪一个语义边界内具有唯一而明确的定义?

因此,DDD 依赖于前面已经建立的数据模型、接口、模块、状态和事务知识。


17. 分布式系统是操作系统、数据库和网络的汇合点

分布式系统不是一个孤立课程。

它可以看成三棵知识树的汇合:

Operating Systems
        \
         \
Database ─────→ Distributed Systems
         /
        /
Networking

其核心问题包括:

Replication
Partitioning
Consistency
Consensus
Leader Election
Distributed Transaction
Idempotency
Retry
Timeout
Backpressure
Failure Detection
Eventual Consistency

这些概念共同回答一个问题:

当数据、计算和失败跨越多台机器以后,如何继续维持系统的正确性和可用性?


18. 前端应该位于知识图中的什么位置

前端不是“不值得学习”。

更准确的说法是:

前端框架不是整个软件知识体系的根,而是 Web 平台能力之上的一个专业分支。

基础知识可以向不同领域展开:

                        React / Vue
                       /
Core Knowledge ─── Backend
                       \
                        Data Engineering
                       \
                        Distributed Systems

如果进一步进入高级前端,则仍然存在大量专业知识:

Browser
↓
HTML Parsing
↓
DOM
↓
CSSOM
↓
Style Calculation
↓
Layout
↓
Paint
↓
Composite

以及:

State
↓
Reactive Dependency
↓
Scheduling
↓
Rendering
↓
Reconciliation

还有:

HTTP
↓
Caching
↓
Resource Scheduling
↓
SSR
↓
Streaming
↓
Hydration

这些知识对于简单 CRUD 页面可能收益有限,但对于:

  • 大型交互应用;
  • 编辑器;
  • Web IDE;
  • 图形系统;
  • 数据可视化;
  • 性能工程;
  • 跨端基础设施;
  • UI Framework;

具有非常高的工程价值。

因此,正确的问题不是:

前端值不值得学?

而是:

对于当前问题,应该深入到知识图中的哪一层?

19. 本教材的核心知识 DAG

下面给出整本教材的核心依赖关系。

flowchart TD

    A[离散数学基础] --> B[数据结构]
    A --> C[逻辑与集合]

    B --> D[算法基础]
    B --> E[数据表示]

    C --> F[类型与关系]

    D --> G[复杂度分析]

    E --> H[字节与二进制表示]
    H --> I[字符编码]
    H --> J[序列化]
    J --> K[Schema]
    K --> L[Schema Evolution]

    B --> M[变量与状态]
    M --> N[作用域]
    N --> O[闭包]

    F --> P[类型系统]
    P --> Q[多态]
    P --> R[泛型]
    P --> S[代数数据类型]

    O --> T[高阶函数]
    T --> U[函数组合]
    T --> V[纯函数与副作用]

    Q --> W[接口]
    W --> X[实现]
    W --> Y[抽象]

    Y --> Z[模块]
    Z --> AA[封装]
    AA --> AB[信息隐藏]

    AB --> AC[耦合]
    AB --> AD[内聚]

    AC --> AE[依赖]
    AD --> AE

    AE --> AF[组合]
    AE --> AG[依赖反转]

    AF --> AH[设计原则]
    AG --> AH

    AH --> AI[设计模式]

    AI --> AJ[Strategy]
    AI --> AK[Adapter]
    AI --> AL[Observer]
    AI --> AM[Decorator]
    AI --> AN[Factory]
    AI --> AO[Command]
    AI --> AP[State]
    AI --> AQ[Composite]

    B --> AR[数据建模]
    F --> AR

    AR --> AS[实体与值对象]
    AR --> AT[关系模型]
    AT --> AU[关系代数]
    AU --> AV[SQL]

    AV --> AW[Join]
    AV --> AX[Aggregation]
    AV --> AY[Window]

    AT --> AZ[数据库存储]
    AZ --> BA[Index]
    AZ --> BB[Query Execution]

    AV --> BC[Transaction]
    BC --> BD[ACID]
    BC --> BE[Isolation]
    BE --> BF[MVCC]

    AR --> BG[数据分层]
    BG --> BH[ETL / ELT]
    BH --> BI[Data Warehouse]
    BI --> BJ[Data Mart]
    BJ --> BK[Analytics Model]

    M --> BL[进程]
    BL --> BM[线程]
    BM --> BN[并发]

    BN --> BO[Race Condition]
    BO --> BP[Mutex]
    BO --> BQ[Atomic]
    BP --> BR[Condition Variable]
    BP --> BS[Semaphore]
    BP --> BT[Deadlock]

    BL --> BU[虚拟内存]
    BL --> BV[文件系统]

    BV --> BW[IO]
    BW --> BX[Blocking IO]
    BW --> BY[Non-blocking IO]
    BY --> BZ[IO Multiplexing]
    BZ --> CA[Event Loop]

    BN --> CB[Coroutine]
    BN --> CC[Actor]
    BN --> CD[CSP]

    CA --> CE[Async/Await]
    CB --> CE

    H --> CF[机器指令]
    CF --> CG[汇编]
    CG --> CH[目标文件]
    CH --> CI[Linking]
    CI --> CJ[Executable]
    CJ --> BL

    I --> CK[Source Text]
    CK --> CL[Lexer]
    CL --> CM[Token]
    CM --> CN[Parser]
    CN --> CO[AST]
    CO --> CP[Semantic Analysis]
    CP --> CQ[IR]
    CQ --> CR[Optimization]
    CR --> CS[Code Generation]
    CS --> CF

    BW --> CT[Socket]
    CT --> CU[TCP]
    CU --> CV[TLS]
    CV --> CW[HTTP]
    CU --> CX[QUIC]
    CX --> CY[HTTP/3]

    CW --> CZ[RPC]
    CW --> DA[Web Architecture]

    BC --> DB[Persistence]
    AE --> DC[Architecture Boundary]
    DB --> DC

    DC --> DD[Layered Architecture]
    DC --> DE[Hexagonal Architecture]
    DC --> DF[Clean Architecture]

    AS --> DG[Domain Modeling]
    BC --> DG
    DC --> DG

    DG --> DH[DDD]
    DH --> DI[Bounded Context]
    DH --> DJ[Aggregate]
    DH --> DK[Domain Event]

    CU --> DL[Distributed Failure Model]
    BC --> DL
    BN --> DL

    DL --> DM[Distributed Systems]

    DM --> DN[Replication]
    DM --> DO[Partitioning]
    DM --> DP[Consistency]
    DM --> DQ[Consensus]
    DM --> DR[Leader Election]
    DM --> DS[Idempotency]
    DM --> DT[Retry]
    DM --> DU[Timeout]
    DM --> DV[Backpressure]

    DN --> DW[Eventual Consistency]
    DO --> DW

    DK --> DX[Event-Driven Architecture]
    DM --> DX

    DG --> DY[CQRS]
    DX --> DY

    DM --> DZ[Microservices]
    DC --> DZ

    DA --> EA[Browser Architecture]
    EA --> EB[DOM]
    EA --> EC[CSSOM]
    EB --> ED[Layout]
    EC --> ED
    ED --> EE[Paint]
    EE --> EF[Composite]

    M --> EG[UI State]
    EG --> EH[Reactive System]
    EH --> EI[Scheduler]
    EI --> EJ[Rendering]
    EJ --> EK[Reconciliation]

    CW --> EL[Caching]
    DA --> EM[SSR]
    EM --> EN[Streaming]
    EN --> EO[Hydration]

20. DAG 的主干

如果去掉支线,整个课程可以进一步压缩成七条主链。

20.1 软件设计链

变量
↓
函数
↓
作用域
↓
闭包
↓
类型
↓
接口
↓
抽象
↓
模块
↓
封装
↓
信息隐藏
↓
耦合 / 内聚
↓
依赖
↓
组合
↓
设计原则
↓
设计模式
↓
架构

20.2 数据链

数据结构
↓
数据表示
↓
数据建模
↓
关系模型
↓
SQL
↓
事务
↓
索引
↓
查询执行
↓
数据分层
↓
数据仓库
↓
分布式数据系统

20.3 数据表示链

Bit
↓
Byte
↓
Integer
↓
Floating Point
↓
Character
↓
Unicode
↓
UTF-8
↓
Serialization
↓
Schema
↓
Schema Evolution
↓
Protocol

20.4 系统链

Machine Instruction
↓
Process
↓
Virtual Memory
↓
Thread
↓
Concurrency
↓
Synchronization
↓
IO
↓
Socket
↓
Network

20.5 编程语言链

Source Text
↓
Lexer
↓
Token
↓
Parser
↓
AST
↓
Semantic Analysis
↓
IR
↓
Machine Code
↓
Runtime

20.6 分布式系统链

Network
+
Concurrency
+
Database
↓
Distributed Failure
↓
Replication
↓
Partition
↓
Consistency
↓
Consensus
↓
Distributed Architecture

20.7 业务架构链

Data Modeling
↓
Invariant
↓
Domain Model
↓
Transaction Boundary
↓
Bounded Context
↓
Domain Event
↓
Architecture
↓
Complex Business System

21. 推荐的教材拓扑序

DAG 不要求只有一种学习顺序。

但对于大多数软件工程师,可以使用下面这一拓扑排序:

01 数据结构
02 变量与状态
03 函数
04 作用域
05 闭包
06 类型
07 多态
08 泛型
09 接口与实现
10 抽象
11 模块
12 封装
13 信息隐藏
14 耦合与内聚
15 组合
16 依赖
17 设计原则
18 设计模式
19 数据表示
20 字节
21 Unicode / UTF
22 序列化
23 数据建模
24 关系模型
25 SQL
26 Join
27 Transaction
28 Index
29 Query Execution
30 Process
31 Virtual Memory
32 Thread
33 Race Condition
34 Lock
35 Atomic
36 Deadlock
37 IO
38 Event Loop
39 Coroutine
40 Async/Await
41 Socket
42 TCP
43 TLS
44 HTTP
45 Lexer
46 Parser
47 AST
48 Interpreter
49 Compiler
50 Data Layering
51 Data Warehouse
52 Distributed Failure
53 Replication
54 Partition
55 Consistency
56 Consensus
57 Idempotency
58 Retry
59 Backpressure
60 Architecture Boundary
61 Layered Architecture
62 Hexagonal Architecture
63 DDD
64 Bounded Context
65 Aggregate
66 Domain Event
67 Event-Driven Architecture
68 CQRS
69 Microservices
70 Browser Architecture
71 Rendering Pipeline
72 Reactive System
73 Scheduler
74 SSR
75 Streaming
76 Hydration

这 76 个节点已经足以形成一套相当完整的软件工程基础教材。


22. 每个章节只讨论一个概念

本教材应该刻意避免传统教材常见的问题:

一个章节同时引入五到十个新概念。

对于 AI 辅助学习而言,更合适的粒度是:

One Chapter
=
One Concept

例如:

Chapter 17: Coupling

只解释 coupling。

下一章:

Chapter 18: Cohesion

只解释 cohesion。

之后:

Chapter 19: Dependency

再讨论 dependency。

这样做有几个明显优势。

第一,每个概念都可以单独检索、复习和重新生成。

第二,可以明确记录概念的 prerequisites。

第三,可以让 AI 针对某个知识节点反复采用不同语言、不同案例解释。

第四,可以执行知识图谱意义上的拓扑学习,而不是被一本教材固定的章节顺序绑定。


23. 每章统一使用同一个知识模板

建议所有概念章节采用统一结构。

# Concept Name

## 1. Definition

这个概念是什么?

## 2. Motivation

它解决什么问题?

## 3. Problem Without It

如果没有这个概念,会发生什么?

## 4. Minimal Model

给出该概念最小化的形式模型。

## 5. Mechanism

它内部如何工作?

## 6. Invariants

必须始终保持什么条件?

## 7. Example

给出最小业务例子。

## 8. Cross-Language Comparison

分别使用 TypeScript / Go / Java / Rust 表达。

## 9. Common Misconceptions

最常见的误解是什么?

## 10. Failure Modes

在工程实践中通常如何出错?

## 11. Related Concepts

与哪些概念横向关联?

## 12. Prerequisites

学习这个概念之前应该掌握什么?

## 13. Next Concepts

下一步应该学习什么?

## 14. Exercises

提供推理题,而不只是编码题。

24. AI 学习 Prompt 协议

因此,可以将每一个 DAG 节点直接提交给 AI。

推荐固定 Prompt:

你正在给我编写一本面向资深软件工程师的极简教材。

当前概念:

<CONCEPT>

请严格只围绕这个概念讲解。

要求:

1. 给出严格但工程可理解的定义。
2. 解释它试图解决什么问题。
3. 解释没有这个概念时系统会出现什么问题。
4. 建立最小形式模型。
5. 解释底层机制,而不是只描述 API。
6. 列出关键 invariant。
7. 给一个最小真实业务案例。
8. 如果适用,分别使用 TypeScript、Go、Java、Rust 表达。
9. 比较不同语言为什么采用不同实现。
10. 列出常见误区。
11. 列出真实工程中的 failure modes。
12. 明确列出 prerequisites。
13. 明确列出 next concepts。
14. 给出 3 道概念推理题。
15. 不引入尚未出现在 prerequisites 中的新概念;如果必须使用,先给出最小定义。

写作风格:
学术、技术、紧凑、避免营销语言和无意义类比。

这样整本教材实际上可以被定义成:

DAG
+
Chapter Template
+
AI Generator

25. 教材不应该测试 API 记忆

传统技术面试和技术教育常常过度关注:

某 API 的参数是什么?
某框架生命周期是什么?
某个配置应该写在哪里?

AI 已经显著降低了这类记忆的价值。

本教材更应该测试:

为什么?

什么时候?

有什么不变量?

失败会怎样?

边界在哪里?

数据属于谁?

状态属于谁?

谁依赖谁?

表示为什么这样设计?

另一个方案有什么 trade-off?

例如,不应该主要考:

Promise.all 怎么用?

而应该考:

三个异步任务是否具有依赖关系?
是否允许部分失败?
是否需要限制并发?
是否具有幂等性?
调用方取消以后任务是否应该继续?

这才是可迁移的工程能力。


26. 算法应该占据什么位置

算法仍然重要,但对于大多数业务软件工程师,其学习目的已经发生变化。

不一定要求重复实现所有经典算法。

更重要的是理解:

Time Complexity
Space Complexity
Hashing
Tree
Heap
Graph
Index
Search
Sort
Dynamic Programming

并能够判断:

这个问题属于哪一类计算问题?

数据规模是多少?

复杂度会如何增长?

是否存在更合适的数据结构?

因为具体实现已经越来越容易由标准库或 AI 提供。

但如果连:

O(n)
O(log n)
O(n log n)
O(n²)

都缺乏直觉,则仍然无法判断 AI 生成的程序是否合理。


27. AI 不应该替代系统模型

未来大量代码可能不会由人逐行编写。

但这并不意味着软件工程知识的重要性下降。

更可能发生的是:

Human
↓
Model / Constraint / Architecture
↓
AI
↓
Code

人的角色逐渐从:

statement author

转向:

system modeler
constraint designer
architecture reviewer
failure analyst

因此,高水平工程师的核心问题不再只是:

我能不能实现?

而是:

我能不能验证实现?

而验证需要依赖完整的心智模型。

如果工程师不知道:

  • transaction isolation;
  • event ordering;
  • ownership;
  • lifetime;
  • idempotency;
  • data invariant;
  • module boundary;

那么即使 AI 生成的代码能够编译,也很难判断它是否真正正确。


28. 最终学习目标:读懂陌生系统

本教材的最终目标并不是让学习者记住几十个设计模式或者掌握几十种工具。

理想状态是:

面对一个从未见过的大型系统,仍然能够迅速建立其内部模型。

进入一个陌生代码库时,可以主动寻找:

Data Model
State
Control Flow
Dependency Graph
Module Boundary
Persistence Boundary
Concurrency Model
Failure Model
External Interface
Representation Transformation

例如第一轮阅读可以只回答:

核心数据有哪些?

核心状态在哪里?

入口在哪里?

数据从哪里进来?

经过哪些 transformation?

在哪里持久化?

哪些模块拥有副作用?

异步边界在哪里?

错误在哪里被处理?

哪些组件依赖外部系统?

第二轮再继续识别:

抽象是否合理?

依赖方向是否稳定?

是否存在循环依赖?

是否混合了多个业务边界?

是否存在重复状态?

数据表示是否泄漏?

事务边界是否正确?

并发访问是否安全?

一旦具备这些能力,语言和框架迁移就会变得明显容易。

面对陌生语言,需要寻找的只是:

这里如何表达 interface?

如何表达 generic?

如何表达 closure?

如何处理 error?

如何执行 async?

如何管理 resource?

如何组织 package / module?

面对陌生框架,则进一步寻找:

生命周期在哪里?

状态模型是什么?

依赖注入在哪里?

IO 边界在哪里?

调度器在哪里?

扩展点在哪里?

这与重新学习整个软件工程是完全不同的难度。


29. 一个更抽象的软件工程知识模型

最终,我们可以将本书几乎所有概念进一步压缩成九个基本问题:

Data
State
Representation
Transformation
Boundary
Dependency
Control Flow
Concurrency
Failure

任何复杂系统都可以沿这九个维度进行分析。

Data

系统处理什么事实?

State

哪些事实随时间变化?

Representation

这些事实以什么形式存在?

Transformation

一种表示如何变成另一种表示?

Boundary

哪些东西属于同一个模块或一致性范围?

Dependency

谁知道谁?

谁依赖谁?

Control Flow

谁调用谁?

控制权如何传播?

Concurrency

哪些事情可能同时发生?

Failure

当其中一部分失败时,系统如何表现?


30. 软件工程的统一视角

于是,前端:

State
+
Transformation
+
Rendering

后端:

Boundary
+
Dependency
+
Persistence

数据库:

Data
+
State
+
Consistency

数据工程:

Representation
+
Transformation
+
Aggregation

操作系统:

State
+
Resource
+
Concurrency

编译器:

Representation
+
Transformation
+
Semantics

分布式系统:

State
+
Concurrency
+
Failure

架构:

Boundary
+
Dependency
+
Evolution

看起来不同的领域,实际上反复研究的是同一组基本问题。


31. 本书的基本立场

因此,本教材采用以下原则。

第一:

不以框架作为知识结构的根。

第二:

不以语言作为知识边界。

第三:

不把设计模式当作代码模板。

第四:

不把数据处理理解为简单的数据搬运。

第五:

不把异步理解为 async/await 语法。

第六:

不把数据库理解为 SQL API。

第七:

不把编译原理理解为编译器开发专属知识。

第八:

不把架构理解为架构图。

第九:

不把 AI 生成的代码等同于已经理解的代码。


32. 应该建立什么样的工程直觉

一个成熟的软件工程师应该逐渐形成一些近乎自动化的判断。

看到对象时问:

它代表什么数据?

看到字段时问:

它是事实还是派生状态?

看到类时问:

它真正隐藏了什么?

看到接口时问:

这里为什么需要替换实现?

看到模块时问:

边界为什么画在这里?

看到异步时问:

谁在调度?谁可能同时发生?

看到网络请求时问:

失败以后发生什么?

看到数据库写入时问:

事务边界在哪里?

看到消息队列时问:

是否可能重复?
顺序是否重要?

看到缓存时问:

允许多长时间不一致?

看到数据转换时问:

为什么需要新的 representation?

看到设计模式时问:

它控制的到底是哪一种变化?

看到 AI 代码时问:

它维持了哪些 invariants?

这些问题比记忆任何一个框架的 API 都更具有长期价值。


33. 结语:知识应该形成图,而不是列表

软件工程知识并不是:

Java
React
Redis
Kafka
Docker
Kubernetes

这样的技术名词列表。

它更接近一个概念网络:

             abstraction
            /           \
       interface       module
          |              |
     dependency       boundary
          \              /
            architecture
                 |
               system

另一个方向:

data
↓
representation
↓
transformation
↓
storage
↓
transaction
↓
replication
↓
distributed state

还有:

process
↓
thread
↓
concurrency
↓
IO
↓
network
↓
distributed system

当这些知识逐渐连接起来以后,工程师会发现:

很多看似新的技术,实际上只是旧问题的一种新组合。

一个新的状态管理框架可能重新组合:

State
Dependency
Observer
Scheduler

一个新的后端框架可能重新组合:

Module
Dependency Injection
HTTP
Serialization
Persistence

一个新的分布式数据库可能重新组合:

Storage
Replication
Partition
Consensus
Transaction

一个新的 AI Agent Framework 可能重新组合:

State
Workflow
Event
Tool Interface
Serialization
Retry
Persistence

因此真正具有长期复利的学习方式,不是不断追逐技术名称。

而是不断增加自己能够识别的底层概念。

最终理想状态是:

New Technology
↓
Recognize Existing Concepts
↓
Locate Novel Mechanism
↓
Understand Trade-offs
↓
Use It

而不是:

New Technology
↓
Start From Zero

这就是本教材试图建立的软件工程学习体系。


Appendix A — 推荐核心教材

软件设计

John Ousterhout — A Philosophy of Software Design

重点:

  • Complexity
  • Deep Module
  • Information Hiding
  • Interface
  • General-Purpose Module

Martin Fowler — Refactoring

重点:

  • Code Smell
  • Refactoring
  • Responsibility
  • Change Boundary

Gamma et al. — Design Patterns

不建议机械背诵全部模式。

优先:

Strategy
Adapter
Observer
Decorator
Factory
Command
State
Composite
Proxy
Facade

Appendix B — 系统基础

Computer Systems: A Programmer's Perspective

重点:

Data Representation
Machine Code
Memory
Linking
Process
Virtual Memory
IO
Concurrency

Operating Systems: Three Easy Pieces

重点:

Virtualization
Concurrency
Persistence

Computer Networking: A Top-Down Approach

重点:

Application Layer
Transport Layer
Network Layer

Appendix C — 数据系统

Database System Concepts

重点:

Relational Model
Relational Algebra
Index
Transaction
Query Execution

Designing Data-Intensive Applications

重点:

Data Model
Storage
Replication
Partition
Transaction
Batch
Stream
Distributed Systems

这本书尤其适合已经具有一定工程经验后反复阅读。


Appendix D — 编程语言与编译

Crafting Interpreters

重点:

Lexer
Parser
AST
Interpreter
VM
GC

如果希望进一步理解类型系统,可以继续:

Types and Programming Languages


Appendix E — 领域与架构

DDD 建议首先阅读:

Domain-Driven Design Distilled

然后再阅读:

Domain-Driven Design

重点不是模式名称,而是:

Business Model
Language
Boundary
Invariant
Consistency

Appendix F — 整套教材的最小核心书单

如果只允许选择五本:

A Philosophy of Software Design
↓
Computer Systems: A Programmer's Perspective
↓
Operating Systems: Three Easy Pieces
↓
Designing Data-Intensive Applications
↓
Crafting Interpreters

它们分别回答:

软件应该如何组织?
计算机如何执行软件?
运行中的系统如何工作?
数据系统如何工作?
编程语言如何工作?

这五个问题构成理解现代软件系统的一条极强主干。


Appendix G — 最终原则

可以将整套方法压缩为一句话:

学习软件工程时,应优先掌握跨语言、跨框架、跨业务长期稳定的概念,并将语言与框架理解为这些概念在具体计算环境中的实现。

进一步压缩:

Learn concepts.
Model systems.
Understand boundaries.
Control dependencies.
Understand data.
Reason about state.
Reason about failure.
Use languages as tools.
Use frameworks as tools.
Use AI as an implementation amplifier.

最终目标不是成为某一个框架的熟练使用者。

而是:

面对陌生语言、陌生框架、陌生代码库和陌生业务时,仍然能够快速恢复其数据模型、状态模型、依赖结构、控制流、并发模型与失败模型,并在此基础上安全地修改系统。

这才是一套能够跨越技术周期的软件工程能力。

💯❤️
2