Skip to content

资料库 · 软件设计与重构

第 ⑤ 卷,条目 81–100。来源以 martinfowler.com 为主。

前四卷讲的都是"怎么写",这一卷讲的是怎么改。这两件事的难度完全不同——写新代码是从零开始,改代码得在一堆约束下动手,而且几乎所有工程时间都花在后者上。

这一卷也是本资料库里过期最慢的一卷。第 89 条那篇《持续集成》写于二十多年前,今天读仍然成立,而第四卷里两年前的镜像建议就已经不能照抄了。


重构与代码演进

81. Workflows of Refactoring

来源:martinfowler.com,Martin Fowler。

读它解决什么:知道重构手法之后,想知道在真实工作里什么时候用哪种。

要点

  • 主题是重构的几种工作流(而非具体手法)
  • 作者是《重构》一书的作者

我的补充:这篇填的是《重构》那本书的空白——书里教的是手法目录(提取函数、内联变量……),这篇讲的是时机。几种常见工作流:准备性重构(要加功能,先把代码改成容易加的形状,这是我用得最多的一种)、理解性重构(读不懂就一边读一边改名字和结构,把理解沉淀进代码)、捡垃圾式重构(路过看到小问题顺手改)。关键判断是最后一种的边界:顺手改和"这个 PR 怎么变成了 800 行"之间只有一线,我的规矩是顺手改不能碰测试之外的行为,一旦要动行为就单开一个提交。

阅读原文 →

82. The Economic Benefit of Refactoring

来源:martinfowler.com,Exploring Gen AI 系列,2026 年。

读它解决什么:老板问"重构能带来什么",这是回答这个问题的论证。

要点

  • 论述重构的经济收益,属于 Exploring Gen AI 系列
  • 发布于 2026 年,写在 AI 编码工具普及之后

我的补充:论证重构价值时,最没用的说法是"代码更干净"——那听起来像审美偏好。有效的说法是把它换算成变更成本:同一个功能,在整洁的代码里两天,在纠缠的代码里两周,而且后者引入回归的概率高得多。放在 AI 编码的语境下还多一层:结构混乱的代码库让 AI 代理的表现明显变差。上下文窗口有限,一个职责混杂的两千行文件会挤掉真正相关的信息;而职责清晰、命名准确、有测试兜底的代码库,代理改起来准确率高得多。所以"为了 AI 好用而重构"在今天是一个能说服人的新论据。

阅读原文 →

83. Is Design Dead?

来源:martinfowler.com,经典文章。

读它解决什么:敏捷强调不做大而全的前期设计,那设计这件事还存在吗。

要点

  • 讨论在敏捷/XP 语境下软件设计的地位
  • 是 Fowler 早期的经典文章之一

我的补充:这篇要回答的是一个至今还在被误解的问题。"敏捷不做前期设计"被很多团队读成了"不做设计",结果是每次迭代都在补上一次迭代留下的坑。文章的真实立场是:设计从一次性的前期活动变成了持续进行的活动,靠重构和测试来支撑。判断标准很实际——如果你的代码库让"改设计"变得极其昂贵,那你就是被迫要做前期设计;反之,设计才能演进。这也解释了为什么自测试代码(第 88 条)是演进式设计的前提而不是配套。

阅读原文 →

84. Code As Documentation

来源:martinfowler.com(bliki)。

读它解决什么:注释和文档写多少合适,代码本身能承担多少表达职责。

要点

  • 主题是把代码本身当作文档
  • bliki 条目,短篇

我的补充:这个观点常被滥用成"好代码不需要注释",那是错的。代码能表达的是做了什么(what)和怎么做(how),表达不了的是为什么(why)——尤其是"为什么没用那个更显然的做法"。这类信息只能写在注释里。我在这个博客的代码里就留了这种注释:fetchJsonWithTimeout 上面那段写的是"不要再写 Promise.race([fetch, timeout]),那种写法只是让调用方提前拿到 reject,底层请求仍在跑"——代码里看不出这个坑,读者只会觉得当前实现啰嗦,然后"简化"回错误版本。config.ts 里那三条 permalink 兜底规则上面也是同样性质的注释:说明为什么顺序不能动。

