目录 [−]
「Any fool can write code that a computer can understand. Good programmers write code that humans can understand.」
任何傻瓜都能写出计算机能看懂的代码。好程序员写的是人能看懂的代码。
—— Martin Fowler
第 22 章解决了「人怎么读懂 AI 写的代码」,用 UML 把代码画成图。
读懂之后呢?你打开 AI 生成的代码,能跑,但是一团乱麻:一个方法三百行,一个类管了八件事,同样的逻辑复制了五遍。这时候怎么办?
重构。
这件事本身不新鲜,Martin Fowler 1999 年就把它写成了一本书。变的是执行者。以前是人对着那本书一处一处手动改,现在是 AI 对着同一本书的目录自动改,人退到后面审查 diff。

本章分两半。前半讲重构的理论根基:什么是重构、技术债是怎么回事、什么时候该重构、Fowler 编了哪些坏味道和手法。这部分主要参考 Fowler 的《重构》,以及把这本书做成在线可检索版本的 refactoring.guru。后半讲 goal workflow 套件里的两个 Skill:/refactor(介绍页:https://goal.rpcx.io/index_cn.html#step-refactor )把 Fowler 第 2 版的整个目录封进了一个 AI Agent 能直接调用的能力里;/smell(介绍页:https://goal.rpcx.io/index_cn.html#step-smell )则在它前面一步,负责扫出整个代码库到底哪里有问题。一个诊断,一个治疗。
23.1 什么是重构,什么是技术债
重构(refactoring)是在不改变外部行为的前提下改善代码内部结构。说白了,把一团乱麻(dirty code)整理成清爽的代码(clean code),但程序对外的表现一字不变。
- 脏代码是经验不足,再加上 deadline、管理混乱、开发途中走捷径,几样凑一起的产物。
- 整洁代码易读、易懂、易维护,它让开发变得可预测。你大概知道改一处要花多久,而不是每次都掉进未知的坑。
代码为什么会从整洁滑向肮脏?Ward Cunningham 的技术债这个说法解释得很到位。走捷径、跳过测试,就像找银行贷款:眼下买东西是快了,但要付利息。你不光还本金,还得还利息;利息攒够了,甚至会超过你的全部收入,永远还不清。代码也一样。不写测试能暂时提速,可它每天都在拖你后腿,直到哪天你补上测试,把债还掉。

refactoring.guru 列了一串技术债的成因。这些在 AI 时代值得重读,因为大多被放大了:
- 业务压力:功能没做完就得上线,于是补丁和权宜之计堆上去,盖住没收尾的部分。
- 看不到后果:管理层不知道技术债有「利息」,债越欠越多、开发越来越慢,于是舍不得给重构留时间。
- 强耦合没人管:项目变成一块铁板而不是一堆独立模块,动一处牵全身。
- 缺测试:没有即时反馈,人就敢用又快又险的 workaround。最坏的情况是没测试直接上线,可能给几千个客户发出一封诡异的测试邮件,也可能直接把数据库清空。
- 缺文档、缺沟通:新人上手慢;关键的人一走,开发就停摆。
- 长期多分支并行:隔离的改动越多,合并时积下的债越大。
- 重构一拖再拖:需求一直变,旧代码总有显得笨重的一天;可程序员每天还在往旧代码上接新代码,所以拖得越久,将来要返工的依赖就越多。
「缺测试」和「重构一拖再拖」这两条在 AI 时代尤其要命。Agent 几分钟就能吐出几千行代码,没有特征测试兜底、没有及时整理,技术债的本金和利息会以人类时代见不到的速度滚起来。第 12 章讲 GSD Core 时提过的「反合理化表」,对抗的正是「以后再重构」「先跳过测试」这类自欺。它其实就是技术债成因的制度化解药。
23.2 何时重构:三次法则
Fowler 的经验法则很简洁——三次法则(Rule of Three):
- 第一次做某件事,直接做完。
- 第二次做类似的事,心里别扭也照旧重复一遍。
- 第三次再碰到同类的事,开始重构。
第一次重复忍着,第三次出现就动手。除此之外还有三个天然时机:
- 加功能时:重构能帮你读懂别人的代码。面对一坨脏代码,先重构再加,整洁的代码好改得多。这一改不只方便你,也方便后面接手的人。
- 修 bug 时:bug 跟现实里的虫子一样,专挑代码最暗最脏的角落待着。把代码清干净,错误几乎会自己冒出来。
- 代码评审时:这往往是代码公开前最后一次整理的机会。最好跟作者结对来做,简单问题当场修掉,复杂的估个时间再说。
23.3 如何重构:正确的姿势
重构是一连串小改动,每一步让代码好一点,同时让程序始终能跑。refactoring.guru 的「做对了」清单只有三条,却是整套方法论的核心:
- 代码必须变干净。 重构完还是一团乱,那这一小时就白花了。这种事多半发生在你丢掉「小步改」、把一大堆重构混进一次大改动的时候。尤其赶 deadline 的时候,很容易把自己绕晕。
- 重构期间不写新功能。 别把重构和加特性搅在一起,至少在单个 commit 里把两件事分开。
- 重构后所有已有测试都得过。 测试挂了只有两种可能:要么你改错了,修就是;要么你的测试太底层了,比如去测了类的私有方法,这种情况是测试的锅,该把它重写成更高层、BDD 风格的测试。
记住这三条。后半 /refactor Skill 的安全协议,基本就是把它们逐条机械化了一遍。
23.4 Fowler 的目录:坏味道与手法
refactoring.guru 把 Fowler 书里的内容编成了两张能检索的目录:
- **代码坏味道(Code Smells)**是问题的征兆,好发现,但它可能只是更深层问题露出的一角。
- **重构手法(Refactoring Techniques)**是具体的改法。每种手法都有得有失,所以每次重构都该有明确的动机,谨慎施用。
手法分六大类:组合方法(Composing Methods)、在对象间搬移特性(Moving Features Between Objects)、组织数据(Organizing Data)、简化条件表达式(Simplifying Conditional Expressions)、简化方法调用(Simplifying Method Calls)、处理泛化关系(Dealing with Generalization)。
整个流程是「症状到处方」:你先嗅到味道,比如「过长方法」或者「Feature Envy」,目录再告诉你该用哪几种手法去治。这套映射关系明确、能机械执行,也正是 AI Agent 接得住的地方。
23.5 /refactor:把 Fowler 的书变成一个 Skill
理论讲完,看看 goal workflow 套件里的 /refactor 是怎么把这本书落成一个能调用的能力的。
/refactor 基于 Fowler《重构》第 2 版的完整目录:识别坏味道,套用验证过的手法,在不动外部行为的前提下把代码改清爽。它在 goal workflow 主闭环(第 8 章)之外,是个 Bonus 技能。安装一行就够:
1 | npx skills add smallnest/goal-workflow --skill refactor |
触发可以直接打 /refactor,也可以用自然语言或术语:
1 | 重构:重构 UserManager 类,它太大了 |
23.5.1 22 种坏味道(五大类别)
Skill 内置 22 种代码坏味道,按五大类组织,每种都关联到对应的重构手法:
| 类别 | 坏味道 | 主要重构手法 |
|---|---|---|
| 臃肿类 | 过长方法 | Extract Method, Replace Temp with Query |
| 过大的类 | Extract Class, Extract Subclass | |
| 基本类型偏执 | Replace Data Value with Object | |
| 过长参数列表 | Introduce Parameter Object | |
| 数据泥团 | Extract Class | |
| OO 滥用类 | Switch 语句 | Replace Conditional with Polymorphism |
| 临时字段 | Extract Class, Introduce Null Object | |
| 被拒绝的遗赠 | Replace Inheritance with Delegation | |
| 异曲同工的类 | Rename Method, Extract Superclass | |
| 变更阻碍类 | 发散式变化 | Extract Class |
| 霰弹式修改 | Move Method, Move Field | |
| 平行继承体系 | Move Method, Move Field | |
| 冗余类 | 注释(代码本应自说明) | Extract Method, Rename Variable |
| 重复代码 | Extract Method, Pull Up Method | |
| 冗赘类 | Inline Class, Collapse Hierarchy | |
| 纯数据类 | Move Method, Encapsulate Field | |
| 死代码 | 删除(Git 历史有记录) | |
| 夸夸其谈未来性 | Inline Class, Remove Parameter | |
| 耦合类 | 依恋情节 | Move Method |
| 狎昵关系 | Move Method, Move Field | |
| 消息链 | Hide Delegate | |
| 中间人 | Remove Middle Man | |
| 不完美的库类 | Introduce Foreign Method |
23.5.2 40+ 种重构手法(六大类)

每种手法都附带机械步骤(mechanics)和 before/after 对比示例,这正是 refactoring.guru 那张目录的 AI 可执行版:
| 类别 | 手法数 | 代表性手法 |
|---|---|---|
| 组合方法 | 9 | Extract Method, Inline Method, Extract Variable, Replace Temp with Query, Substitute Algorithm |
| 移动特性 | 7 | Move Method, Move Field, Extract Class, Inline Class, Hide Delegate |
| 组织数据 | 13 | Replace Data Value with Object, Encapsulate Field, Replace Type Code with Subclasses, Replace Magic Number |
| 简化条件 | 8 | Decompose Conditional, Guard Clauses, Replace Conditional with Polymorphism, Introduce Null Object |
| 方法调用 | 13 | Rename Method, Separate Query from Modifier, Introduce Parameter Object, Replace Error Code with Exception |
| 泛化 | 9 | Pull Up Method, Push Down Method, Extract Interface, Form Template Method, Replace Inheritance with Delegation |
23.5.3 五阶段安全协议:把「做对了」清单制度化
/refactor 真正关键的设计不是目录,而是安全协议。它把 23.3 节那三条清单变成了 Agent 必须照办的执行护栏:
- 准备阶段:写特征测试(characterization test),提交当前状态,开一个重构分支。
- 每一步:一次只改一处,编译过,测试全过,提交。
- 验证阶段:测试全过,手动冒烟测一遍,自己审 diff,最后提交。
- 铁律:不动外部行为,不夹带功能改动,每一步都有测试兜底。
对着 Fowler 的三条清单看,正好一一对上:「代码必须变干净」对应自审 diff,「不写新功能」对应不夹带功能改动,「测试必须全过」对应每步测试加特征测试兜底。
AI 时代的价值就在这。人在 deadline 底下,难免偷偷给自己找台阶,「这块以后再重构」「这次先跳过测试」。Agent 被钉死在协议上,没这个空子可钻。重构的纪律,从「靠人自觉」变成了「靠机器强制」。
23.5.4 语言专属指南
Skill 还针对主流语言给出特化建议:
| 语言 | 核心建议 |
|---|---|
| Java | final 局部变量、IDE 自动重构、Records、Sealed Classes |
| TypeScript | 解构减少参数、const 优先、Union Types 替代类型码、?. 消除 null 检查 |
| Python | Type Hints、dataclasses、@property、Context Managers |
| Go | 小接口、命名返回值、表格驱动测试、early returns 消除嵌套 |
| Rust | Result/Option 替代错误码和 null、Pattern Matching、From trait、Derive macros |
23.5.5 在工作流中的位置
/refactor 不单干,它跟两个邻居配合,其中最重要的是下一节要单独讲的 /smell;另外 /modern-go(35+ 条 gofix 风格规则)跟它一样,都是用来保持代码库健康的工具。还有 /review-it——它的审查原则里写得很清楚:「Reject noise,拒绝不切实际的边界情况、投机性风险、过度重构」。这跟 /refactor 安全协议里「小修复优先」是一个意思——重构不是越多越好,闻到坏味道才动手。
23.6 /smell:先诊断,再开刀
/refactor 解决的是"知道哪里该改、怎么改"。但更常见的困境在前一步:面对一个 AI 攒出来的几万行代码库,你不知道该从哪下手。哪个模块烂得最厉害?是架构错了,还是只有某个函数太长?先动哪块?
/smell 回答的就是这个。它跟 /refactor 同级,分工是:/smell 诊断,扫出代码库的病灶并排优先级;/refactor 治疗,照 Fowler 手法一处处修。一个出报告,一个动手术。
安装:
1 | npx skills add smallnest/goal-workflow --skill smell |
触发同样可以直接打 /smell,或用自然语言:
1 | 代码坏味道检测:找出代码坏味道 |
23.6.1 比 Fowler 更宽的视野:8 大类 50+ 坏味道
这里要留意 /smell 和 /refactor 在覆盖面上的区别。/refactor 盯的是 Fowler 那 22 种坏味道,基本都在函数和类这个尺度。/smell 往上抬了一层,把架构和复杂度也算进来,一共 8 大类、50+ 种:
| 类别 | 示例 |
|---|---|
| 架构 | 大泥球、分布式单体、贫血模型、CQRS 滥用、层边界违反 |
| 耦合 | 循环依赖、内容耦合、公共耦合(全局状态)、印记耦合 |
| 内聚 | 上帝对象、霰弹式修改、依恋情结、数据泥团 |
| 设计 | 抽象泄露、静态粘连、服务定位器滥用、SOLID 违反 |
| 代码 | 重复代码、长方法、基本类型偏执、魔数、死代码 |
| 测试 | 零测试覆盖、测试-实现耦合、不稳定测试 |
| 命名 | 模糊命名、命名不一致 |
| 复杂度 | 嵌套循环 (O(n²))、N+1 查询、重复线性扫描、循环内排序、渲染重复计算 |
最后两类,「测试」和「复杂度」,是 Fowler 的坏味道目录里没有的,而它们在 AI 时代恰好用得上。AI 生成的代码经常零测试覆盖,也经常埋着 N+1 查询、循环内排序这类一眼看不出、上了量才爆的性能坑。这些 /smell 能一并扫出来。
23.6.2 输出:一份带优先级的重构路线图
/smell 不只是列问题,它产出一份结构化的 Markdown 报告:
- 执行摘要和整体健康评估
- 检测到的架构风格,对比它"应该是"的风格
- 按严重级别分类的发现:严重 / 警告 / 建议
- 依赖图分析和模块健康评分卡
- 8 大类坏味道的分布统计
- 一份重构路线图,分成「立即可做 / 短期 / 长期」
最有价值的是最后那份路线图。它直接回答了开头那个问题:该先动哪块。你可以让它只跑「仅严重」模式做快速体检,也可以把范围缩到某个模块,或者只扫最近改动的文件。
23.6.3 两个 Skill 怎么配合

把 /smell 和 /refactor 串起来,就是一条完整的"健康维护"链路:
/smell出报告:定位病灶,排好优先级,知道先改哪、后改哪。/refactor逐条施工:针对报告里的具体坏味道,套 Fowler 手法,走五阶段安全协议,每步提交。- 改完再跑一遍
/smell,看健康评分有没有真的涨上去。
这正好呼应了 23.3 节那条铁律,「代码必须变干净」。/smell 的健康评分让"变干净"这件事从主观感受变成了可量化的前后对比:重构前一个分,重构后一个分,涨了才算没白干。
23.7 小结
重构的方法论二十年没怎么变:嗅坏味道、对着手法目录、小步施工、每步测试、不夹带功能。AI 时代变的只有一件事,把 Fowler 那本书从「人读的参考书」变成了「Agent 执行的 Skill」,再用安全协议把人最容易偷的懒,改成机器必须遵守的护栏。goal workflow 把这件事拆成两个 Skill:/smell 诊断,扫出整个代码库的病灶并排好优先级;/refactor 治疗,照着 Fowler 手法一处处修。先体检,再开刀,改完再体检,这就是 AI 时代维护代码库健康的闭环。
第 22 章用 UML 让你读懂 AI 写的代码,这一章用重构让你改好 AI 写的代码。读懂是前提,改好是落点。两件事合起来,才是「你可以外包思考,但不能外包理解」这句话在工程上真正站住脚的样子。
