# 架构师是一种思维方式

软件架构速成课。31 个概念——Trade-offs、Technical Debt、Coupling、Single Source of Truth、Idempotency、ADR、C4 等——每个都有规范的英文名、一个生活中的画面、一张图和一张卡片。九章,讲如何用一种今天的决策塑造未来三年的眼光去看问题。结尾有一页纸的速查表,收齐所有概念。

- **Author:** Fedor Chishchin
- **Level:** intro
- **Reading time:** 约 35 分钟阅读
- **Published:** 2026-05-12
- **Source:** https://fedorthinks.com/zh/learn/software-architect-express

---

## 1. 架构师的心态

架构师和工程师的区别在哪。不在于知识——而在于看问题的方式。

### 课程地图:八组共 31 个概念

> 一张地铁线路图:先看到全部线路,再一站一站地坐过去。

架构不是一堆技术——而是一组**动手写代码之前要问的问题**。每个问题在业内都有自己的名字:**Trade-offs**、**Coupling**、**Idempotency**、**ADR**。本课把它们归成八组——第 1–8 章依次讲这八组。

_Diagram: 八组概念——第 1–8 章依次讲这八组。_

### 怎么读这门课

> 护照:每本的栏目都一样,所以容易比较。

每个概念都按同一套方式讲:它的**规范名称**(记住它——这是你要去搜索的词,也是面试里会被问到的词)、一个生活中的画面、一张**图**,以及一张带栏目的**卡片**:_是什么_、_为什么_、_例子_、_陷阱_(人们通常在哪里出错)和*关联*概念。每章以它的关键问题收尾;全课以检查清单和速查表收尾。

### Problem Framing — 质疑任务,而不只是质疑方案

> 病人进门就要一张处方。好医生先问哪里疼——也许会发现,他点名要的那种药本来就开错了。

当有人说"做 X"时,工程师问"我该怎么做 X"。架构师问的是**"我们为什么需要 X,有没有别的东西能解决真正的问题"**。这就是 **problem framing**(界定问题)。任务常常本身就提错了——而某人点单要来的那个方案,一年后变成了问题本身。

_Diagram: 先问“为什么?”,再找真正的问题,然后才是选项——包括“什么都不做”。_

#### Problem Framing (找到真正的问题)

- **是什么:** 在动手做方案之前,先找出请求背后真正的问题。
- **为什么:** 最贵的错误,是把错误的东西做得完美。
- **例子:** 有人让你"加一个 Excel 导出"。你问为什么——原来是大家觉得系统里的报表不好用。那就去改报表。
- **陷阱:** 不问"为什么",直接跳到"怎么做"。
- **关联:** 为 **Trade-offs** 提供选项;"什么都不做"往往就是 **YAGNI** 的答案。

### Long-term Thinking — 以年为单位的视野,为后果负责

> 工程师为周六要搬进来的那家人盖房子。架构师则问:"等他们有了第三个孩子呢?等母亲搬来同住呢?等他们想把一楼出租呢?"

工程师解决**被提出来的那个问题**。架构师解决**三年后它会变成的样子**那个问题。这意味着:为还没用上的能力做设计,同时刻意舍弃那些听起来优雅、却永远用不上的能力。

> 木匠把钉子钉歪了,一天就能修好。架构师把承重墙放错了位置,房子修不了——只能将就着住。

所以架构师要为**后果负责,而不是为代码负责**。工程师的错误,一周就能修好。架构师的错误,公司要为它**付好几年**的账——付在技术债里、付在流失的团队里、付在错过的机会里。架构师必须在*第一行代码写下之前*就想清楚,并且有勇气在架构扛不住真实需求时叫停工程。

_Diagram: 代码错误一周就能修好。架构错误要付好几年的账。_

#### Long-term Thinking (为后果负责)

- **是什么:** 用一个决策在两三年后的样子来评判它。
- **为什么:** 架构错误最贵:要付好几年的账。
- **例子:** 选数据库表结构时,问一句:"客户多了十倍怎么办?再多出一种语言怎么办?"
- **陷阱:** 把"想在前面"和"提前全做好"混为一谈。想——要;多造——不要。
- **关联:** 由 **Simplicity** 和 **YAGNI** 来平衡;帮你判断 **Reversibility**。

> **架构师的核心问题:** "我们到底是不是在做对的事?"

> 工程师把事做对。架构师做对的事。

## 2. Trade-offs 与决策

架构是选择放弃什么的艺术。不存在完美的方案。

### Trade-offs — 每个决策都有得有失

> 自行车比走路快,但下雨会淋湿。汽车淋不着,但会堵在路上。地铁不堵,但不能送到家门口。

工程师选技术时,常常想找"最好的"。这是幻觉。架构师知道:**任何选择都是在好几条轴线上的取舍**——开发速度对可维护性,简单对灵活,现在的成本对三年后的成本。把轴线说出来、有意识地选择,就是架构工作。

_Diagram: 一个决策,是多条轴线上的一个点。往一边走——另一边就吃亏。_

#### Trade-offs (取舍)

