Development
弃用与迁移
管理弃用和迁移。适用于移除旧系统、API 或功能,或将用户从一个实现迁移到另一个实现时。
代码是负债,而非资产。每一行代码都有持续的维护成本。
每一行代码都有持续成本:测试、文档、安全补丁、依赖更新和心智负担。当同样的功能可以用更少的代码实现时——旧代码就应该被移除。
当用户足够多时,每一个可观察到的行为都会被依赖——包括 Bug、时序特性和未记录的副作用。弃用需要主动迁移,而不仅仅是发布公告。
构建新功能时,问自己:"三年后我们如何移除它?"
在弃用任何东西之前,回答这些问题:
1. 这个系统是否仍然提供独特的价值?
→ 如果是,维护它。如果否,继续。
2. 有多少用户/消费者依赖它?
→ 量化迁移范围。
3. 是否有替代方案?
→ 如果没有,先构建替代方案。不要在没有替代方案的情况下弃用。
4. 每个消费者的迁移成本是多少?
→ 如果可以自动完成,就自动完成。如果是手动且成本高,与维护成本权衡。
5. 不弃用的持续维护成本是什么?
→ 安全风险、工程师时间、复杂性的机会成本。
| 类型 | 使用场景 | 机制 | |------|----------|------| | 建议弃用 | 迁移可选,旧系统稳定 | 警告、文档、提示 | | 强制弃用 | 旧系统有安全问题、阻碍进展 | 设定硬性截止日期并提供迁移工具 |
默认使用建议弃用。 仅在维护成本或风险证明强制迁移合理时才使用强制弃用。
不要在没有可用替代方案的情况下弃用。替代方案必须:
## 弃用通知:OldService
**状态:** 自 2025-03-01 起弃用
**替代方案:** NewService(参见下方迁移指南)
**移除日期:** 建议性——暂无硬性截止日期
**原因:** OldService 需要手动扩展且缺乏可观测性。
### 迁移指南
1. 将 `import { client } from 'old-service'` 替换为 `import { client } from 'new-service'`
2. 更新配置(参见下方示例)
3. 运行迁移验证脚本:`npx migrate-check`
逐个迁移消费者,而非一次性全部迁移:
1. 识别与弃用系统的所有接触点
2. 更新为使用替代方案
3. 验证行为一致(测试、集成检查)
4. 移除对旧系统的引用
5. 确认无回归
只有在所有消费者都迁移后:
1. 验证零活跃使用(指标、日志、依赖分析)
2. 移除代码
3. 移除相关测试、文档和配置
4. 移除弃用通知
5. 庆祝——移除代码是一项成就
新旧系统并行运行,逐步将流量从旧系统路由到新系统。
阶段 1:新系统处理 0%,旧系统处理 100%
阶段 2:新系统处理 10%(金丝雀发布)
阶段 3:新系统处理 50%
阶段 4:新系统处理 100%,旧系统空闲
阶段 5:移除旧系统
创建一个适配器,将旧接口的调用转换为新实现。
class LegacyTaskService implements OldTaskAPI {
constructor(private newService: NewTaskService) {}
getTask(id: number): OldTask {
const task = this.newService.findById(String(id));
return this.toOldFormat(task);
}
}
使用功能标志将消费者从旧系统切换到新系统:
function getTaskService(userId: string): TaskService {
if (featureFlags.isEnabled('new-task-service', { userId })) {
return new NewTaskService();
}
return new LegacyTaskService();
}
僵尸代码是无人拥有但人人依赖的代码。特征:
应对措施: 要么指定负责人并妥善维护,要么制定具体的迁移计划后弃用。
完成弃用后: