一、全书概览

一句话总结: 这本书不是教你写代码的语法,而是教你像工程师一样思考——代码质量、抽象层次、错误处理、测试策略,每一步都有明确的"好"与"坏"对比,帮你从"能写代码"跨越到"写好代码"。

全书结构分为两大部分,前半讲思维模型,后半讲实操手法。Tom Long 在 Google 做了多年软件工程师,他把一线大厂的最佳实践提炼成了一套可执行的规则体系,而且每条规则都告诉你什么时候该遵守、什么时候该打破。

全书结构

| 章节 | 主题 | 一句话概括 | |------|------|-----------| | 第1章 | 代码质量 | 什么是好代码?不只是能跑,而是能让人看懂、能改得动 | | 第2章 | 抽象层次 | 别把所有细节揉在一起,分层才能管理复杂度 | | 第3章 | 工程师协作与代码契约 | 你写的代码不是给你自己看的,要有契约意识 | | 第4章 | 错误处理 | 错误不可避免,关键是怎么处理它 | | 第5章 | 让代码可读 | 代码是给人看的,顺便给机器执行 | | 第6章 | 避免重复 | DRY不是死板教条,而是要让每个知识有单一来源 | | 第7章 | 小心依赖 | 依赖是双刃剑,用好了复用,用坏了牵一发而动全身 | | 第8章 | 做出实用的架构决策 | 架构不是画大图,而是做出能落地的取舍 | | 第9章 | 测试策略 | 测试不是负担,是让你敢改代码的底气 | | 第10章 | 安全编写代码 | 安全是每个开发者的责任,不是安全团队的事 |


二、逐章要点

第1章:代码质量

这章是全书的起点,Tom Long 没有上来就列规则,而是先问了一个问题:你凭什么说一段代码是"好"的?

他给出的标准很务实——好代码有三个特征:可读、可靠、可维护。注意,"能跑"不在这个列表里。一段代码能跑只是最低门槛,就像一个人能走路不代表他会跑步。真正衡量代码质量的是:你的同事能不能在30分钟内看懂这段代码在做什么?半年后你自己回来看还能不能一眼看明白?

核心观点:代码质量不是主观感受,而是可以用可读性、可靠性、可维护性三个维度来衡量的客观属性。

书中一个让我印象深刻的例子是"代码债"的概念。就像金融债务一样,代码债也有利息——你每多写一行难以理解的代码,未来的维护成本就会累加。短期看省了时间,长期看付出了更高的代价。

| 关键概念 | 说明 | |----------|------| | 代码质量三要素 | 可读、可靠、可维护 | | 代码债 | 技术债务的利息会随时间复利增长 | | 代码评审 | 不仅找bug,更是传播质量的机制 | | 质量是一种习惯 | 不是事后打磨,而是写的时候就该有意识 |

行动清单:

  • [ ] 每次提交代码前,问自己:三个月后看这段代码,能看懂吗?
  • [ ] 在 code review 时不只看逻辑,也看可读性
  • [ ] 把代码质量指标加入团队的定义标准中

第2章:抽象层次

这是全书最有技术含量的一章。抽象是软件工程的终极武器——没有抽象,你面对的就是一团乱麻;有了正确的抽象,复杂系统也能变得可控。

Tom Long 用了"洋葱模型"来解释:最内层是核心业务逻辑,外层依次是基础设施、框架、运行环境。每一层只和相邻层通信,不跨层调用。这听起来像废话,但你看看自己的代码库,有多少地方是业务逻辑直接操作数据库连接的?

核心观点:好的抽象让复杂系统变得可理解,坏的抽象反而增加认知负担。

书中特别强调了"泄漏抽象"(Leaky Abstraction)的问题。当你创建了一个抽象层,但使用者仍然需要了解底层细节才能正确使用它时,这个抽象就失败了。比如一个 Cache 类,使用者还需要手动调用 invalidate() 才能保证数据一致性——那这个缓存封装就是泄漏的。

| 关键概念 | 说明 | |----------|------| | 洋葱模型 | 核心业务逻辑在中心,基础设施在外层 | | 泄漏抽象 | 抽象层没有完全隐藏底层细节 | | 单一职责 | 每个模块只做一件事,做好一件事 | | 依赖方向 | 依赖应该指向抽象,不指向具体实现 |

行动清单:

  • [ ] 画一下你当前项目的依赖关系图,看看有没有跨层调用
  • [ ] 审查每个类/模块的公开接口,确保没有泄漏实现细节
  • [ ] 新功能开发时先想好属于哪一层,不要混放