- **是什么:** 每个选择都给你一些东西,也拿走一些东西。没有"最好"。
- **为什么:** 大声说出来的取舍,可以讨论、可以回头再看。藏起来的,不行。
- **例子:** 托管的云服务:起步快(得),但量大时更贵、也更难离开(失)。
- **陷阱:** 一味寻找"最好的技术",而不是合适的那个。
- **关联:** 记录在 **ADR** 里;今天有意识的损失,就是 **Technical Debt**。

### Simplicity (KISS) — 简单的方案几乎总是更对

> 瑞士军刀什么都能干,但都干得不好。一把好刀,比军刀里所有的兄弟都切得好。

工程师怕漏掉需求,就把系统塞满将来可能用得上的一切。架构师知道:**每一项没人用的能力,都是开发上的一笔长期税**。复杂度要有意识地引入,有理由才引入;默认选——简单、容易改的方案。这条原则常被称为 **KISS** —— _Keep It Simple, Stupid_。

_Diagram: 左边:你在为没人用的东西付钱。右边:真需要时再加。_

#### Simplicity (KISS, Keep It Simple)

- **是什么:** 默认选能解决问题的最简单方案。
- **为什么:** 简单的容易理解、测试和修改。复杂的,只能忍着。
- **例子:** 一天一百个后台任务?一张数据库表加 cron 就够了——不需要单独的消息队列(message broker)。
- **陷阱:** "以防万一"地建:每多一个功能,就是一笔长期税。
- **关联:** **YAGNI** 的姐妹;让 **Evolvability** 更便宜。

### Reversibility — 难以撤销的决策值得更多关注

> 在餐厅,点错的菜可以退。婚礼上说出的"我愿意",退不了。

架构决策并不都一样——出错的代价不同。选编程语言或数据库,**几乎无法撤销**。选日志库,一天就能换掉。架构师把不成比例的时间花在不可逆的决策上,可逆的则快速推进。业内常把它们叫作**单向门**(one-way door)和**双向门**(two-way door)。

_Diagram: 可逆的事快速定。不可逆的——慢慢来,并把“为什么”写下来。_

#### Reversibility (单向门 / 双向门)

- **是什么:** 一个决策如果错了,撤销它要花多大代价。
- **为什么:** 思考的时间,应该和犯错的代价成正比。
- **例子:** 日志库——一天就能换。主数据库——要迁移好几年。
- **陷阱:** 为可逆的事争论几个星期,却用五分钟定下不可逆的事。
- **关联:** 不可逆的决策值得写一份 **ADR**;**Interface** 和在 **External Dependencies** 之上自建一层,能让事情更可逆。

### Technical Debt — 是有意识的选择,不是偷懒

> 企业贷款没问题,只要你知道什么时候、怎么还。没有还款计划的贷款,是通往破产的路。

有时*此刻*正确的做法,是先交付一个*以后*得重写的东西。这就是 **technical debt**(技术债)。这很正常。**不正常的是不承认它是债。** 架构师会写下:做了哪些取舍、为什么、什么时候必须回头处理。

_Diagram: 记录在案的债,按计划偿还。没记录的债,利滚利——团队每个月都变慢一点。_

#### Technical Debt (技术债)

- **是什么:** 现在走的捷径,以后要用重写来偿还。
- **为什么:** 有时今天的速度比整洁更重要——这是一笔正当的交换。
- **例子:** 上线前,币种被写死成了一种。记录下来:"出现第二个市场时回头处理。"
- **陷阱:** 没有记录的债:没人记得它存在,利息却在涨。
- **关联:** 记录在 **ADR** 里的一次 **Trade-off**;通过 **Migration** 偿还。

> **每个决策都要问的问题:** "我在选什么——同时又因此失去了什么?"

## 3. 系统设计

如何把一个大问题拆成一个个组件,每个都能单独理解、修改和替换。

### Decomposition — 架构师的核心技能

> 一头鲸鱼没法整个吃下去。切成能放进嘴里的小块,就可以了。

大系统是无法理解的。**Decomposition**(分解)就是把它切成几部分的技能。**好的架构,是一组小部件,每个都能单独看懂**。如果解释一个组件还得牵出另外三个——这个分解就是坏的。

_Diagram: 整个系统没法理解。能自己讲明白自己的各个部分,可以。_

#### Decomposition (拆分成部分)

- **是什么:** 把系统切成若干部分,每一部分都能单独理解。
- **为什么:** 人脑一次只能装下这么多。小的部分才能被理解和修改。
- **例子:** 一家网店:订单、支付、商品目录、账户——每一部分都有自己的职责。
- **陷阱:** 按技术来切("所有 controller 放这里"),而不是按含义来切。
- **关联:** 切得好不好,用 **Cohesion & Coupling** 来衡量;各部分通过 **Interface** 对话。

### Cohesion & Coupling — 内部高内聚,外部低耦合

> 一套好公寓:厨房围绕一个目的组织(灶台挨着水槽),和邻居只共用一面墙。

一个模块内部,所有东西都应该**围绕一件事**——这是**高内聚**。模块之间——通过清晰的接口、接触降到最少——这是**低耦合**。如果你改了一个模块,却得去动另外五个——它们之间的耦合就太高了。

_Diagram: 左:每个模块只做一件事,只有一条连线。右:一切混在一起,和所有东西都连着。_

#### Cohesion & Coupling (内部专注,外部少连)