阅读原文 →

85. Evolutionary Database Design

来源:martinfowler.com。

读它解决什么:应用代码能持续重构,数据库 schema 怎么跟着演进。

要点

  • 主题是数据库设计的演进式方法
  • 与演进式设计(第 83 条)是同一思路在数据层的延伸

我的补充:数据库是演进式设计里最难的一块,因为它有状态,改错了不能靠回滚代码来救。核心实践是:schema 变更全部走版本化的迁移脚本(不允许手工在生产上敲 DDL),且每个迁移都要能向前兼容——部署过程中新旧版本的应用代码会同时在跑,所以"加列"必须允许旧代码不写它,"删列"要拆成两步(先让新代码不再读它,下个版本再真删)。这个"扩展-收缩"模式是零停机部署的必要条件。破坏性迁移和代码发布同一次上线,是我见过最容易导致长时间故障的操作模式。

阅读原文 →

86. Frequency Reduces Difficulty

来源:martinfowler.com(bliki)。

读它解决什么:为什么"疼的事情做得更频繁"反而更轻松。

要点

  • 论点是提高频率能降低单次难度
  • bliki 短条目

我的补充:这是我认为最有杠杆的一条工程原则,因为它反直觉。部署很痛苦,直觉是少部署;结果攒了三周的改动一次上线,风险更大、出事更难定位、于是更害怕部署——恶性循环。反过来,一天部署十次,每次改动小、出问题一眼能看出是哪个变更、回滚也简单。同样的道理适用于:合并(频繁合并 → 冲突小)、依赖升级(每月升 → 每次几个小版本;两年升一次 → 变成重写)、测试(每次提交跑 → 失败原因明确)。凡是你在推迟的技术动作,先问一句"如果把频率提高十倍,它会变简单还是变难",答案通常是变简单。

阅读原文 →

87. Early Pain

来源:martinfowler.com(bliki)。

读它解决什么:为什么要主动把痛苦提前,而不是能拖就拖。

要点

  • 论点是尽早暴露问题的价值
  • bliki 短条目

我的补充:具体化到工程实践就是:跨系统集成放在项目开头做而不是结尾(集成问题往往需要改架构,越晚发现越贵)、性能测试别等到上线前一周、部署流水线在写第一行业务代码之前就搭好。这条和上一条互为支撑——提高频率的本质就是让痛苦早且小地发生。反面模式我见得最多的是"先做功能,最后统一做部署和监控",结果最后那两周同时面对配置问题、权限问题、性能问题,每一个都可能要求改已经写完的代码。

阅读原文 →


测试与持续集成

88. Self Testing Code

来源:martinfowler.com(bliki)。

读它解决什么:什么叫"自测试代码",它和"写了单元测试"的区别在哪。

要点

  • 定义自测试代码这个概念
  • bliki 条目,是 CI 与重构的前置概念

我的补充:定义的关键在**"你能靠一条命令确认没坏",而不是"覆盖率有多少"。这是重构(第 81 条)和演进式设计(第 83 条)能成立的唯一前提**——没有可信的测试套件,任何非平凡的重构都是赌博,理性的开发者会选择不动它,于是代码只能不断变糟。判断自己的项目是否达标,问三个问题:跑测试是不是一条命令?跑完之后你敢不敢直接部署?测试失败时是不是总是意味着真有问题(flaky 测试会摧毁这个信任,这也是 第二卷第 30 条 synctest 那么重要的原因)?三个都是"是"才算。

阅读原文 →

89. Continuous Integration

来源:martinfowler.com,经典长文。

