代码质量与重构方法论

从7本经典著作中提炼的代码质量核心原则与重构实践精华。

来源书目

| # | 书名 | 作者 | 核心主题 | |---|------|------|----------| | 1 | 代码整洁之道 | 罗伯特·C·马丁(Bob大叔) | 专业主义精神、TDD、测试策略 | | 2 | 重构:改善既有代码的设计(第2版) | Martin Fowler | 重构手法、代码坏味道、测试安全网 | | 3 | 好代码坏代码 | 汤姆·朗(Tom Long) | 代码质量三维模型、抽象层次、错误处理 | | 4 | 整理优先?小改进大回报 | 肯特·贝克(Kent Beck) | 15种整理手法、整理节奏、期权理论 | | 5 | 程序员修炼之道 | David Thomas & Andrew Hunt | DRY、正交性、破窗效应、务实哲学 | | 6 | 代码之外 | 朱征宇 | 系统性思维、工程权衡、质量文化 | | 7 | 领域驱动设计:业务建模与架构实践 | 王红亮 | DDD落地、统一语言、聚合设计、限界上下文 | | 8 | 程序员的三门课 | 于君泽 | 架构五原则、技术债分类、PDCA循环 | | 9 | 云原生应用架构实践 | 网易云 | 三阶段架构演进、够用就行的初创哲学 | | 10 | 分布式应用系统架构设计与实践 | 谢文辉 | 分布式四大目标、缓存问题矩阵、数据分片策略 | | 11 | 解决方案架构师修炼之道 | 所罗伯·斯里瓦斯塔瓦 | 架构卓越五支柱、6R云迁移策略、C4模型 | | 12 | 重构个体 | 刘勇(Axton) | 架构思维=资产化思维、模块化/可复用/可迭代 | | 13 | 软件架构师的12项修炼 | 亨德里克森 | 架构师金字塔、70%时间在沟通、语境切换 | | 14 | Google系统架构解密 | 希瑟·阿德金斯 | 纵深防御、爆炸半径控制、安全≠可靠性对立 | | 15 | Effective软件架构 | 奥利弗·戈德曼 | 架构=高变更成本决策、ADR、演化式架构 |


一、什么是"好代码"——多本书的共同定义

综合7本书的观点,好代码有四个共识维度:

  1. 可读性:代码被阅读的次数远多于编写的次数。程序员约60-70%的时间在读代码(费莉安·赫尔曼斯《程序员超强大脑》),可读性是最值得投入的质量属性。
  2. 可靠性:能正确处理边界情况,错误时 Fail Fast 而非静默失败。
  3. 可维护性:改一个小bug不需要改三个文件,新人能在合理时间内理解模块。
  4. 可变性:ETC原则——Easier To Change。好设计的终极标准是让未来的变更更容易(David Thomas)。

"任何傻瓜都能写出计算机能理解的代码。优秀的程序员写的是人能理解的代码。" —— Martin Fowler

代码质量三角(Tom Long)

可读性、可靠性、可维护性三者缺一不可。能跑只是最低门槛,真正衡量代码质量的是同事能不能看懂、半年后自己还能不能理解、改起来有多大信心。

质量是一种习惯,不是事后打磨

Bob大叔和Tom Long都强调:质量不是Code Review时才关注的事,而是写代码时就应该有的意识。每次提交前问自己:"三个月后看这段代码,能看懂吗?"


二、核心设计原则

2.1 DRY——每条知识只有一个权威来源

来源:《程序员修炼之道》Tip 15

DRY不是"代码不重复",而是"知识不重复"。如果同一个业务规则出现在代码、文档、配置文件三个地方,改了一处忘了改另一处就是bug。

注意区分(Tom Long):

  • 知识重复(有害):同一段业务规则出现在三处,改一处漏一处
  • 代码巧合(可接受):两段代码恰好长得像,但各自处理不同业务场景,强行合并反而制造耦合

实践:建立"三次原则"——同样的代码出现三次再抽象,不要第一次就。