- **是什么:** **Cohesion**(内聚)——模块的各个部分有多专注于一件事。**Coupling**(耦合)——模块之间有多依赖彼此。
- **为什么:** 这样一个改动就留在一个模块里,而不是像波浪一样扩散。
- **例子:** 税的计算放在"账单"里,而不是散落在订单、邮件和报表里。
- **陷阱:** "改一个,动五个":高耦合的明显信号。
- **关联:** **Decomposition** 的衡量尺;靠 **Interface** 维系。

### Layers & the Dependency Rule — 分层讲的是依赖的方向

> 房子的楼层:三楼立在二楼上,二楼立在一楼上,一楼立在地基上。你不能把地基放到三楼上。

在好的架构里,**业务逻辑不知道数据库、网页界面或框架的存在**。内层不依赖外层——这就是**依赖规则**(dependency rule)。需要换数据库或框架时,业务逻辑原封不动。这就是一句话讲完的 **Clean Architecture**。

_Diagram: 细节(界面、数据库、框架)依赖业务逻辑——绝不反过来。_

#### Dependency Rule (分层, Clean Architecture)

- **是什么:** 依赖指向内部:细节认识核心,核心不认识细节。
- **为什么:** 换数据库或框架,不用动业务规则。
- **例子:** "买三件打九折"这条规则既不引入 SQL 也不引入 HTTP——不用数据库就能测试。
- **陷阱:** SQL 查询和 HTTP 响应直接写在业务逻辑里。
- **关联:** 通过 **Interface** 起作用;在架构模式课程(Hexagonal、Clean)里有深入讲解。

### Interface over Implementation — 接口比实现更重要

> 你不在乎大楼里电梯是什么牌子。你在乎的是按钮的用法始终一样、门开得可靠、声音安静。

一个模块和另一个模块对话时,重要的是**它们之间的契约(接口)**,而不是对方内部怎么造的(**实现**)。先设计边界(API、类的接口、消息格式),再做实现。实现可以随便换;契约只有在完全清楚代价时才能改。

_Diagram: 调用方只看到接口。接口背后的实现可以替换。_

#### Interface over Implementation (契约优先)

- **是什么:** 先设计各部分之间的边界,再做内部。
- **为什么:** 只要契约不变,实现就可以自由更换。
- **例子:** 一个接口 `sendEmail(to, text)`:今天背后是一家邮件服务商,明天换另一家——调用它的代码不用变。
- **陷阱:** 调用方绕过接口,直接伸手去碰另一个模块的内部。
- **关联:** 在系统边缘,接口就成了 **API Contract**;是 **Dependency Rule** 的基础。

### Monolith vs Microservices — 是成熟度的问题,不是潮流

> 一个厨师在小厨房里做一道菜,比十个厨师在 Slack 上商量着做还快。每小时一百道菜,十个厨师就做得更好。

**单体**(monolith)是整体部署的一个应用。**微服务**(microservices)是许多小服务,各自独立部署。微服务解决的是**组织规模和负载**的问题,不是代码质量的问题。小团队上微服务,是没人要的复杂度。架构师从单体起步,真的出现痛点时再拆,而不是"因为流行"。

_Diagram: 从单体开始。真的疼了再拆:团队或负载变大的时候。_

#### Monolith vs Microservices (一个应用还是多个服务)

- **是什么:** 一个整体的应用——还是许多独立的服务。
- **为什么:** 微服务给大团队、大负载提供独立部署和独立扩展。
- **例子:** 五个人的团队做产品——单体。二十个团队在同一次部署里互相踩脚——该拆了。
- **陷阱:** 为了赶潮流上微服务:网络、故障和一致性的麻烦全来了,却没有回报。
- **关联:** 沿着 **Decomposition** 来拆;拆完后服务之间需要 **Sync vs Async** 和 **Idempotency**。

> **分解的检验:** "我能不提其他组件,就把一个组件讲清楚吗?"

## 4. 数据与状态

系统里最持久的部分。代码模式会被重写;数据模型能活好多年。

### Data Model — 它比其他一切都活得久

> 房子的地基,比一个世纪里盖在它上面的所有墙和屋顶都活得久。

语言会过时,框架会更替,界面会被重画十遍。**一开始定下的数据模型会留下来**——因为在运行中的系统上改它,代价极高。它是架构师最先花时间的东西,也是最后才急着去改的东西。

_Diagram: 界面被重画很多次,框架和语言不断更换——数据模型留下来。_

#### Data Model (实体、字段、关系)

- **是什么:** 系统里有哪些实体,它们的字段和关系。
- **为什么:** 它是活得最久的部分——事后最贵的改动。
- **例子:** 一开始就要定:一个订单是一个收货地址还是多个?以后再改,就得迁移上百万行数据。
- **陷阱:** 一个晚上画好几张表,然后在上面建好几年。
- **关联:** 每个事实放在哪——**Single Source of Truth**;怎么改它——**Migration**。

### Single Source of Truth — 每个事实只住在一个地方

> 如果家里每个人都有自己的购物清单,会有两个人去买牛奶,没人去买面包。

同一份信息放在两个地方,**必然走向不一致**。**single source of truth(SSOT,单一事实来源)**原则:架构师要确定系统里每个事实住在哪里。如果数据被复制了(比如为了性能),就必须明确:哪个是事实来源,哪个是缓存或投影。