第3章:工程师协作与代码契约

这章讲的是"人"的问题。Tom Long 直说:大部分软件项目的失败不是因为技术问题,而是因为沟通问题。

"代码契约"是这章的核心概念。每个公开的 API、每个函数签名、每个数据结构,都是你和同事之间的契约。契约写清楚了,协作就顺畅;契约模糊,bug 就会像蟑螂一样冒出来。

核心观点:好的代码契约能减少80%的沟通成本,差的契约能让一个团队陷入无休止的扯皮。

书中举了一个实际例子:一个函数叫 processData(),参数是 (data),返回值是 Result。这种契约等于没写——什么数据?怎么处理?Result 里有什么?改成 validateAndTransformUserInput(rawInput: string): ValidationResult 就清晰多了。

| 关键概念 | 说明 | |----------|------| | 代码契约 | API 设计就是团队间的协议 | | 命名即文档 | 好名字比注释更有用 | | 接口设计原则 | 让错误用法难以编译通过 | | 向后兼容 | 改公开 API 要慎重,破坏兼容性代价巨大 |

行动清单:

  • [ ] 检查你最近写的函数名和参数名,能不能在不看注释的情况下理解用途
  • [ ] 给每个公开 API 写一个使用示例(不是注释,是测试代码)
  • [ ] 在修改公共接口前,先搜索一遍整个代码库的调用方

第4章:错误处理

错误处理是区分新手和老手最明显的分水岭。新手写代码只考虑 happy path,老手写代码会想"如果这里出错了怎么办"。

Tom Long 提出了一个很有用的分类法:错误分为可恢复错误和不可恢复错误。可恢复错误(比如网络超时)应该让调用者决定怎么处理;不可恢复错误(比如空指针)应该尽早暴露,fail fast。

核心观点:错误处理的黄金法则是:让错误难以被忽略,让恢复变得容易。

书中对比了"返回错误码"和"抛出异常"两种策略。返回错误码的好处是显式,坏处是调用者容易忘记检查。异常的好处是不会被默默吞掉,坏处是如果滥用会导致控制流混乱。Tom Long 的建议是:在性能关键路径上用错误码,在其他地方用异常。

| 关键概念 | 说明 | |----------|------| | Fail Fast | 出错时立即暴露,不要带着错误状态继续运行 | | 可恢复 vs 不可恢复 | 不同类型的错误需要不同的处理策略 | | 错误码 vs 异常 | 各有适用场景,没有银弹 | | 错误传播 | 不要吞掉错误,让需要处理的人能看到它 |

行动清单:

  • [ ] 检查项目中有没有空 catch 块(吞掉错误的地方)
  • [ ] 给每个函数的返回值加上类型检查,别相信"调用者会正确使用"
  • [ ] 对外部输入做防御性校验,对内部代码信任但验证

第5章:让代码可读

这章是最实用的一章。Tom Long 直接给了大量"坏代码 → 好代码"的对比示例,看一眼就知道差别在哪。

可读性的核心原则只有一条:代码应该像好的散文一样,从上到下读下去不需要回跳。如果你写了一个函数,读者需要来回跳转才能理解逻辑顺序,那就是有问题。

核心观点:代码被阅读的次数远多于被编写的次数,所以可读性是最值得投入的质量属性。

几个具体建议:变量名要说清楚它是什么,不要用 tmpdataresult 这种万能名;布尔变量用 isXxxhasXxx 的格式;函数不要太长,超过20行就该考虑拆分;控制嵌套深度,超过3层就该用 early return 或提取函数。

| 关键概念 | 说明 | |----------|------| | 命名规范 | 名字应该揭示意图,不揭示实现 | | 控制流简化 | 减少嵌套,用 early return | | 函数长度 | 一个函数做一件事,长度控制在20行以内 | | 注释策略 | 注释解释"为什么",不解释"是什么" |

行动清单:

  • [ ] 找出项目中最长的3个函数,尝试拆分它们
  • [ ] 全局搜索 tmpdataresultobj 等模糊变量名,逐一改名
  • [ ] 把超过3层嵌套的 if-else 改成 early return 或策略模式

第6章:避免重复

