案例2:遗留代码重构
场景:接手一个 5 年历史的 Node.js 服务,代码混乱,需要把 Callback 风格迁移到 async/await,同时不破坏现有行为。
挑战:看不懂的代码、没有测试、不敢乱改
第一步:理解现状(计划模式)
先进入只读计划模式,不让 Claude 改任何东西:
bash
claude --permission-mode plan你:
这是一个遗留的 Node.js 服务。我完全不了解它。
请帮我:
1. 从 package.json 和入口文件开始,理解整体架构
2. 列出所有主要模块和它们的职责
3. 识别技术债:回调嵌套、不安全做法、过时依赖等
4. 给出重构优先级建议
先只读不要改任何东西。Claude 输出(摘要):
## 架构分析
这是一个 Express API 服务,主要模块:
- routes/ API 路由(8 个文件,共 2000+ 行)
- services/ 业务逻辑(深度嵌套 callback)
- models/ 数据库操作(直接拼接 SQL,存在注入风险!)
- utils/ 工具函数(部分已废弃)
## 发现的问题
严重:
- models/ 中多处直接拼接用户输入到 SQL(XSS/注入风险)
- 密码存储用的是 MD5,没有加盐
中等:
- services/ 全部用回调风格,最深嵌套达到 7 层
- 错误处理不一致,部分地方 catch 了但没有正确传播
轻微:
- 大量 console.log 作为日志
- 部分依赖版本太旧(3 年未更新)
## 重构建议(按优先级)
1. 先修 SQL 注入(安全问题,立刻修)
2. 写集成测试保护现有行为
3. 再迁移 async/await(有测试保护才安全)
4. 最后更新依赖和日志系统关键技巧:先分析再动手
对于陌生代码库,花时间让 Claude 分析比直接让它改更安全。这个分析过程往往会发现你不知道的严重问题(比如 SQL 注入)。
第二步:先写测试,保护现有行为
你:
在重构任何代码之前,先给关键 API 写集成测试。
用 Jest + Supertest,覆盖:
- 所有 GET 端点的正常路径
- POST/PUT 的参数验证
- 认证中间件
目标:测试作为重构的安全网,重构后所有测试必须仍然通过。
先写测试,不要改任何业务代码。第三步:修 SQL 注入
先修最严重的安全问题:models/ 目录里的 SQL 注入。
对每个文件:
1. 找出所有直接拼接用户输入的 SQL
2. 改用参数化查询(使用已有的数据库客户端的 ? 占位符语法)
3. 改完后跑测试验证行为没变
每次只改一个文件,改完告诉我,等我验证再继续。第四步:分批迁移 async/await
不要一次性全改,分批进行,每批都跑测试:
你:
开始迁移 services/ 目录的 callback → async/await。
策略:
- 每次只迁移一个 service 文件
- 迁移完立刻跑相关测试
- 测试通过才继续下一个
- 保持对外接口不变(只改内部实现)
先从 userService.js 开始,这个是其他 service 的依赖。中途如果发现问题:
userService.js 迁移后,有一个测试失败了:
✗ should handle concurrent updates
看一下测试内容和迁移后的代码,找出原因。
可能是 Promise 并发处理方式不对。第五步:验证和收尾
你:
所有文件都迁移完了。
帮我做最终验证:
1. 跑完整测试套件
2. 检查是否有遗漏的 callback 模式(用 Grep 搜索)
3. 检查错误处理是否一致
然后写一个详细的 commit 信息,说明:
- 做了什么改动
- 为什么这样做
- 破坏性变更(如有)关键经验
| 经验 | 具体做法 |
|---|---|
| 先读后改 | 用计划模式彻底理解代码再动手 |
| 测试保护 | 有测试才敢重构,测试是安全网 |
| 小步迭代 | 每次只改一个文件,改完测试 |
| 安全优先 | 发现安全漏洞立刻修,别等到「后面」 |
可复用的重构提示词:
这是一个遗留代码库,我需要重构 [具体部分]。
重构目标:[具体目标,如 callback → async/await]
规则:
1. 重构前先为现有行为写测试
2. 每次只改一个文件
3. 每次改完跑测试,通过后才继续
4. 保持对外接口不变
5. 如果发现安全问题,立刻告诉我
先从分析开始,给我重构计划,不要急着动代码。