_Diagram: 写入只进事实来源;缓存、副本和投影都是派生的拷贝。_

#### Single Source of Truth (SSOT)

- **是什么:** 每个事实有一个主要的家;其余都是拷贝。
- **为什么:** 拷贝出现分歧时,清楚该信哪一个。
- **例子:** 商品的价格住在商品目录里。搜索索引和网站缓存是拷贝,由商品目录重建。
- **陷阱:** 两个系统都以为自己拥有同一个字段。
- **关联:** 拷贝就是 **Cache** 和投影;拷贝越少——**State** 越少。

### State — 是复杂度;越少越好

> 干净的桌面让工作更轻松。杂乱的桌面逼你记住东西在哪,还不断让你分心。

存在某处的每一份**状态**,都是**可能失去同步、丢失、或与其他状态矛盾**的东西。好的架构追求无记忆的函数(**stateless**,无状态)和生命周期短的变量。状态只留在真正需要它的地方——通常是数据库。

_Diagram: 应用实例什么都不记——所有状态都在一个地方,数据库。_

#### State (stateless vs stateful)

- **是什么:** 系统在两次请求之间记住的一切。
- **为什么:** 状态越少——越少的地方可能出现分歧或丢失。
- **例子:** 购物车放在数据库里,而不是服务器内存里:服务器重启——购物车还在。
- **陷阱:** 状态放在某一台服务器的内存里:服务器挂了——数据就没了。
- **关联:** 无状态的服务很容易复制多份——这就是 **Scalability**。

### Transactions — 意图的原子性

> 转账是两个动作,但只有一个意图。要么两次写入都发生了,要么都没发生。只做一半就是灾难。

当几个改动必须**作为一个整体**发生时,就需要**事务**(transaction)。这种性质叫**原子性**(atomicity)。架构师知道系统里哪些操作是原子的、哪些可能只失败一半,以及如何与之共处。在分布式系统里,这成了核心问题(two-phase commit、saga、idempotency)。

_Diagram: 两次写入都成功,或者都不成功。没有“一半”。_

#### Transaction (原子性)

- **是什么:** 几个改动,要么全部发生,要么都不发生。
- **为什么:** 否则系统会卡在"做了一半"的状态里。
- **例子:** 转账:从 A 扣款、给 B 入账——放在一个事务里。
- **陷阱:** 以为一个事务能跨多个服务。那种情况下需要 saga 和 idempotency。
- **关联:** 跨服务时要靠 **Idempotency**;见架构模式课程里的 Saga 模式。

### Cache — 第二份“真相”,而且它会说谎

> 冰箱上的日历比看手机快。但如果有人在手机上改了日程——冰箱上的那份就已经在说谎了。

**缓存**(cache)带来速度,却拿走了**新鲜度的保证**。架构师始终要清楚:缓存了什么、缓存多久、缓存说谎了会怎样。缓存最难的问题不是怎么加——而是*怎么让它失效*(**cache invalidation**)。

_Diagram: 缓存回答得快,但数据库一有改动就必须把它清掉——否则它就在说谎。_

#### Cache (缓存失效)

- **是什么:** 数据的一份快速拷贝,放在读取者近旁。
- **为什么:** 速度,以及减轻数据源的压力。
- **例子:** 数据库里价格变了——缓存里还是旧的。顾客看到一个价格,付的却是另一个。
- **陷阱:** 加了缓存,却没想好什么时候、怎么清掉它。
- **关联:** 是 **Single Source of Truth** 之下的一份拷贝,不是第二份真相。

> **面对任何数据都要问的问题:** "事实来源在哪里?拷贝出现分歧会怎样?"

## 5. 连接与边界

最大的问题不出在组件内部,而出在组件之间。

### API Contract — 系统的边界是契约,不是代码

> 国与国的边界不是一道栅栏——而是一套规则:什么可以带过去、谁可以入境、需要什么护照。

API、消息格式、文件格式——这些是**对所有使用者的承诺**。改契约,就是改承诺,代价很高。好的架构师设计契约时,会让它能扩展而不破坏现有的客户端(加字段,绝不删字段)。这种性质叫**向后兼容**(backward compatibility)。

_Diagram: 加一个字段——老客户端照常运行。删一个字段——老客户端就坏了。_

#### API Contract (向后兼容)

- **是什么:** 一份公开的承诺:接受哪些请求,返回什么。
- **为什么:** 有你控制不了的客户端依赖它。
- **例子:** 响应里加了个 `currency` 字段——老应用直接忽略。把 `price` 改了名——老应用就崩了。
- **陷阱:** 像改内部代码一样随便地改契约。
- **关联:** **Interface over Implementation** 的对外面孔;通过 **Migration** 来改。

### Sync vs Async — 在时间上选择怎样耦合

> 打电话:双方必须同时在线。写信:收信人方便的时候再读。

**同步**(synchronous)调用把**两个服务在时间上绑在一起**:接收方挂了,发送方也卡住。通过队列的**异步**(asynchronous)消息打破了这种绑定:发送方把信放下就继续往前走。这带来韧性,但也增加复杂度——延迟、重复、顺序问题。

_Diagram: 上:A 等 B,跟着一起挂。下:A 把消息放进队列,继续往前走。_