2.2 正交性——消除不相关事物之间的影响

来源:《程序员修炼之道》Tip 17

修改UI不应该影响数据库逻辑,修改数据库不应该影响网络层。组件应该自成一体、独立自主、有单一清晰的职责。

正交性的检验标准:如果你改了模块A,模块B的行为意外变了,说明它们不够正交。

2.3 ETC——更容易变更

来源:《程序员修炼之道》Tip 14

如果一个设计让未来的变更变得困难,那它就是坏设计。这是好设计的终极判据——比"是否符合设计模式"更有价值。

2.4 破窗效应——不要放过任何小问题

来源:《程序员修炼之道》Tip 5

一扇不修的破窗会传递"没人管"的信号,导致更多破窗。代码同理——一个糟糕的变量命名就是"第一扇破窗",不修的话半年后代码质量彻底失控。

"只要看到不好的设计、错误的决策、糟糕的代码,就赶紧去纠正。" —— David Thomas

勒布朗法则(Bob大叔):Later equals never。稍后等于永不。你以为以后会回来优化这段代码,但你不会的。


三、代码坏味道与识别

来源:《重构》第3章 + 《好代码坏代码》

Fowler列出的"坏味道"是最实用的代码质量诊断工具。以下是最高频出现的几种:

| 坏味道 | 信号 | 推荐处理 | |--------|------|----------| | 重复代码 | 同一段逻辑出现在两个以上地方 | Extract Function / Extract Class | | 过长函数 | 超过30行,需要写注释解释"这段在做什么" | Extract Function(函数名即注释) | | 过大类 | 超过200行,因为不同原因被反复修改 | Extract Class(按变化原因拆分) | | 深层嵌套 | if-else超过3层缩进 | 卫语句(Guard Clause)提前return | | 发散式变化 | 一个类因为不同原因被反复修改 | Extract Class(按变化原因拆分) | | 散弹式修改 | 一个改动需要同时修改多个类 | Move Function / Move Field | | 模糊命名 | tmp、data、result、obj | 用揭示意图的名字替代 | | 魔法数字 | if (req.ConcurrentReq > 10) | 替换为命名常量 |

Kent Beck的建议:"每当感觉需要写注释来说明某段代码在做什么的时候,就把这段代码提取成一个函数,函数名就是那个注释。"


四、重构方法论

4.1 重构的核心原则

来源:《重构》Martin Fowler

定义:在不改变软件可观察行为的前提下,改善其内部结构。

"两顶帽子":任何时候,你要么在添加新功能(添加新行为),要么在重构(不改变行为,只改结构)。不要同时做这两件事,否则出了bug都不知道是哪个导致的。

小步前进:每次只做一个微小的、可独立验证的改动,然后立刻跑测试。如果测试失败,只需要回退最后一步。

"不要为了重构而重构":重构是为了让下一个功能更容易添加、下一个bug更容易修复。如果你不确定该不该重构,问自己:"如果我需要在这里加一个新功能,是会很简单还是会很痛苦?"

4.2 Kent Beck的整理优先框架

来源:《整理优先?》

Kent Beck不追求完美代码,而是追求"比昨天好一点的代码"。他的核心观点:在改代码行为之前,先做一点结构上的整理,会让行为变更更安全、更清晰、更快速。

但"先"字后面带了一个问号——没有铁律,只有判断力。

15种整理手法(按实用性排序):

  1. 卫语句:提前return减少嵌套,主逻辑一气呵成
  2. 删死代码:YAGNI原则,git是安全网
  3. 解释变量:把复杂表达式拆成有意义的中间变量,命名就是注释
  4. 解释常量:消灭魔法数字,每个数字后面都可能隐藏着一条业务规则
  5. 规范化对称性:统一风格,一致性让读者能预测
  6. 新接口旧实现:渐进式替换,先建新接口内部调用旧实现
  7. 阅读顺序调整:核心逻辑放前面,按自然理解路线组织
  8. 声明与初始化放一起:能用const就不用let
  9. 显式参数:函数签名应该让人一眼看出需要什么
  10. 分块语句:用空行分块,最便宜的"段落标记"
  11. 提取辅助函数:独立且用途明确的代码抽成函数
  12. 合并回一块:过度拆分时合并回来重新审视
  13. 解释注释:说"为什么这样做",不说"做了什么"
  14. 删除冗余注释:和代码重复的注释比没有注释更糟糕

