# 按这本 2004 年的书，FDE 接手的代码大多是遗留代码

> Michael Feathers 的《修改代码的艺术》教你给没有测试的客户系统加上 AI 功能，又不弄坏正在运行的东西。

原文: https://fdetimes.net/zh/books-courses/working-effectively-with-legacy-code-fde/

Michael Feathers 说，他最为人所知的是一个定义：遗留代码就是没有测试的代码。代码也许去年才写好，但只要还没有测试，它就是遗留代码。

按这个标准，前线部署工程师（FDE）在客户现场第一周打开的代码库，大多是遗留代码。而这份工作常常要求你给一个不是自己写的系统加上 AI 功能，比如用 LLM 汇总案卷或给工单分类。这个系统没人敢碰，客户也不允许它出问题。

所以，这本 2004 年出版的书值得每个想做 FDE 的人放在手边。书里只字未提 AI，却讲透了这份工作最难的部分：修改别人的代码，同时不破坏已经在运行的东西。

## 旧书能解决新问题吗？

*Working Effectively with Legacy Code* 由 Pearson 于 2004 年 9 月 22 日出版，属于 Robert C. Martin 系列（ISBN-13 978-0-13-117705-5）。出版社对这本书目标的概括是：帮助程序员应对接手的代码，而不必全部重写，因为重写代价极高。

这种思路与部署工作的现实相符。你很少会被邀请提议重写客户系统，更几乎不可能在几周内完成重写。你需要的是一种安全地逐步切入的方法。

有一个障碍最好提前知道。书中的示例用 Java、C++ 和 C# 编写，并默认读者能看懂 UML 表示法。如果你主要写 Python 或 TypeScript，就去读书里的思路，自己把示例换成熟悉的语言，而不是照抄代码。

## 观点一：缺少测试才是真正的风险

这本书的核心是写测试，确保修改不会意外改变程序行为。Feathers 说得很直白：没有测试，你就麻烦大了；有了测试，你就进入了“黄金地带”。

他最实用的建议是：不要试图用测试覆盖整个系统。在测试薄弱的代码里，只为即将修改的那部分写测试。对时间紧迫的 FDE 来说，这应当成为一条工作准则：测试跟着改动范围走，而不是跟着覆盖率指标走。

## 观点二：修改之前，先记录代码在做什么

假设客户有一个 Java 函数 `routeTicket()`，长达几百行，用一串基于关键词的 `if` 语句给支持工单分配优先级。客户希望用 LLM 替换这段优先级逻辑。第一步并不是调用模型。

第一步是拿一批真实工单跑一遍现有函数，然后写测试，断言它今天产生的确切输出，包括那些看起来像 bug 的结果。Feathers 把这类测试称为特征测试（characterization test），并说明它记录的是代码的当前行为，而不是正确行为。

Feathers 也认为这正是 AI 擅长的工作。他建议接受一点：有些测试是临时的，写出来只为理解代码或支撑某次修改，之后就可以扔掉。这样你就能让编码助手批量生成描述性测试，不用担心要永远维护它们。

**要点:** 给客户代码加 AI 之前，先记录这段代码现在做了什么。

## 观点三：找到接入 LLM 的接缝

书中最有价值的概念是接缝（seam）。Martin Fowler 引用过 Feathers 的定义：接缝是一个不必在该处修改代码，就能改变程序行为的地方。Feathers 把它比作衣服上的接缝，一个天然的断点，可以把一个组件换成另一个。

回到 `routeTicket()`。把优先级逻辑提取到 `PriorityClassifier` 接口中，通过构造函数传入。默认实现原样保留旧的 `if` 链，所有特征测试都保持通过。这就是你的接缝。

接下来，写第二个调用 LLM 的实现，接到同一个接缝上，按工单分组逐步启用。测试时用返回固定结果的 fake 替换它，测试套件就既不依赖模型，也不依赖网络。旧的路由逻辑一行都没改。

## 适合谁读，按什么顺序读

这本书适合有两到八年经验、曾经战战兢兢改过别人代码的开发者。先读 Martin Fowler bliki 上的 Legacy Seam 页面，理解这个概念；再细读书中关于给遗留代码写测试和关于接缝的章节。其余部分可以略读，遇到对应场景时再回来翻。

在 FDE 的岗位描述里，“与现有系统集成”“在客户代码库中工作”这类表述，说的正是这项能力。写简历时，可以讲一次你给没有测试的模块加新功能的经历：怎样写特征测试，接缝选在哪里，以及怎样做到什么都没坏。

模型每个季度都在变。客户现场那个没有测试、长达几百行的函数却会一直在那里，等着一个知道去哪里找接缝的人。

**本周动手试试:**

- 在你负责的代码库里挑一个没有测试的函数，写 5 个特征测试，记录它当前的确切输出，包括看起来像 bug 的输出
- 在这个函数里找一个可以通过构造函数或参数替换组件的地方，试着在这个位置接入一个模拟的 LLM 调用
- 打开这本书之前，先读 Martin Fowler bliki 上的 Legacy Seam 页面

## 信源

- [Working Effectively with Legacy Code | InformIT (Pearson)](https://www.informit.com/store/working-effectively-with-legacy-code-9780131177055)

- [Legacy Seam (Martin Fowler's bliki)](https://martinfowler.com/bliki/LegacySeam.html)

- [#195 - Working Effectively with Legacy Code and AI Coding Assistant - Michael Feathers](https://techleadjournal.dev/episodes/195/)