#### Sync vs Async (打电话还是写信)

- **是什么:** 马上等答复(打电话)——或者发出去就不等了(写信)。
- **为什么:** 异步让服务解耦:一个出故障,不会拖停另一个。
- **例子:** 下订单——同步(顾客在等)。收据邮件——异步,通过队列。
- **陷阱:** 忘了异步的代价:延迟、重复、消息乱序。
- **关联:** 重复要求 **Idempotency**;在微服务之间很常见。

### Idempotency — 一个操作可以放心地重复执行

> 有人叫电梯去三楼。就算十个人再按一遍同一个按钮,电梯还是去三楼,什么也不会坏。

在真实的系统里,调用会被重试——因为超时、网络故障、重启。**幂等**(idempotent)的操作,重复执行结果不变。**一个重试起来很危险的操作,是一颗定时炸弹。** 架构师默认把关键操作(支付、消息处理)做成幂等的。

_Diagram: 同一个请求来了三次——钱只扣了一次。_

#### Idempotency (可安全重复)

- **是什么:** 重复执行一个操作,结果和只执行一次一样。
- **为什么:** 重试不可避免:超时、网络故障、重启。
- **例子:** 客户端带着唯一的键发起支付。同一个键又来了一次——服务器返回之前的结果,而不是再扣一次款。
- **陷阱:** "我们从来不会遇到重试"——会的,只是你还没看到。
- **关联:** **Async** 和重试时必不可少;是 **Reliability** 的基础之一。

### External Dependencies — 永远是风险

> 如果晚饭靠街对面的披萨店——没什么大不了,还有备选。如果靠全城唯一的肉类供应商——就是要命的事。

每一个**外部依赖**——API、服务、库——**都可能宕机、变慢,或者改了它的契约**。架构师要决定出现这些情况时会怎样:整个系统倒下,还是降级继续运行?通常值得把外部依赖隔离在你自己的一层(**adapter**,适配器)后面——这样以后才能替换。

_Diagram: 核心只和自己的 adapter 对话。它后面是:供应商、备用供应商,或降级模式。_

#### External Dependencies (自己的 adapter)

- **是什么:** 一切你控制不了的东西:别人的 API、服务、库。
- **为什么:** 它们出故障或变更时,你的系统不该跟着倒下。
- **例子:** 支付服务商藏在你自己的 `PaymentGateway` 接口后面。换服务商,意味着写一个新 adapter,而不是重写订单。
- **陷阱:** 对服务商 SDK 的调用散落在整个代码里。
- **关联:** **Interface over Implementation** 的一种应用;**Reliability**(fallback,备用方案)的一部分。

> **边界的检验:** "如果这条连接断了,会怎样?"

## 6. 非功能性需求

不是"系统做什么",而是"做得有多好"。它们叫非功能性需求(NFR)或质量属性——客户永远不会明说,却一定会要求的东西。

### Security — 不是一个功能,而是系统的生活方式

> 在博物馆里,安全不是门口的一个摄像头——而是层层设防:闸机、保安、每个展厅的摄像头、展柜上的锁。

**安全**(security)没法在最后才补上。它要建在每一层:**认证、授权、加密、隔离、审计、最小权限**。这种分层的做法叫 **defense in depth**(纵深防御)。架构师从第一天就考虑它,在每个决策上都问:"如果攻击者从这里进来了呢?"

_Diagram: 分层防御:突破了一层,攻击者还会撞上下一层。_

#### Security (defense in depth, least privilege)

- **是什么:** 系统每一层都有防护,而不是门口只有一把锁。
- **为什么:** 迟早有一层会被攻破——其余的必须顶住。
- **例子:** 报表服务对数据库只有只读权限——就算它被黑了,数据也删不掉。
- **陷阱:** "发布之前再加安全。"
- **关联:** 审计依赖 **Observability**;隔离依赖清晰的边界。

### Performance — 不是“快”,而是“对所需负载来说足够快”

> 载三个人的出租车,不该像高铁那样跑。高铁也开不进小巷。

"让它快一点"是毫无意义的需求。架构师用数字来表述**性能**(performance):**每秒多少请求(throughput,吞吐量),能容忍多大延迟(latency),高峰时系统表现如何,超过上限会发生什么**。过早优化是浪费;完全不考虑则是灾难。

_Diagram: 需求就是数字:能容忍多大的延迟,在多大的负载下。以及超过上限会怎样。_

#### Performance (latency, throughput)

- **是什么:** 系统能处理多少、响应多快——用数字表示。
- **为什么:** 没有数字,就没法判断够不够,也不知道在哪里停手。
- **例子:** "每秒 200 个请求时,搜索在 300 毫秒内响应;高峰时——排队,而不是崩溃。"
- **陷阱:** 只说"让它快一点"却没有数字——还去优化并不疼的地方。
- **关联:** 负载增长——**Scalability**;通过 **Observability** 来度量。

### Scalability — 不重写也能增长的能力

> 设计时就留了加盖二楼余地的房子,可以不拆就扩建。没留这个余地的房子——只能推倒重建。

**Scalability**(可扩展性):系统必须**朝两个方向**增长——更多的用户(**横向**——加服务器)和更多的功能(**模块化**——加模块而不破坏旧的)。这要在设计时就埋进去,而不是事后补。

