什么是合适的重构流程

重构是软件开发中不可或缺的环节,但错误的重构方式会让代码库陷入混乱。本文档介绍一种渐进式、可控的重构策略,特别适用于大文件拆分等复杂场景。

核心原则

渐进式重构,而非一次性搬迁。

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

大文件拆分的标准流程

第一步:创建专属分支

在开始任何重构工作之前,必须创建一个独立的 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. 不更新引用,让原文件保持混乱
3. 继续复制粘贴,创建十几个新文件
4. 最后才想起来要统一修改所有引用
5. 发现引用关系错综复杂,根本无法理清
6. 陷入"修复引用 → 引入新bug → 再修复"的死循环

为什么这种方式有问题?

  • 工作量爆炸:最后需要一次性修改几十处引用,容易遗漏
  • 调试困难:如果出现问题,无法定位是哪一步引入的
  • 心理压力:面对一团乱麻的代码,容易产生挫败感
  • 代码审查困难:一次性改动太大,reviewer 无法有效审查

✅ 正确重构的关键特征

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

重构后的收尾工作

  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, 《重构:改善既有代码的设计》