第2章 务实的方法
以易于变更为总目标,串联 DRY、正交、可逆、曳光、原型、领域语言和估算。
学习目标
- 能把“第2章 务实的方法”解释为一条以易于变更为目标的路径,并连接 Topic 8–15 的设计、反馈和估算证据
- 能用 DRY、正交、可逆、曳光、原型和领域语言减少隐藏耦合,再用估算把假设写成可迭代的范围
- 能在一个真实项目中注入单一变化或故障,定位首个偏离,比较恢复成本,并让独立复核者重放结论
为什么第2章 务实的方法不可压缩
本页依据 David Thomas、Andrew Hunt《程序员修炼之道:通向务实的最高境界(第2版)》,云风译,电子工业出版社,2020年4月,ISBN 9787121384356 的公开完整中文目录,独立重构 第2章 务实的方法。本页不复制原书正文、插图、练习答案或代码,而把目录命题转写成变更地图、决策回路、反例和复核证据。
本章的共同问题不是“有没有一种最好的架构”,而是下一次需求、工具、数据或约束变化时,团队能否用有限步骤找到影响范围、取得反馈并安全回退。DRY 处理知识的重复,正交处理变化的扩散,可逆处理过早锁定,曳光和原型处理未知,领域语言处理规则表达,估算处理时间与范围的不确定性;它们共同服务于可变更性。
第2章 务实的方法:一份可复核的地图
| 目录坐标 | 关键问题 | 可观察证据 | 失败时的动作 |
|---|---|---|---|
| 8 优秀设计的精髓 | 哪种设计更容易改变 | 变更触达图、反馈时间和回退步骤 | 缩小边界并比较另一个设计 |
| 9 DRY——邪恶的重复 | 哪条知识只有一个权威源 | 来源链接、生成检查和重复删除记录 | 先区分相似代码与重复知识 |
| 10 正交性 | 一个局部变化是否扩散 | 依赖图、独立测试和受影响节点 | 移除隐藏状态或隔离边界 |
| 11 可逆性 | 这个决定能否撤回 | 替代方案、出口和回滚演练 | 把最终决定拆成可撤回实验 |
| 12 曳光弹 | 端到端路径能否尽早反馈 | 真实边界上的最小纵向切片 | 连接缺失边界而不是继续堆层 |
| 13 原型与便签 | 未知是否被低成本验证 | 问题、实验、丢弃条件和学习记录 | 丢弃原型并保留结论,不搬进生产 |
| 14 领域语言 | 规则是否由问题域表达 | 术语、语法、错误信息和领域示例 | 与领域专家核对歧义和缺口 |
| 15 估算 | 范围和假设怎样随反馈更新 | 单位、区间、假设、历史偏差 | 用新样本重估,不维护伪精确日期 |
Changeability Contract 把“容易修改”变成可以观察的步骤、节点和边界,而不是设计者的主观评价。
↡某条业务规则或事实只在一个明确源头维护,其他位置通过引用、生成或检查获得它。Single Source of Truth 是 DRY 的工程落点;它不要求所有相似代码合并,也不允许同一知识在多个地方独立演化。
↡一个组件、决定或状态的变化不会通过隐藏依赖扩散到不相关的组件、流程或环境。Orthogonality Boundary 要明确数据、控制、时间和责任的边界,才能判断一次修改究竟影响了谁。
↡保留替代路径、数据出口和回滚动作,使尚未掌握的信息不会被一次不可撤回的选择锁死。Reversible Decision 不是拒绝做决定,而是先把代价高的承诺拆成可学习、可撤回的步骤。
↡连接真实输入、核心处理和真实输出的最小端到端路径,用连续反馈校准方向和估算。Tracer Path 不是展示用的假路径;它必须触达真实边界,并且能暴露接口、数据、部署或用户结果的缺口。
8 优秀设计的精髓:把可变更性写出来
提示14:优秀的设计比糟糕的设计更容易变更。比较两个设计时,先给同一个变更请求,列出需要改动的节点、需要等待的反馈、需要迁移的数据和可以回滚的动作。更好的设计不是抽象层最多的设计,而是让重要变化在局部完成,并能尽早验证影响的设计。
一个 Changeability Contract 至少包含变更触发、所有者、触达节点、反馈入口、拒绝条件和恢复动作。若设计 A 要同时改接口、数据库、部署和多个消费者,设计 B 只需改一个策略边界并通过契约测试验证,B 的优势来自可观察步骤,而非“看起来更优雅”。
9 DRY——邪恶的重复:维护知识的唯一来源
提示15:DRY——不要重复自己,关注的是知识重复,不是字符重复。两个函数可能长得相似却表达不同规则;相反,一条费率、权限或接口约束可能以代码、配置、测试和文档多次出现,才是真正危险的重复。
提示16:让复用变得更容易要求复用入口有清楚的边界、示例和失败行为。复用如果需要调用者先了解一串隐含状态,反而会催生复制。建立 Single Source of Truth 后,让生成、引用或检查把知识带到其他边界,并记录删除重复副本的理由。
10 正交性:让局部变化停在边界内
提示17:消除不相关事物之间的影响。先画出数据、控制、时间和责任的依赖,再对一个局部变量做变化。如果无关模块也改变,说明共享状态、全局配置、顺序假设或过宽的接口把变化泄漏了。
正交不是让组件永远不通信,而是让通信有明确协议、所有者和失败边界。可以用独立测试、替代实现和受影响节点清单检查它:测试失败的第一处通常比最终红灯更能说明耦合从哪里开始。
11 可逆性:把未知变成可撤回的实验
提示18:不设最终决定要求对仍有未知的架构、存储、供应商和接口保留出口。记录候选方案、学习目标、有效期限、迁移成本和回滚动作,先用小范围实验获得信息。
提示19:放弃追逐时尚不是拒绝新工具,而是把工具的流行度与当前问题的证据分开。一个新技术只有在目标约束、反馈成本和团队能力中显示收益,才值得增加承诺;否则保留替代方案并设置停止条件。
12 曳光弹:先连接真实边界
提示20:使用曳光弹找到目标。Tracer Path 把输入、关键处理、持久化、部署和用户结果串成最小可运行切片,尽早暴露“每层都完成但端到端不通”的假进度。它的价值在于反馈,不在于最终结构是否被保留。
13 原型与便签:为学习而丢弃
提示21:用原型学习。原型开始前写出要回答的问题、允许牺牲的性质、时间盒和丢弃条件;结束时留下学习记录、证据和未解决问题。若原型代码没有经过生产约束审查,就不能因为演示顺利而直接成为长期边界。
14 领域语言:让规则靠近问题域
提示22:靠近问题域编程。领域语言不仅是类名或一张术语表,还包括规则的表达方式、可组合的动作、错误信息和边界示例。让领域专家能指出“这句话在业务上不成立”,让程序员能把反馈定位到规则,而不是只看到通用异常。
15 估算:用范围和反馈避免意外
提示23:通过估算来避免意外要求声明数量、单位、假设、区间和风险,而不是报出没有依据的单点日期。提示24:根据代码不断迭代进度表要求随着曳光路径、原型和真实实现提供新证据,更新剩余工作与区间,并保留旧预测供复盘。
估算是当前知识的模型,不是对未来的承诺。比如把“还剩三周”拆为工作项、等待、迁移、验证和发布窗口后,团队才能看见哪一个假设最可能改变范围,也才能决定下一次最有价值的反馈是什么。
变更地图与决策回路
两张图配合使用:变更地图说明目录节点如何减少扩散,决策回路说明如何在未知处停下来学习。任何节点都要有输入、输出、所有者、失败动作和可重放证据;只有名称没有变化和反馈的箭头,不能证明理解。
三步务实路径
先画变化与知识边界
选一个真实需求,建立 Changeability Contract,标出 Single Source of Truth、共享状态、领域术语和最可能的变化。对两个设计分别列触达节点、反馈入口、迁移成本和回滚步骤。
一个可重放的章节实验
pragmatic_method_experiment:
unit: tpp20-chapter-02-pragmatic-approach
change_target: 发布策略需要支持第二种回滚方式
single_source_of_truth: 发布契约与契约测试
orthogonality_boundary: 策略选择与业务处理分离
reversible_decision: 先对一组低风险租户启用新策略
tracer_path: 请求 -> 策略选择 -> 发布 -> 用户结果
prototype_question: 新策略是否能在不改业务规则时切换
domain_language: 启用、暂停、回滚、迁移窗口
estimate: 工作量区间、等待项、验证项和剩余风险
first_mismatch: 回滚动作更新了策略却没有更新发布清单
recovery: 连接契约测试和清单检查,重放发布演练实验先固定项目基线、输入身份、工具版本和成功标准,再一次只改变一个条件。正常样本证明路径能走通,边界样本证明系统会明确接受或拒绝,一次故障样本证明首个失配可见且能回退。把这些结果与旧估算并列,才能知道学到的是事实还是愿望。
选择、拒绝与迁移矩阵
| 判断 | 接受证据 | 应拒绝的信号 |
|---|---|---|
| DRY | 规则有唯一源头,生成或检查能发现漂移 | 多份副本靠口头提醒同步 |
| 正交 | 局部改动只触达声明的接口和测试 | 无关模块因全局状态一起变化 |
| 可逆 | 有替代方案、出口、期限和回滚演练 | 以“以后再说”掩盖不可撤回承诺 |
| 曳光 | 最小路径触达真实边界并产出反馈 | 每层都绿但没有用户结果 |
| 原型 | 问题、时间盒、丢弃条件和学习记录齐全 | 演示代码悄悄进入生产 |
| 领域语言 | 规则、示例和错误信息能被领域专家核对 | 通用技术词遮住业务边界 |
| 估算 | 区间、单位、假设和新证据持续更新 | 单点日期没有来源和偏差记录 |
迁移到云服务、移动端、数据系统或 AI 辅助开发时,分别记录团队规模、发布频率、数据敏感性、自动化反馈和监管约束。工具可以缩短反馈,却不能替团队判断哪条知识是事实、哪项决定仍可撤回、哪条估算假设已经失效。
常见误区
本章回顾
掌握 第2章 务实的方法,不是背诵一组设计口号,而是能以易于变更为目标:用 DRY 找到知识单源,用正交边界限制扩散,用可逆决定保留选择权,用曳光路径和原型获得反馈,用领域语言表达规则,再根据新证据迭代估算。独立复核者应能重放一次变化、看到首个偏离、执行回退,并解释哪些结论还没有覆盖。
可验证练习
练习
本组练习覆盖 第2章 务实的方法、Topic 8–15、提示14–24,要求提交 Changeability Contract、Single Source of Truth、Orthogonality Boundary、Reversible Decision 和 Tracer Path 的证据,并把新反馈写回估算。
问题 1: 一个发布系统的业务规则、配置、测试和运行手册各自维护了一份回滚条件。如何用 DRY、正交和可逆性改造它?
问题 2: 如何用“提示20:使用曳光弹找到目标”和“提示21:用原型学习”探索一个未知的支付服务?
问题 3: 项目已完成一半,但原估算仍是三周。如何根据“提示23:通过估算来避免意外”和“提示24:根据代码不断迭代进度表”重建进度?
名词解释
名词解释
本章出现的专业名词,用大白话再讲一遍。
- Changeability Contract
用触达范围、反馈时间、恢复难度和边界比较设计是否容易变更的合同。
- Single Source of Truth
某条知识或规则的唯一权威源,其他位置通过引用、生成或检查获得它。
- Orthogonality Boundary
让局部变化不扩散到不相关组件的明确数据、控制、时间和责任边界。
- Reversible Decision
保留替代路径、出口和回滚动作的可撤回决定,用于在未知中取得信息。
- Tracer Path
连接真实输入、核心处理和真实输出的最小端到端路径,用连续反馈校准方向。
前后导航
来源与改写范围
- Pragmatic Programmer 作者页面:核对第2章、Topic 8–15与提示14–24的版本位置和主题范围。
- 中文目录页面:核对第2章 务实的方法、8 优秀设计的精髓、9 DRY——邪恶的重复、10 正交性、11 可逆性、12 曳光弹、13 原型与便签、14 领域语言、15 估算及其提示的公开目录范围。
- 出版社书目信息:交叉核对中文译本的出版信息与版次边界。