_Diagram: 两条增长轴:向右——更多服务器,向下——更多模块。_

#### Scalability (横向与模块化)

- **是什么:** 负载和功能都能增长,而不用重写的能力。
- **为什么:** 成功的系统会增长——边跑边重写,代价很高。
- **例子:** 负载均衡器后面的无状态服务:负载涨了——再加三个副本。
- **陷阱:** 只有一百个用户,却按一百万用户来建。
- **关联:** 建立在 **State**(无状态)和 **Cohesion & Coupling**(模块)之上。

### Reliability — 系统出故障时的表现

> 好船不是不会沉的船(那样的船不存在)——而是有十个隔舱的船,一个被撞破了,其余的仍然浮着。

系统的每个部分终究都会坏。**Reliability**(可靠性)不是"永远不坏"——而是"坏了也能扛住"。架构师不试图阻止故障——而是**设计系统怎样扛过故障**:**故障隔离、重试、优雅降级、fallback**。首要规则:_一个组件的故障,不能搞垮整个系统_。

_Diagram: 船的隔舱:一处破口不会沉掉其他部分。下方——四种可靠性策略。_

#### Reliability (容错)

- **是什么:** 系统某一部分坏了之后,整体怎么表现。
- **为什么:** 一切都会坏——唯一的问题是一处故障会不会拖垮其他部分。
- **例子:** 推荐服务挂了——商店显示"热门商品",而不是个性化推荐,但照常卖货。
- **陷阱:** 仿佛故障不会发生一样地去建系统。
- **关联:** 重试需要 **Idempotency**;**Observability** 让你看见故障。

### Observability — 看不见的东西,你修不了

> 一辆车,没有油量表、没有温度计、没有机油压力指示灯。一路开到彻底趴窝。

**Observability**(可观测性):系统必须**讲出自己的状态**——**日志(logs)、指标(metrics)、链路追踪(traces)**。凌晨三点出了问题,你只有几分钟去找原因。如果一开始没有设计可观测性,事后再加就晚了——你连该看哪里都不知道。

_Diagram: 日志、指标、追踪三种信号,把一次深夜故障变成几分钟的排查。_

#### Observability (日志、指标、追踪)

- **是什么:** 根据系统发出的信号,弄清系统内部正在发生什么的能力。
- **为什么:** 没有信号,查故障只能盲目地找上好几个小时。
- **例子:** 指标显示凌晨 03:00 错误激增,追踪显示某个外部 API 很慢,日志显示具体是什么错误。
- **陷阱:** 等第一次大故障之后才去加日志。
- **关联:** **Reliability** 和 **Performance** 的眼睛;**Security** 里审计的基础。

> **非功能性的核心问题:** "在什么负载下,什么条件下,要多可靠?"

## 7. 系统演进

系统会活很久。它一定会被改。好的架构,就是容易改的架构。

### Evolvability — 为变化做好准备,比今天正确更重要

> 在一座新城里,马路修得比今天需要的更宽。十年后车多了,窄路就得拆掉重修。

架构师不试图预测未来的每一个功能(做不到)。他们**留出余地,让新功能能加进来而不破坏现有的东西**。这种性质叫 **evolvability**(可演进性)。实践中意味着:边界清晰、契约可扩展、把"做什么"和"怎么做"分开。

#### Evolvability (为变化做好准备)

- **是什么:** 在系统里增加或修改东西有多便宜。
- **为什么:** 需求一定会变——只是不知道会怎么变。
- **例子:** 新增一种支付方式,是加一个新的 adapter,而不是在二十个地方改代码。
- **陷阱:** 试图猜出未来的每个功能,并提前建好。
- **关联:** 来自 **Interface**、**Cohesion & Coupling**,以及可扩展的 **API Contract**。

### Migration — 头等问题,而不是“以后再说”

> 从旧房子搬到新房子,是个持续几个月的过程。你不能把东西全扔了再全买一遍。

当系统需要改变时(新数据库、新协议、新数据模型)——这**不是"从头重写",而是"带着系统走完一段过渡"**。这就是 **migration**(迁移)。真实的系统在变更期间一直在运行。架构师从一开始就考虑迁移的路径,否则过渡就成了不可能的事。

_Diagram: 新旧并存,流量逐步迁移,系统从不停机。_

#### Migration (过渡期)

- **是什么:** 把一个正在运行的系统,一步一步从旧搬到新。
- **为什么:** 真实的系统不能为了翻修而关掉。
- **例子:** 换新数据库:先双写,再改成从新库读,最后关掉旧库。
- **陷阱:** "一个季度就能从头重写"——结果同时养着两套系统两年。
- **关联:** **Data Model** 和 **API Contract** 怎么改;**Technical Debt** 怎么还。

### YAGNI & Deletion — 删除比添加更重要

> 衣柜不能无限扩大。有时必须扔掉旧的,才能给新的腾地方。

任何成功的系统都会积累死代码、没人用的功能、过时的集成。**这是每个开发者每天都在交的税。** 架构师会定期问:"我们能删掉什么?"——有时这就是一年里最有用的架构决策。

> 最结实的墙,是从来不用拆的墙,因为后面没有被塞进任何多余的东西。