读它解决什么:搞清 CI 的原始定义,它和"配了 GitHub Actions"不是一回事。

要点

  • CI 的经典阐述,是这个概念最常被引用的来源
  • 覆盖实践、动机与常见误解

我的补充今天绝大多数团队说的 "CI" 都不是这篇文章说的 CI。 原始定义的核心是"每个人每天至少把代码合进主干一次"——重点在集成频率,工具只是让它可行的手段。跑着 GitHub Actions 但功能分支活了三周,那不是 CI,那是"有自动化测试的特性分支开发"。区别在哪?前者冲突小且持续暴露,后者把集成风险攒到最后一次合并。真要判断自己在做哪个,看一个数据就够:分支从创建到合并的中位时长。超过两三天,你就没在做 CI。这也是第 86 条那条原则的直接应用。

阅读原文 →

90. TDD inside the agent loop - theater or actual value?

来源:martinfowler.com,Exploring Gen AI 系列,2026 年 8 月。

读它解决什么:让 AI 代理按 TDD 流程写代码,到底有用还是只是演戏。

要点

  • 探讨 TDD 在 AI 代理循环中的作用,标题自带质疑("theater or actual value")
  • 2026 年 8 月,是本卷最新的一条

我的补充:这个质疑很值得认真对待。TDD 对人的价值有相当一部分是认知性的——先写测试逼你想清楚接口和边界。代理不需要这种思考约束,它可以直接生成实现。所以问题变成:TDD 对代理还剩什么价值?我的实际观察是剩了一条很关键的:测试是可执行的验收标准。让代理自己写实现再写测试,它会写出"恰好通过自己实现"的测试,包括把 bug 一起固化进断言里。反过来,测试由人先定(或者至少人审过)再让代理去实现,测试就成了独立的判据。但要警惕它的失败模式:代理为了让测试通过而写死返回值、或者偷偷改测试断言——所以测试文件的改动必须人工 review,这是这套流程里不能自动化的一环。

阅读原文 →

91. It's Not Just Standing Up: Patterns for Daily Standup Meetings

来源:martinfowler.com。

读它解决什么:站会开成了低效的逐个汇报,想知道有哪些改进模式。

要点

  • 每日站会的模式集合
  • 标题指出站会的价值不在于"站着"这个形式

我的补充:站会退化的标准形态是面向管理者的进度汇报——每个人对着老板说昨天干了什么,其他人低头看手机。判断依据很简单:如果这个会的内容写在文档里效果一样,那它就不该是会。有效的站会解决的是需要同步的事:谁被卡住了、谁的改动会影响谁、今天有什么风险。我见过最有效的调整是把顺序从"按人"改成"按工作项"——顺着任务板走,讨论的自然就是工作的流动而不是个人的表现。

阅读原文 →

92. Pair Programming Misconceptions

来源:martinfowler.com(bliki)。

读它解决什么:结对编程的常见误解。

要点

  • 澄清对结对编程的误解
  • bliki 条目

我的补充:最普遍的误解是"两个人做一个人的活,效率减半"。这个算法把编程当成了打字。真实的收益在别处:知识扩散(避免某个模块只有一个人懂,这是团队最实在的风险)、即时 review(比事后 PR review 发现问题早得多,返工成本低)、减少走神。但也别当银弹——全天结对对多数人是消耗性的,长时间连续结对会导致疲劳和敷衍。我的取舍是:在复杂或不熟悉的部分结对,在机械的部分各干各的。另外新人上手期结对的收益远高于平时。

阅读原文 →


团队、架构与 AI 时代的工程

93. The Orchestrator's Tax

来源:martinfowler.com,2026 年。

读它解决什么:当你从写代码转为协调多个 AI 代理,隐性成本是什么。

要点

  • 提出"编排者的税"这个概念
  • 2026 年发布,讨论 AI 协作模式下的成本