整理节奏决策(First / After / Later / Never):

| 优先级 | 适用场景 | 判断标准 | |--------|----------|----------| | First | 先整理再改行为 | 整理能显著降低行为变更的风险 | | After | 先改行为再整理 | 行为变更紧急 | | Later | 等相关需求来了再整理 | 收益不明确 | | Never | 永远不整理 | 代码不会再变动 |

"整理是软件设计的薯片——吃了一片就停不下来。你必须有意识地控制自己。" —— Kent Beck

4.3 重构的前提——测试安全网

来源:《重构》+《代码整洁之道》

没有测试就不要重构。 但 Fowler 的新补充很务实:

  • 不完美的测试也比没有测试好
  • 测试的目的不是证明代码正确,而是给你改代码的信心
  • 面对遗留代码(完全没有测试),先写"特征测试"(Characterization Tests)锁定当前行为,然后才能安全修改

测试金字塔(多本书共识):

           ╱╲
          ╱E2E ╲         少量,慢但全面
         ╱──────╲
        ╱ 集成测试 ╲       适量,平衡速度和覆盖
       ╱──────────╲
      ╱   单元测试   ╲     大量,快速且精准
     ╱──────────────╲

五、抽象与架构思维

5.1 抽象层次(洋葱模型)

来源:《好代码坏代码》Tom Long

最内层是核心业务逻辑,外层依次是基础设施、框架、运行环境。每一层只和相邻层通信,不跨层调用。

泄漏抽象(Leaky Abstraction):当使用者仍然需要了解底层细节才能正确使用时,抽象就失败了。比如一个Cache类,使用者还需要手动调invalidate()。

5.2 依赖管理

来源:《好代码坏代码》

  • 依赖倒置:高层模块和低层模块都依赖抽象,不依赖具体实现
  • 接口隔离:不要让使用者依赖它不需要的东西
  • 循环依赖是架构恶化的信号

5.3 领域驱动设计(DDD)核心要点

来源:《领域驱动设计:业务建模与架构实践》王红亮

三大支柱

  1. 统一语言(Ubiquitous Language):活在代码里的命名约定,不是文档。类名、方法名、变量名都必须使用业务术语。
  2. 限界上下文(Bounded Context):领域模型的边界。同一个词在不同上下文可以有不同含义——不是统一它,而是拆分它。
  3. 领域模型(Domain Model):业务规则的编码表达,不是数据库表结构的映射。

聚合设计(DDD中最难的部分):

  • 聚合要小:1个聚合根 + 少量实体/值对象
  • 通过ID引用跨聚合,不通过对象引用
  • 一个事务只修改一个聚合
  • 聚合根是唯一入口

分层架构

┌─────────────────────────────────┐
│        接口层(Interface)         │
├─────────────────────────────────┤
│        应用层(Application)       │  ← 编排流程,管理事务
├─────────────────────────────────┤
│        领域层(Domain)            │  ← 业务逻辑(核心)
├─────────────────────────────────┤
│      基础设施层(Infrastructure)   │
└─────────────────────────────────┘

依赖方向:领域层不依赖任何外层。应用服务应该"薄到让你觉得这玩意儿有什么用"——它让领域层保持纯粹。


六、错误处理与防御性编程

来源:《好代码坏代码》+《程序员修炼之道》

错误处理黄金法则

让错误难以被忽略,让恢复变得容易。

错误分类