有时正确的架构决策,是**不做架构决策**。不要"为了将来"引入一层抽象。不要"以后某天会用到"就拆分单体。不要"以防万一"就建一套插件系统。这条原则叫 **YAGNI** —— _You Aren't Gonna Need It_(你不会需要它)。好的架构师知道什么时候该说"先不做"或"我们不需要这个"。

#### YAGNI & Deletion (You Aren't Gonna Need It)

- **是什么:** 现在不需要的就不要建,不再需要的就删掉。
- **为什么:** 最好的架构是不需要的那一个:成本为零。
- **例子:** 删掉了一个只有两个客户在用的集成:少了一千行代码,少了一个依赖,少了深夜的告警。
- **陷阱:** "以防万一"的抽象和插件。
- **关联:** **Simplicity** 的延伸;为 **Evolvability** 腾出空间。

> **演进的检验:** "如果需求变了,我得重写多少代码?"

## 8. 沟通与文档

没人理解的架构等于不存在。一半的工作,是把它传达出去。

### ADR — 架构决策要写下来,否则就丢了

> 口头的遗嘱,随立遗嘱的人一起消失。写下来的,能传好几代人。

每一个重要的架构决策都需要记录:**选了什么、有哪些备选、为什么否掉、接受了哪些妥协**。这份文档现在用不上——它是一年后用的,那时来了新人,问"为什么这里做得这么怪?"标准格式是 **ADR(Architecture Decision Record,架构决策记录)**。

_Diagram: 每个决策一页短文:背景、选择、备选、取舍、何时重新审视。_

#### ADR (Architecture Decision Record)

- **是什么:** 对一个架构决策及其理由的简短记录。
- **为什么:** 一年后没人记得为什么。记录记得。
- **例子:** "ADR-007:用 Postgres,不用 MongoDB。我们需要订单与支付之间的事务。代价——更严格的表结构。如果出现非结构化数据,再重新审视。"
- **陷阱:** 只写"是什么",不写"为什么",也不写被否掉的选项。
- **关联:** 里面装着 **Trade-offs**、**Technical Debt** 和不可逆的决策。

### C4 Model — 一张图胜过千言万语

> 用文字描述怎么走到一栋房子,又长又常常没用。给人看一张地图,一秒钟就够。

架构师**手边总有一张系统的图**:有哪些组件、怎么连接、边界在哪里。不求完美——能用就行。它用来向新人快速解释,也让你能看清系统的全貌。**C4 模型(Context、Container、Component、Code)**简单,对大多数情况已经够用。

_Diagram: 四个缩放层级——像地图一样:国家、城市、街道、房子。_

#### C4 Model (Context · Container · Component · Code)

- **是什么:** 四个层级的图——从整个系统一直到代码。
- **为什么:** 每类读者各看各的缩放层级,而不是一张图包打天下。
- **例子:** Context:商店、顾客、支付服务商。Container:网页应用、API、数据库、队列。
- **陷阱:** 一张巨大的图,所有东西都画在同一层,什么也看不清。
- **关联:** 把 **Decomposition** 及其边界画出来;与 **ADR** 互补。

### Stakeholder Communication — 向非工程师讲清楚,并且敢说“不”

> 好医生用病人听得懂的话解释诊断。坏医生满口医学术语,病人点点头,什么也没听懂。

如果架构师只会向其他工程师解释决策,他们就**丢掉了一半的工作**。业务、产品、法务、安全——所有的**干系人**(stakeholders)都需要理解后果。把技术决策翻译成业务语言(**成本、风险、时间**),是架构师的核心技能之一。

> 对什么请求都答应的外科医生,会失去病人。好的外科医生会在手术会致命时拒绝。

团队常常提出听起来合理、却会造成长期问题的要求。**架构师必须敢于反驳、解释风险,有时还要守住底线**——即使不受欢迎。同时也要——敢于承认自己错了,当反方的论据更有力时,改变决定。

#### Stakeholder Communication (解释,并敢说“不”)

- **是什么:** 用听者的语言解释决策:成本、风险、时间。
- **为什么:** 没人理解的决策,不会得到支持——或者会被推翻。
- **例子:** 不说"我们需要消息队列",而说"没有它,邮件服务一出故障,我们就会丢订单——每月大约这么多。"
- **陷阱:** 满嘴术语——或者为了避免争吵什么都答应。
- **关联:** 建立在 **Trade-offs**、**ADR** 和 **C4 Model** 之上。

> **沟通的检验:** "六个月后的新人,能看懂当初为什么这么做吗?"

## 9. 自检与速查表

架构师在任何决策之前、以及一天结束时都要过一遍的检查清单。还有本课所有概念,汇在一页纸上。

#### 任何架构决策之前

- **究竟在解决什么问题?** 不是被提出来的任务,而是它背后的问题。
- **有哪些备选?** 至少三个,包括"什么都不做"。
- **这个选择让我失去了什么?** 至少说出两个缺点。
- **这个决策有多可逆?** 可逆的——快速定。不可逆的——多想想。
- **两三年后这会是什么样子?** 不是理想的样子——是现实的样子。
- **除了我,还有谁需要理解这个决策?** 以及我该怎么向他们解释。

#### 看任何一个系统时