DRY(Don't Repeat Yourself)是编程界最著名的原则之一,但 Tom Long 的态度很有意思——他不认为 DRY 是教条。有时候,重复两段逻辑比强行抽象一个"通用方案"更清晰。

关键区别在于:重复的是知识还是代码? 如果同一段业务规则出现在三个地方,那就是知识重复,改一个漏一个,早晚出bug。但如果两段代码恰好长得像,各自处理不同的业务场景,那强行合并反而会制造耦合。

核心观点:DRY 的目标是让每个知识点只有一个来源,而不是让每行代码只出现一次。

书中还讨论了"合理重复"的场景:比如不同模块对同一个外部服务的调用封装,如果强行统一接口,可能导致某个模块为了适配通用接口而做了不必要的转换。

| 关键概念 | 说明 | |----------|------| | DRY 原则 | 每个知识点只存在一处 | | 知识重复 vs 代码重复 | 前者有害,后者有时可接受 | | 抽象的时机 | 第三次出现时再抽象,不要第一次就 | | 过度抽象 | 比重复更可怕的错误 |

行动清单:

  • [ ] 搜索项目中重复的业务逻辑(不是语法重复),提取到共享模块
  • [ ] 检查现有的"通用工具类",有没有为了通用而过度复杂的情况
  • [ ] 建立"三次原则":同样的代码出现三次再抽象

第7章:小心依赖

依赖管理是中高级工程师的必修课。Tom Long 把依赖分为三类:稳定的、不稳定的、你控制不了的。不同类型的依赖需要不同的处理策略。

核心观点:依赖越少越好,但完全不依赖是不可能的。关键是控制依赖的方向和数量。

书中有一个很实用的"依赖倒置原则"讲解:高层模块不应该依赖低层模块,两者都应该依赖抽象。具体来说,你的业务逻辑不应该直接依赖 MySQL 驱动或 Redis 客户端,而是依赖一个 Storage 接口。这样换数据库时只需要实现一个新的 adapter,业务代码不用改。

| 关键概念 | 说明 | |----------|------| | 依赖倒置 | 依赖抽象而非具体实现 | | 依赖分类 | 稳定/不稳定/不可控,区别对待 | | 接口隔离 | 不要让使用者依赖它不需要的东西 | | 循环依赖 | 两个模块互相依赖,是架构恶化的信号 |

行动清单:

  • [ ] 用工具(如 Madge)检查项目中是否存在循环依赖
  • [ ] 审查 import 语句,确保没有业务代码直接导入基础设施细节
  • [ ] 对第三方依赖做定期审查,升级有安全漏洞的版本

第8章:做出实用的架构决策

这章不是讲那些高大上的架构模式,而是讲工程师每天都会面对的决策:这个功能该放哪个模块?要不要引入新的库?怎么拆分服务?

Tom Long 的建议非常接地气:好的架构决策是能在当前约束条件下解决问题的决策,不是能在所有场景下完美的决策。 过度设计比没有设计更有害。

核心观点:架构不是一锤子买卖,而是持续的权衡和调整。最好的架构决策是"现在能用、将来好改"的决策。

书中提到了一个"决策记录"(ADR, Architecture Decision Record)的实践:每次做重要的技术决策时,写下你为什么这么做、考虑了哪些替代方案、放弃了什么。这不是为了文档而文档,而是为了以后回溯时能理解当初的思考过程。

| 关键概念 | 说明 | |----------|------| | 实用主义架构 | 解决当前问题,留好扩展空间 | | ADR(架构决策记录) | 记录为什么这样决策,不只是记录决策结果 | | 渐进式设计 | 不要一步到位,让架构随需求演进 | | 权衡意识 | 每个决策都有代价,要清楚你付出了什么 |

行动清单:

  • [ ] 给项目建一个 docs/adr/ 目录,记录重要的技术决策
  • [ ] 做技术选型时至少对比3个方案,写下放弃其他方案的理由
  • [ ] 每季度回顾一次架构决策,看看当初的假设是否还成立

第9章:测试策略

测试是这本书后半段的重头戏。Tom Long 的观点很明确:测试的价值不在于证明代码能工作,而在于给你改代码的勇气。 没有测试的代码库,每次改动都是在赌。

书中把测试分成了几个层次:单元测试、集成测试、端到端测试。每个层次解决不同的问题,有不同的成本。单元测试最快最便宜,但覆盖范围有限;端到端测试覆盖最全,但慢且脆弱。

核心观点:好的测试是快速、可靠、独立的。如果一个测试有时通过有时失败(flaky test),它比没有测试更糟。

一个经常被忽略的点是测试的可读性。测试代码也是代码,也应该像生产代码一样被认真对待。一个看不懂的测试,在它失败时你也不知道是该修代码还是修测试。

| 关键概念 | 说明 | |----------|------| | 测试金字塔 | 大量单元测试 + 适量集成测试 + 少量E2E测试 | | 测试三属性 | 快速、可靠、独立 | | Flaky Test | 不稳定的测试比没测试更糟糕 | | 测试即文档 | 好的测试用例就是最好的使用说明 |

行动清单:

  • [ ] 给核心业务逻辑补上单元测试,覆盖率目标70%以上
  • [ ] 在 CI 中加入测试步骤,每次提交自动运行
  • [ ] 每周清理一次 flaky test,别让它们污染你的信心

第10章:安全编写代码

最后一章讲安全,Tom Long 的立场很坚定:安全是每个开发者的责任,不是安全团队独占的领域。 你不需要成为安全专家,但你需要有基本的安全意识。

书中覆盖了常见的攻击向量:SQL 注入、XSS、CSRF、不安全的反序列化等。但更重要的不是具体的漏洞列表,而是建立"不信任任何输入"的思维方式。

核心观点:安全编程的核心原则是:永远不要信任外部输入,包括来自你自己的前端。

| 关键概念 | 说明 | |----------|------| | 不信任输入 | 所有外部数据都是潜在攻击向量 | | 最小权限 | 代码只请求它需要的权限,不多要 | | 安全默认值 | 框架和库的安全配置应该是默认开启的 | | 纵深防御 | 一层安全措施不够,要多层防护 |

行动清单:

  • [ ] 审查项目中的用户输入处理,确保做了适当的校验和转义
  • [ ] 依赖检查:运行 npm audit 或等效工具,修复已知漏洞
  • [ ] 给敏感操作(删除、修改权限等)加审计日志

三、关键概念速查

| 概念 | 定义 | 一句话理解 | |------|------|-----------| | 代码质量 | 代码的可读性、可靠性、可维护性的综合衡量 | 能跑 ≠ 能用,好用 ≠ 好维护 | | 抽象层次 | 将系统按职责分层,每层只关注自己的逻辑 | 洋葱模型,核心在内,基础设施在外 | | 代码契约 | 公开 API 和数据结构的明确约定 | 接口就是团队间的法律合同 | | Fail Fast | 出错时立即暴露,不让错误状态传播 | 别把垃圾往下游扔 | | DRY 原则 | 每个知识点在系统中只存在一处 | 改一处就够了,不用满世界找 | | 依赖倒置 | 高层模块和低层模块都依赖抽象 | 别让业务逻辑绑死在 MySQL 上 | | 测试金字塔 | 单元测试多、集成测试中、E2E测试少 | 好的测试策略是分层投入 | | ADR | 架构决策记录,记录决策的背景和理由 | 以后回来看才知道当初为什么这么做 | | 代码债 | 为了短期速度牺牲质量带来的长期维护成本 | 技术债务的利息是复利 | | 泄漏抽象 | 抽象层没有完全隐藏底层实现细节 | 封装了个寂寞 |


四、核心框架/模型

1. 代码质量三维模型

            可维护性
               ↑
               |    ★ 好代码
               |   /|
               |  / |
               | /  |
          可靠性/   |
               |   /
               |  /
               | /
               +——————→ 可读性

  低分代码:能跑但看不懂、改不动
  高分代码:看得懂、改得动、跑得稳

2. 抽象层次(洋葱模型)

┌─────────────────────────────────┐
│         运行时环境               │  ← 操作系统、网络、文件系统
│  ┌───────────────────────────┐  │
│  │       框架/库             │  │  ← Express、React、ORM
│  │  ┌─────────────────────┐  │  │
│  │  │    基础设施         │  │  │  ← 数据库、缓存、消息队列
│  │  │  ┌───────────────┐  │  │  │
│  │  │  │  核心业务逻辑  │  │  │  │  ← 你的代码真正该待的地方
│  │  │  └───────────────┘  │  │  │
│  │  └─────────────────────┘  │  │
│  └───────────────────────────┘  │
└─────────────────────────────────┘

规则:内层不依赖外层,外层为内层提供服务

3. 测试金字塔

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

4. 错误处理决策树

出错了
  ├─ 能恢复吗?
  │   ├─ 是 → 让调用者决定怎么恢复(返回 Result/抛特定异常)
  │   └─ 否 → Fail Fast,立即暴露,不要继续
  ├─ 是内部错误还是外部输入问题?
  │   ├─ 内部 → 用 assert/panic,这不该发生
  │   └─ 外部 → 防御性校验,返回明确错误信息
  └─ 性能关键路径吗?
      ├─ 是 → 用错误码,避免异常开销
      └─ 否 → 用异常,更难被忽略

五、金句摘录

"代码被阅读的次数远远多于被编写的次数。如果你只花10%的精力在可读性上,你就浪费了90%的阅读者的时间。"

"抽象的目的是减少认知负担,而不是展示你的设计能力。如果一个抽象需要看5分钟才能理解,那它就是一个坏抽象。"

"最危险的代码不是有 bug 的代码,而是看起来能工作但实际上隐藏着错误假设的代码。"

"好的测试给你改代码的勇气。没有测试,每次改动都是在祈祷。"

"安全不是安全团队的事,就像质量不是测试团队的事。每个在键盘上敲代码的人,都是第一道防线。"

"过度设计的架构比没有架构更有害。因为它不仅浪费了设计的时间,还让简单的改动变得复杂。"


六、行动清单

每天

  • [ ] 写代码前花2分钟想一下:这段代码三个月后还能看懂吗?
  • [ ] 提交前自审:有没有空 catch、有没有模糊命名、有没有超过3层嵌套
  • [ ] 遇到重复代码时判断:这是知识重复(要提取)还是代码巧合(可接受)

每周

  • [ ] 做一次 code review,不只看逻辑正确性,也看可读性和设计合理性
  • [ ] 跑一次依赖安全扫描(npm audit / pip audit),修复高危漏洞
  • [ ] 清理 flaky test,不让不稳定的测试污染团队的信心

每月

  • [ ] 回顾一次项目的架构决策(ADR),看看当初的假设是否还成立
  • [ ] 检查一次代码库中的循环依赖和跨层调用,记录需要修复的技术债
  • [ ] 评估一次测试覆盖率,给核心模块补上缺失的单元测试

七、一句话总结

写代码不难,难的是写让人(包括三个月后的自己)看得懂、敢修改、不会出问题的代码——这本书就是教你做到这件事的实操手册。


八、读者热议

1. "比《代码整洁之道》更务实的一本"

豆瓣读者 @Henry(2023-01-10)认为这本书比 Bob 大叔的《代码整洁之道》更贴近实际工作场景。Bob 大叔的书有点"布道"的味道,告诉你应该追求什么;Tom Long 的书更像是一本操作手册,告诉你具体怎么做。每章都有坏代码→好代码的对比,看完就知道该怎么改。

认同度:⭐⭐⭐⭐⭐ —— 非常认同。Bob 大叔的书理念很好但实操性偏弱,这本书恰好补上了这个缺口。两本配合着读效果最好。

2. "适合有1-3年经验的开发者,老手可能觉得浅"

读者反馈这本书的定位很明确:写给已经能写代码但还没形成系统方法论的人。如果你已经做了5年以上,书中的大部分内容你可能在实践中已经摸索出来了。但对于刚入行或经验不多的开发者,这本书能帮你少走很多弯路,避免"不知道自己不知道"的阶段。

认同度:⭐⭐⭐⭐ —— 基本认同。经验丰富的工程师看这本书更多是"确认自己做得对",但偶尔也会发现一些以前没注意到的盲点。对于 junior 到 mid-level 的跃升,这本书的价值是实打实的。

3. "抽象和依赖那两章值回票价"

有读者特别推荐第2章(抽象层次)和第7章(小心依赖),认为这两章把很多架构书用100页才讲清楚的事情用20页讲明白了。洋葱模型的解释非常直观,依赖倒置原则的讲解也比 GoF 的经典描述更容易理解。如果你时间有限,优先读这两章。

认同度:⭐⭐⭐⭐⭐ —— 完全认同。这两章确实是全书含金量最高的部分。很多开发者知道"要分层"但不知道怎么分、分到什么程度,这两章给出了清晰的指导。

4. "中规中矩,没有太多惊喜"

也有读者觉得这本书比较"四平八稳",没有特别让人眼前一亮的新观点。书中的很多原则在《代码大全》《重构》《Clean Code》中都已经出现过。优点是整合得不错,缺点是缺乏原创性。

认同度:⭐⭐⭐ —— 部分认同。确实,这本书的理论基础大部分来自经典著作,原创性不算强。但它的价值在于整合和更新——用现代编程语言(C++、Java、Python)的示例重新讲解,加入了微服务和云时代的考量。对于没读过经典的老书的人来说,这本是更好的入门选择。


笔记生成:2026-04-28 by 喵喵 🐈


相关笔记

同作者仅有此篇 同主题: