什么是合适的重构流程

本文介绍渐进式重构流程,以大文件拆分为例说明如何分析依赖、迁移代码并验证每一步。

核心原则

分步迁移,逐步验证。

每次只移动一小部分代码,立即验证,然后继续。这种方法将风险降到最低,并确保每个步骤都是可控的。

大文件拆分的标准流程

第一步:创建专属分支

在开始任何重构工作之前,必须创建一个独立的 git 分支:

git checkout -b refactor/split-large-file

为什么需要独立分支?

  • 保护主分支的稳定性
  • 可以随时回退到重构前的状态
  • 便于代码审查和追踪变更
  • 如果重构失败,不影响主分支和其他开发工作

第二步:分析拆分目标

不要急于动手,先搞清楚”拆什么”:

  1. 阅读完整文件:理解文件的整体结构和职责
  2. 识别功能模块:找出逻辑上独立的代码块
  3. 绘制依赖关系:标注模块之间的引用关系
  4. 确定拆分粒度:每个新文件应该职责单一、内聚性高
原始大文件分析示例:

┌─────────────────────────────┐
│   utils-large-file.ts       │
├─────────────────────────────┤
│  • 字符串处理函数 (15个)     │  ← 可拆分为 string-utils.ts
│  • 日期处理函数 (8个)        │  ← 可拆分为 date-utils.ts
│  • 数组工具函数 (12个)       │  ← 可拆分为 array-utils.ts
│  • 格式化函数 (10个)         │  ← 可拆分为 formatters.ts
└─────────────────────────────┘

第三步:从最简单的模块开始

什么是最简单的模块?

  • 没有外部依赖或依赖最少的模块
  • 函数数量少、逻辑清晰的模块
  • 不涉及复杂类型定义的模块

从简单到复杂的好处:

  • 快速建立信心
  • 积累拆分经验
  • 早期发现潜在问题
  • 为后续复杂拆分建立模板

第四步:渐进式迁移

这是最关键的步骤,遵循”拆一个、改引用、删原代码”的循环:

循环执行以下步骤:

1. 将目标模块复制到新文件
   ↓
2. 更新原文件中对该模块的引用,指向新文件
   ↓
3. 从原文件中删除已迁移的代码
   ↓
4. 运行测试/构建,验证没有破坏功能
   ↓
5. 提交变更(可选,但推荐)
   ↓
6. 重复以上步骤,直到全部拆分完成

具体示例

假设我们要拆分 utils-large-file.ts:

# 1. 创建新文件 string-utils.ts,包含字符串处理函数
touch src/utils/string-utils.ts
// src/utils/string-utils.ts
export function capitalize(str: string): string {
  return str.charAt(0).toUpperCase() + str.slice(1)
}
 
export function camelCase(str: string): string {
  // ... 实现
}
// 原文件 utils-large-file.ts 中,更新引用
// 修改前:
// import { capitalize, camelCase } from './utils-large-file'
 
// 修改后:
import { capitalize, camelCase } from './string-utils'
// 然后从 utils-large-file.ts 中删除 capitalize 和 camelCase 的函数定义
# 验证没有破坏功能
npm run build
npm test

渐进式重构的关键步骤

特征说明
小步前进每次只迁移一个模块
即时验证每步都运行测试/构建
引用同步拆完立即修改引用
原代码清理迁移后删除原文件中的对应部分
分支保护始终在独立分支进行

重构后的收尾工作

  1. 运行完整的测试套件:确保没有破坏任何功能
  2. 检查代码风格:运行 linter 和 formatter
  3. 审查新文件结构:确保拆分后的文件组织合理
  4. 更新文档:如果文件结构变化影响文档,及时更新
  5. 提交并合并:
git add .
git commit -m "refactor: 拆分大文件为多个职责单一的模块"
git checkout main
git merge refactor/split-large-file

其他重构场景的通用策略

虽然本文档以大文件拆分为例,但同样的原则适用于:

  • 函数提取:将长函数拆分为多个小函数
  • 模块重组:重新组织目录结构
  • 接口优化:简化复杂的 API 或函数签名
  • 依赖解耦:减少模块间的耦合

核心思想不变:小步、验证、清理、循环。

总结

渐进式重构按分析目标、迁移模块、更新引用和验证结果的顺序推进;具体粒度和检查范围应与项目风险相匹配。

“重构的两条法则:

  1. 任何情况下,重构前必须有能运行的代码
  2. 每次只做一个小改动,然后测试” — Martin Fowler, 《重构:改善既有代码的设计》