- **这个系统里最脆弱的是什么?** 负载一上来,什么最先坏?
- **哪些改动容易,哪些痛苦?** 这就是衡量架构质量的尺子。
- **每个重要事实的真相放在哪里?** 它的拷贝又在哪里——缓存、副本、投影。
- **组件之间哪些边界是明确的,哪些是偶然形成的?** 偶然形成的就是技术债。
- **每个外部依赖坏了会怎样?** 一个一个地过。
- **如果今天从头开始,我会做哪些不同的事?** 那就是演进的方向。

#### 你在像架构师一样思考的迹象

- 你花在**讨论和设计**上的时间,比写代码更多。
- 你常在问"怎么做"之前先问**"为什么"**。
- 你把取舍**大声说出来**,而不是试图藏起来。
- 你写**文档和图**比大多数人都勤。
- 你看到**整个系统**,并能把任何一部分放在整体里讲清楚。
- 你会想到**一年后要在这上面工作的人**——并把东西写得让他们用着舒服。
- 你偶尔会因为用不上,而**拒绝一项漂亮的技术**。

### 这些概念如何相连

> 一个工具箱:没有钉子,锤子没用;没有锤子,钉子也没用。

本课的概念不是孤立存在的。一个决策从 **Problem Framing** 开始,当作 **Trade-off** 来权衡,检查它的 **Reversibility**,再写进 **ADR**。系统用 **Decomposition** 来切,切得好不好用 **Cohesion & Coupling** 来评判,各部分在 **Interface** 处相遇。数据的拷贝住在 **Single Source of Truth** 之下,异步世界里的重试则要求 **Idempotency**。

_Diagram: 三条链:如何做决策,如何切分系统,如何与数据和故障共处。_

## 速查表:全部 31 个概念

### 心态

- **Problem Framing** (找到真正的问题): 先问"为什么",再问"怎么做"。找到请求背后的问题。
- **Long-term Thinking** (为后果负责): 从三年后看一个决策;为后果负责,而不是为代码负责。

### Trade-offs 与决策

- **Trade-offs** (取舍): 每个选择都给你一些东西,也拿走一些东西。把轴线大声说出来。
- **Simplicity** (KISS): 默认简单。没人用的功能是一笔长期税。
- **Reversibility** (单向门 / 双向门): 可逆的快速定,不可逆的慢慢来。
- **Technical Debt** (技术债): 现在走捷径,以后还账。可以——只要写下来。

### 系统设计

- **Decomposition** (拆分成部分): 每个部分都能单独理解。
- **Cohesion & Coupling** (内部专注,外部少连): 模块内部——只做一件事;模块之间——连接最少。
- **Dependency Rule** (分层, Clean Architecture): 依赖指向内部:业务逻辑对数据库和框架一无所知。
- **Interface over Implementation** (契约优先): 先定边界,再做实现。实现可以随便换。
- **Monolith vs Microservices** (一个应用还是多个服务): 从单体开始,真的疼了再拆。

### 数据与状态

- **Data Model** (实体、字段、关系): 活得比代码久。先想它。
- **Single Source of Truth** (SSOT): 每个事实有一个主要的家;其余都是拷贝。
- **State** (stateless vs stateful): 状态就是复杂度。保持少量,并放在一个地方。
- **Transaction** (原子性): 几个改动——要么全做,要么全不做。
- **Cache** (缓存失效): 一份会说谎的快速拷贝。难的是什么时候清掉它。

### 连接与边界

- **API Contract** (向后兼容): 对客户端的承诺。只扩展,不收回。
- **Sync vs Async** (打电话还是写信): 同步在时间上耦合;异步更有韧性,但带来延迟和重复。
- **Idempotency** (可安全重复): 重复一次,结果相同。支付和消息必备。
- **External Dependencies** (自己的 adapter): 别人的系统会出故障、会变更。把它们藏在你自己的一层后面,规划好降级。

### 非功能性需求

- **Security** (defense in depth): 从第一天起就分层防护,最小权限。
- **Performance** (latency, throughput): 不是"快",而是数字:负载、延迟、高峰时的表现。
- **Scalability** (横向与模块化): 靠服务器和模块来增长,不用重写。
- **Reliability** (容错): 一个部分出故障,不能搞垮整个系统。
- **Observability** (日志、指标、追踪): 系统会讲出自己的情况。从一开始就设计进去。

### 演进

- **Evolvability** (为变化做好准备): 给新东西留出余地,而不是提前去猜它。
- **Migration** (过渡期): 不是"从头重写",而是带着系统走完过渡。
- **YAGNI & Deletion** (You Aren't Gonna Need It): 不要多造;不再需要的就删掉。

### 沟通

- **ADR** (Architecture Decision Record): 选了什么、有哪些备选、为什么、代价是什么。
- **C4 Model** (Context · Container · Component · Code): 四个缩放层级的系统图。
- **Stakeholder Communication** (解释,并敢说“不”): 用成本、风险和时间来说话;该反驳时反驳,错了就承认。

> 架构师不是知道得更多的人。而是在开始之前想得更久的人。

速成课结束 · 9 章 · 31 个概念

实践在后面。每一章都能展开成书:_Designing Data-Intensive Applications_、_Clean Architecture_、_Software Architecture in Practice_、_System Design Interview_。但在读它们之前——先问自己这份文档里的问题。它们比答案更重要。