| 类型 | 处理方式 | 示例 | |------|----------|------| | 可恢复错误 | 让调用者决定怎么处理 | 网络超时→重试/降级 | | 不可恢复错误 | Fail Fast,立即暴露 | 空指针→panic | | 内部错误 | 用assert/panic | 这不该发生 | | 外部输入问题 | 防御性校验 | 参数验证 |

契约式设计(DbC)

来源:《程序员修炼之道》

每个函数有三重契约:

  • 前置条件:调用方必须满足什么
  • 后置条件:函数执行完后保证什么
  • 不变式:整个生命周期中始终为真

违反前置条件,函数有权直接崩溃——这不是粗暴,而是让问题尽早暴露。

"死掉的程序比有缺陷的程序造成的损害要小得多。" —— David Thomas


七、专业主义与工程文化

来源:《代码整洁之道》+《代码之外》

专业程序员 vs 业余程序员

| 维度 | 专业 | 业余 | |------|------|------| | 对待bug | 全权负责,主动修复 | 找借口推卸 | | 对待承诺 | 说到做到 | 随意承诺经常食言 | | 对待学习 | 持续精进,每周刻意练习 | 靠工作"顺便"学习 | | 对待代码质量 | 零容忍技术债 | "先跑起来再说" | | 对待压力 | 坚守底线,寻求建设性方案 | 走捷径牺牲质量 | | 对待预估 | 区分预估和承诺 | 拍脑袋不区分 |

说"不"的勇气

专业程序员有义务对不合理的需求和工期说"不"。说"不"不是推诿,而是基于专业判断做出负责任回应。说"不"的正确方式:提供替代方案。

"我会试一试——这是你能听到的最不专业的回答。" —— Bob大叔

技术债务管理

来源:《代码之外》朱征宇

技术债务就像金融债务——适度的债务可以加速发展,但过度债务会导致破产。管理方法:

  • 可视化:让团队看到债务
  • 优先级化:哪些最紧急
  • 定期偿还:每个迭代分配15-20%的时间

架构决策记录(ADR)

来源:《好代码坏代码》

每次做重要技术决策时,写下为什么这么做、考虑了哪些替代方案、放弃了什么。以后回溯时能理解当初的思考过程。好的架构决策是"现在能用、将来好改"的决策。


八、架构决策与系统设计思维

8.1 架构是决策,不是图纸

来源:《Effective软件架构》+《解决方案架构师修炼之道》

架构的本质是"那些后期变更成本极高的决策"(Goldman)。架构师最重要的产出不是架构图,而是决策——在不确定性中做出正确权衡。

架构决策框架(Goldman):

  1. 定义问题——真正的问题是什么?
  2. 识别约束——有哪些不可改变的?
  3. 生成选项——至少3个方案
  4. 分析权衡——得到了什么,放弃了什么?
  5. 做出决策——为什么选这个?
  6. 记录决策——ADR(未来能回溯理由)

这和《好代码坏代码》中提到的ADR理念完全一致,说明好实践在不同层面(代码层和架构层)都是相通的。

架构成熟度模型(Goldman):

Level 1: 无架构 ────── 随意决策,无记录
Level 2: 隐式架构 ──── 有架构但未文档化
Level 3: 显式架构 ──── 架构已记录和审查
Level 4: 演化式架构 ── 架构可验证和演化(适应度函数)
Level 5: 自适应架构 ── 架构自动适应变化

8.2 架构演进的阶段性

来源:《云原生应用架构实践》+《分布式应用系统架构设计与实践》

网易云团队提出的三阶段演进模型,和分布式架构的四大目标(高性能、高可用、可扩展、可维护)呼应:

  • 初创阶段:够用就行,不要过度设计。选团队最熟悉的技术栈,单库单表就够
  • 成长阶段:核心是"拆"——服务拆分、数据拆分、团队拆分
  • 稳定阶段:靠架构层面的冗余和容错,而非单个服务的可靠性

这和Kent Beck的"整理节奏"异曲同工——初创阶段Never整理,成长阶段Later/After,稳定阶段First。时机比完美更重要。

