这是一本判据书,不是教程
读者拿起架构书,通常期待两种东西:一套可以照抄的目录结构,或者一组可以套用的设计模式。卷四两种都不给。
它给的是判据:在什么条件下应该共享,在什么条件下必须分开;什么差异可以保留,什么差异必须消除;什么时候该迁移,什么时候该停下。判据的特点是它不替你决定,但它让决定可被讨论、可被复核。
这也决定了阅读方式。教程可以跳读代码,判据书必须读反例。每一章末尾的「常见错误与边界」比正文更重要,因为判据的价值往往体现在它排除的那些选项上。
四卷的位置
《互联网产品的客户端架构》四卷,各 45 篇,按承诺强度由内向外展开:
| 卷 | 主题 | 核心承诺 | 失败代价 |
|---|---|---|---|
| 卷一 | 端形态与体验基础 | 结构稳定 | 一个版本可修 |
| 卷二 | 内容、社交与实时互动 | 消息可达 | 用户可感知,大多可恢复 |
| 卷三 | 交易、增长与平台化 | 事实唯一 | 资金损失,需人工介入 |
| 卷四 | 规模化与架构演进 | 长期一致 | 慢性成本,逐月累积 |
前三卷回答「怎么把一件事做对」,卷四回答「怎么让五件事长期都做对」。它不重复前三卷的基础,而是把前三卷的方法放进多端、多团队、多版本的约束里重新检验。
如果前三卷是造一辆车,卷四是如何让五个工厂造出同一款车,并且随时能换发动机。
每章的四段写法
十三章结构一致,每章三节,每节按四段展开:
第一段:现场。 一个具体的多端问题,通常带数字与症状。例如「五个端的提交订单按钮有三种防抖策略」,或者「同一笔退款在 Web 显示已到账、在 iOS 显示处理中」。现场的作用是让抽象判断有落点。
第二段:模型。 把现场拆成可计算的结构:矩阵、层级、状态机、预算、清单。模型要能回答「这一格为什么是这个值」,而不只是给出一张好看的图。
第三段:代码。 用 evolve-lab 里可运行的 TypeScript 模块把模型固化。代码不大,通常几十到一百多行,但覆盖边界条件:未知输入、缺失能力、循环依赖、越界预算。
第四段:判据。 给出采用与放弃的条件、验收方式与反例。判据部分不写「最佳实践」,因为最佳实践依赖上下文;它写「如果……那么……否则……」。
三节之间也有节奏:第一节定义问题与模型,第二节落到工程实现,第三节讨论治理与退出条件。时间紧的读者可以只读第一、三节,但会错过实现中的坑。
evolve-lab 的模块地图
实验工程与前三卷同构,按章分目录,全部使用 Node 26 原生类型剥离,零第三方依赖:
evolve-lab/
src/platform/ # 端矩阵、共享内核、能力探测(第 1 章)
src/design/ # token、组件契约、主题(第 2 章)
src/plugin/ # 插件协议、沙箱、热更新(第 3 章)
src/perf/ # 预算、归因、回归(第 4 章)
src/stability/ # 故障域、降级、演练(第 5 章)
src/build/ # 依赖图、产物、流水线(第 6 章)
src/observability/ # 日志指标追踪、采样、告警(第 7 章)
src/security/ # 数据分级、SDK、隐私(第 8 章)
src/i18n/ # 格式化、RTL、区域能力(第 9 章)
src/a11y/ # 语义、偏好、审计(第 10 章)
src/governance/ # ADR、所有权、技术债(第 11 章)
src/migration/ # 绞杀者、双写、退出(第 12 章)
src/summary/ # 多端端到端、平台矩阵、收官(第 13 章)
test/ # 与 src 同构的测试目录
模块之间刻意保持低耦合:每一章只依赖 node: 内置模块与同章文件,不跨章引用。这样读者可以单独抽出某一章运行,而不需要先跑通整本书。
唯一的例外是第 13 章,它会引用前十二章的数据结构,作为收口验收。
三级可运行性怎么自测
每节代码块的第一行标注级别:
L1:纯函数与纯判断,无 I/O。直接node --test。L2:进程内模拟,使用注入的时钟与内存实现。同样node --test,但通常需要固定并发。L3:本地真实依赖,如真实文件系统、真实 HTTP 服务。需要先启动依赖。
自测命令如下(在 evolve-lab 目录内):
node --test test/platform/*.test.ts
node --test test/design/*.test.ts
node --test test/plugin/*.test.ts
注意 Node 26 的测试运行器要求传入文件 glob,直接传目录会报 MODULE_NOT_FOUND。涉及共享状态的测试使用 --test-concurrency=1,避免假时钟互相干扰。
书里的代码块与工程文件逐字节一致,校验方式是用 // file: 头找到目标路径再比对。这意味着如果你发现书里与工程不一致,那不是排版问题,而是真错误,值得提一个 issue。
代码与命名约定
- 文件与目录用英文 kebab-case,如
endpoint-matrix.ts、hot-update.ts。 - 类型与接口用大驼峰,函数与变量用小驼峰,常量用大写蛇形。
- 不使用
enum与namespace:Node 的类型剥离不支持它们,运行时会直接报错。 - 本地导入带
.ts后缀,这是 Node 原生类型剥离的要求,不是风格选择。 - 金额一律用整数分,时间一律注入,不调用
Date.now()。 - 所有「未知输入」返回
undefined或显式错误,不返回默认值。
这些约定不是为了统一风格,而是为了让代码在五个端上都能被同一套判断复用。风格分歧可以容忍,语义分歧不行。
错误处理的口径
卷四讨论的多端问题,大多不是「代码抛异常」,而是「某个端静默地做了不同的事」。因此错误处理的重点在暴露,不在捕获:
- 能力缺失默认按
absent处理,不假设支持。 - 承诺未声明默认按最低等级处理,不假设一致。
- 依赖越界直接报错,不自动降级为警告。
- 预算超限返回超限明细,不只返回一个布尔值。
这套口径会让开发阶段更吵,但能让发布阶段更安静。反过来做,问题会推迟到线上,由用户替你发现。
为什么用 TypeScript 与 Node 26
四个理由。
第一,可验证。 卷四的很多判断是「集合运算」:承诺矩阵取最强等级、依赖图找环、能力表算得分、预算表找超限项。这些判断用任何语言都能写,但 TypeScript 的类型系统能让「允不允许」在编译期就暴露,读者不必运行也能看懂约束。
第二,跨端中性。 书里讨论 Web、iOS、Android、小程序与桌面,但实验代码不绑定任何一个端的运行时。同一段承诺计算逻辑,可以直接翻译成 Swift、Kotlin 或 WXML 逻辑层的等价实现,判断不变。
第三,零依赖。 没有 package.json、没有锁文件、没有构建步骤。Node 26 原生剥离类型,node file.ts 直接运行。这让代码块可以逐字节复制到工程里,不产生版本漂移。
第四,测试即文档。 node --test 的断言同时是规格说明。读测试比读实现更快理解一个模块承诺了什么。
不选框架、不选平台 SDK,是刻意的边界。框架会变,判断不会。
章与章之间的依赖
十三章不是十三条平行线,存在三条主依赖:
第 1 章(承诺与边界)
├─→ 第 2 章(设计系统)─→ 第 9、10 章(国际化、无障碍)
├─→ 第 3 章(插件化)
├─→ 第 4、5 章(性能、稳定性)─→ 第 7 章(可观测性)
├─→ 第 6 章(构建交付)
└─→ 第 8 章(安全隐私)
第 11 章(治理)依赖 1、2、6
第 12 章(迁移)依赖 1、11
第 13 章(收口)依赖全部
依赖的含义是「概念前置」,不是「必须按序读」。例如第 9 章的时区与货币判断,需要第 1 章的承诺等级才能说清「哪个端可以不同」;但没有读第 2 章,也能理解格式化的正确性。
如果只想读一章,选第 1 章或第 11 章。前者定义问题,后者定义谁负责。
关于数字与实测
书里出现两类数字,需要区分:
- 构造数据:案例中的订单量、团队人数、版本号、耗时。它们用于让场景自洽,不代表任何真实系统。
- 实跑结果:实验工程的测试数量、构建耗时、依赖扫描结果。这类数字附有命令与版本,可以复现。
写作时遵守一条规则:没有真跑过的,不写「实测」;只跑过一次的,不写「稳定在」。如果读者发现某个数字与自己的环境差异很大,那通常是正常的——架构判断不该依赖精确数字,而该依赖数量级与趋势。
在团队里怎么用这本书
书里的方法只有在团队里跑起来才有价值。三种常见用法:
读书会。 每周一章,先读现场与判据,再让负责该领域的同学补充本团队的真实数据。不要逐行读代码,代码是课后材料。
架构评审。 直接使用附录 C 的清单。每个问题要求回答「现状、证据、缺口、owner」,答不上来的项就是风险项。
落地顺序。 先做第 1 章的承诺矩阵,再做第 11 章的 ADR 与所有权,然后才是第 2、6 章的平台建设。顺序反了会变成「先建平台,再找用途」。
三种用法可以叠加,但不要同时启动。治理动作太多,团队会把它们当成流程负担而不是工具。
三个动手练习
练习一:给你的产品填一张承诺矩阵。 选五个核心功能、三个端,填 15 个格子。填不出来的格子标「未定义」,统计数量。多数团队第一次填会有三到五格填不出来,这些格子就是下一轮联调吵架的位置。
练习二:审计一次依赖方向。 把客户端仓库的模块画成图,标出层级(core / protocol / adapter / ui),找出所有反向依赖。不用修,先数清楚有多少条。
练习三:写一条可执行的治理规则。 从「core 不得依赖 ui」开始,用脚本或 lint 规则实现,接入 CI。规则要能在一次提交内给出明确错误,而不是一份需要人读的文档。
三个练习的产出可以直接作为第 11 章治理清单的输入。
自检清单
读完一卷,可以用下面六个问题自检:
- 能否说出五个端上哪三个行为必须逐字一致?
- 能否指出共享代码里最容易被误用的一个模块,以及它的边界?
- 能否在十分钟内定位「某功能在某端不可用」的原因?
- 能否说出当前架构下一次迁移的退出条件?
- 能否列出三条已经自动化检查的架构规则?
- 能否说出一次故障演练的最近日期与结论?
答不上来的问题,对应章节值得重读。
勘误与反馈
书稿与实验工程同步维护。发现代码不一致、结论有反例、或者某个判据在你的场景里失效,欢迎带着复现步骤反馈。最有价值的反馈不是「这里不对」,而是「在我们的条件下,这个判据给出了错误结论,原因是……」。
版本信息:实验使用 Node v26.5.0,站点构建使用 Hugo v0.163.3+extended。正文中的日期、订单号、版本号均为构造数据。
每章的产出物
读完一章,不应该只留下印象,而应该留下可交接的东西。卷四每章对应一类产出:
| 章 | 产出物 | 形态 |
|---|---|---|
| 1 | 承诺矩阵、依赖图、能力表 | 表格 + 代码 |
| 2 | token 表、组件契约、主题配置 | JSON + 类型定义 |
| 3 | 插件协议、权限清单、热更新开关 | 接口 + 策略 |
| 4 | 性能预算、归因表、回归基线 | 指标 + 阈值 |
| 5 | 故障域图、降级清单、演练记录 | 图 + 清单 |
| 6 | 依赖图、产物清单、发布流水线 | 配置 + 脚本 |
| 7 | 观测契约、采样策略、告警路由 | 字段 + 规则 |
| 8 | 数据分级、SDK 清单、同意记录 | 表 + 流程 |
| 9 | 格式化规则、RTL 清单、区域能力表 | 函数 + 表 |
| 10 | 语义结构、偏好映射、验收用例 | 代码 + 用例 |
| 11 | ADR、所有权表、技术债台账 | 文档 + 表 |
| 12 | 迁移计划、双写窗口、退出条件 | 计划 + 判据 |
| 13 | 端到端报告、平台矩阵、治理清单 | 报告 |
产出物不必一次做完。第一轮可以是「一页纸 + 三个待办」,重点是让判断留下痕迹。没有痕迹的架构讨论,三个月后只能靠记忆复现。
小结
卷四是一本判据书。它不提供可以照抄的架构,而提供一组让架构可被讨论、可被验收、可被演进的方法。
读法建议:先读每章的现场与判据,再回来读代码;先填自己的承诺矩阵,再看书里的例子;先在小范围跑一条治理规则,再谈平台化。
从目录与学习路径 开始。
常见错误与边界
- 把判据当成结论,跳过「如果……那么……」直接照搬。
- 只读代码不读反例,误以为模型覆盖了所有情况。
- 先建平台再找用途,导致组件很多但没人用。
- 同时启动多项治理,团队把它们当成流程负担。
- 把 L1 的纯判断当成真机验证,忽略平台差异。
- 用「测试全绿」替代证据,不记录跑了哪一级、断言了什么。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。