我的补充:这个"税"我有直接体感。协调多个并行任务时,成本不在任何单个任务上,在上下文切换和结果核验上——你得记住每条线在哪、回来时重建上下文、并且验证每份产出(代理会给出看起来合理但错的东西)。核验成本是最容易被低估的一项:如果你不逐条查,那些产出就是不可信的;如果你逐条查,"省下的时间"可能大半又还回去了。所以并行度不是越高越好,存在一个最优点,超过之后编排税吃掉全部收益。判断标准是:任务之间越独立、验证越容易自动化(有测试、有编译器),并行的收益越大。

阅读原文 →

94. The Conductor Developer

来源:martinfowler.com,Rachel's Ramblings,2026 年。

读它解决什么:开发者的角色从"演奏者"变成"指挥"意味着什么。

要点

  • 提出 conductor developer 这个角色隐喻
  • 属于 Rachel's Ramblings 专栏

我的补充:这个隐喻有个常被忽略的含义:指挥必须懂每一件乐器。不懂就听不出哪里错了,也无法给出有效的修正。这直接反驳了"有了 AI 就不用学基础"这种说法——恰恰相反,当你不再逐行写代码时,判断力成了你唯一的产出,而判断力来自于对细节的理解。这一整套资料库的编排方式也是这个逻辑:我能给出"我的补充"是因为我踩过那些坑,光靠罗列链接谁都能做,值钱的是判断哪条重要、哪条会误导。

阅读原文 →

95. The Archaeologist's Copilot

来源:martinfowler.com,2026 年。

读它解决什么:用 AI 辅助理解遗留代码库的可能性与限度。

要点

  • 以"考古学家的副驾"比喻 AI 在理解既有代码中的作用
  • 2026 年发布

我的补充:这是我认为 AI 在软件工程里当前最扎实的用途——理解代码比生成代码的容错空间大得多。读一个没文档的十万行代码库,AI 能快速给出模块地图、找出某个函数的所有调用点、解释一段费解的逻辑,出错了你能立刻从代码里核对。相比之下让它直接改核心逻辑,风险高得多。但有个界限要认清:AI 推不出"为什么"。它能告诉你这段代码做了什么,说不出当年为什么这么写、绕开了什么坑。那些信息在 git 记录、issue 讨论、以及待过的老同事脑子里。这也是第 84 条那种注释真正的价值。

阅读原文 →

96. DSLs Enable Reliable Use of LLMs

来源:martinfowler.com,2026 年。

读它解决什么:怎么让 LLM 的输出变得可靠,DSL 在其中起什么作用。

要点

  • 论点是领域特定语言能提升 LLM 使用的可靠性
  • 作者长期写 DSL 相关内容(著有《领域特定语言》)

我的补充:这条的逻辑很硬:让 LLM 生成受约束的东西,比生成通用代码可靠得多。DSL 的表达空间小、有语法校验、能在执行前静态检查,于是"生成 → 校验 → 执行"这个回路可以自动化,错误在执行前就被拦住。生成通用代码则相反,语法正确但语义错误的产出无法自动识别。可以直接落地的形式:让模型输出 JSON Schema 约束的结构化数据、SQL、配置片段、或者你自己定义的小语言,而不是让它写一段任意的脚本。这也是为什么"结构化输出"这个能力比表面看起来重要——它把不可验证的自然语言变成可验证的数据。

阅读原文 →

97. Viability of local models for coding

来源:martinfowler.com,Exploring Gen AI 系列,2026 年 7 月。

读它解决什么:本地跑的模型现在能不能用来写代码,看哪些因素。

要点

  • 分析本地模型用于编码的可行性因素
  • 与下一条构成一组(因素分析 + 实际体验)