架构设计五原则(于君泽):稳定、解耦、抽象、松耦合、容错。核心思想:稳定与易变分离,核心与非核心分离,主流程与辅助流程分离。

8.3 资产化思维——从代码架构到工作流架构

来源:《重构个体》

Axton提出的"架构思维"不仅适用于代码,也适用于个人工作流设计:

  • 模块化:每个模块有清晰的输入/输出
  • 可复用:做一次用多次,建立模板库
  • 可迭代:持续优化而非一次到位,记录版本和效果
  • 可交接:别人能接手你的系统

这本质上就是DRY和正交性在个人工作层面的延伸。好的架构思维是通用的。

8.4 安全与可靠性的协同设计

来源:《Google系统架构解密》

Google SRE与安全团队的联合观点:安全性和可靠性不是零和博弈,而是共享许多共性——隐形性、评估需求、简洁性追求、弹性。

关键设计原则

  • 纵深防御:多层安全机制,不依赖单点
  • 最小特权:任何组件只拥有完成功能所需的最少权限
  • 控制爆炸半径:限制故障/攻击的影响范围(角色分离、位置分离、时间分离)
  • 可验证构建:构建结果可追溯、可复现
  • 默认安全:简洁的代码更安全,复杂系统更容易出错和被攻破

这和Fowler的"小步前进"、Kent Beck的"整理手法"一脉相承——简洁性是安全、可靠、可维护的共同基础。


九、架构师的软技能

来源:《软件架构师的12项修炼》+《解决方案架构师修炼之道》

金字塔能力模型(亨德里克森)

          /\
         /  \        商业技能(创新、商业理解、认知迁移)
        /──────\
       /        \     个人技能(透明化、激情、语境切换)
      /──────────\
     /            \   关系技能(沟通、协商、领导力、政治)
    /──────────────\
   技术基础(必要但不充分)

越往上走,软技能越重要。架构师70%以上的时间在沟通,不是在编码。

语境切换是架构师的核心能力——在高管面前用商业语言(成本、风险、ROI),在开发者面前用技术语言(代码、架构、权衡),在客户面前用价值语言(体验、效率、可靠性)。

和Bob大叔的"专业主义"互补:Bob强调底线(说不、守承诺),亨德里克森强调上限(沟通、政治智慧、影响力)。


十、关键金句

"你以为你的雇主应该为你的学习买单?不。你的学习是你自己的责任。" —— Bob大叔

"代码会随着时间流逝而烂掉。写得再好的程序代码,若是发布了就一直保持原样,照样会风化、破碎乃至分崩离析。" —— Martin Fowler

"软件的价值不在于你已经做了什么,而在于你还能做什么。" —— Kent Beck

"大部分系统的失败不是因为技术选型错了,而是因为业务理解错了。" —— 王红亮

"当你发现需要为代码添加注释来解释它在做什么时,先把那段代码提取成一个函数,用函数名代替注释。" —— Kent Beck

"Yesterday's best practice is tomorrow's anti-pattern." —— 尼尔·福特

"架构是那些后期变更成本极高的决策。" —— 奥利弗·戈德曼

"架构师最重要的产出不是架构图,而是决策。" —— 奥利弗·戈德曼

"你不能管理你看不到的东西。" —— 奥利弗·戈德曼

"好的架构不是设计出来的,是演化出来的。" —— 奥利弗·戈德曼

"安全是团队的职责,不只是安全团队的。" —— Google SRE团队

"架构师有70%以上的时间在沟通。" —— 亨德里克森

"住在玻璃房中的架构师才能赢得信任。" —— 亨德里克森

"如果我不提的话,公司会为此付出很大成本吗?" —— 亨德里克森


整理自 Obsidian 读书笔记,2026-05-02 by 喵喵 🐈 2026-05-14 融合8本新书(程序员的三门课、云原生架构、分布式架构、解决方案架构师、重构个体、软件架构师12项修炼、Google系统架构解密、Effective软件架构)