我的补充:本地模型的驱动力主要是合规(代码不能出内网)而不是省钱。要衡量的因素:显存决定你能跑多大的模型,上下文窗口决定能塞进多少代码(这一项对代码任务尤其关键,小窗口下代理连不完整的调用链都看不到),输出速度决定交互体验。我的现实判断是:本地模型在补全和局部改写上已经够用,在跨文件的代理式任务上和云端顶级模型还有明显差距,主要卡在上下文长度和多步推理的稳定性上。如果你的约束是"代码绝不能外传",那就按能力边界安排用途,别指望它替代全部工作。

阅读原文 →

98. Experiences with local models for coding

来源:martinfowler.com,Exploring Gen AI 系列,2026 年 7 月。

读它解决什么:本地模型的实际使用体验,与上一条的理论分析互补。

要点

  • 记录使用本地模型编码的实际经验
  • 与第 97 条同期发布,构成分析加实践的一对

我的补充:这类实际体验记录的价值高于跑分。编码任务的基准测试(HumanEval 那类)和真实工作的相关性很弱——真实工作里的难点是理解既有代码、遵守项目约定、跨文件改动的一致性,而基准测试大多是独立的小函数题。所以看到某个本地模型"跑分接近某个云端模型",不要直接换算成"能替代它"。要判断就在自己的代码库上试三五个真实任务,这比任何榜单可信。

阅读原文 →

99. Citizens Build, Agents Execute, Experts Govern

来源:martinfowler.com,Rachel's Ramblings,2026 年 8 月。

读它解决什么:AI 时代软件开发中不同角色的职责怎么划分。

要点

  • 提出三分的职责模型:公民构建、代理执行、专家治理
  • 2026 年 8 月,本卷最新条目之一

我的补充:这个三分法里最关键的是**"专家治理"那一层。低代码加 AI 让业务人员自己能造出东西("公民构建"),这在过去二十年反复出现过(Excel 宏、Access、各代低代码平台),每次的结局都一样:造得很快,然后没人能维护、没有测试、没有权限模型、没有审计。差别在于这一轮的产出量级大得多。所以"治理"不是官僚,是让这些东西不会在两年后变成没人敢碰的负债**:数据访问边界、可观测性、以及"哪些东西允许进生产"的明确规则。这一层做不好,前面两层的速度收益都会被后续的清理成本吃掉。

阅读原文 →

100. The Agile Fluency Model

来源:martinfowler.com,Diana Larsen 与 James Shore。

读它解决什么:团队的敏捷实践处于什么阶段,下一步该投入什么。

要点

  • 提出敏捷流畅度的分级模型
  • 由 Diana Larsen 与 James Shore 撰写,发表在 martinfowler.com

我的补充:这个模型有用的地方在于它把"敏捷"从一个是非题变成了分级的能力,并且明确指出每一级都需要真实的投入(培训、结构调整、时间),不投入就上不去。这解释了大量"敏捷转型失败"的案例:只改了会议形式(站会、看板、迭代),没有改任何工程能力(自动化测试、持续集成、部署流水线),于是拿不到任何收益,然后得出"敏捷没用"的结论。用它做自我评估时,最诚实的做法是从结果倒推而不是从实践清单打勾:你的团队从想法到上线要多久?出故障多久能恢复?这两个数字比"我们做了哪些实践"准确得多。

阅读原文 →


本卷小结

三个带走的结论:

  1. 自测试代码是一切的前提。 重构、演进式设计、持续集成、乃至让 AI 代理安全地改你的代码,全部依赖"一条命令确认没坏"这个能力。没有它,其他实践都只是形式。
  2. 提高频率能降低难度。 部署、合并、依赖升级、测试——凡是你在推迟的技术动作,先问"频率提高十倍会更简单还是更难"。答案几乎总是更简单,而这与直觉相反。
  3. AI 让判断力变成主要产出。 不再逐行写代码时,你的价值在于能看出哪里错了,而这依赖对细节的理解。"有了 AI 不用学基础"的推论方向是反的。

上一卷: ← ④ 打包、依赖与容器化 · 回到 编程资料库总览

其他分类的资料库: 运维 · 前端