>
每一次失败的迁移都有相同的讣告:我们试图一次性替换所有东西,进行到一半时发现时间不够,现在维护着两套系统。
你需要进行迁移。可能是 Express → Fastify。可能是 JavaScript → TypeScript。可能是 MySQL → PostgreSQL。可能是 React 类组件 → 钩子。可能是单体架构 → 微服务。
你知道当前状态。你知道目标状态。但你不清楚两者之间的安全路径——那种能让你逐步变更、每一步都能验证、出问题时可以回滚,同时保持生产环境正常运行的顺序。
迁移指南针会生成这条路径。
无论迁移什么内容,每一次迁移都遵循相同的基本结构:
状态A(当前)────── 过渡阶段 ────── 状态B(目标)
│
├── 并行运行区(两种状态共存)
├── 回滚点(可安全撤销的位置)
├── 验证关卡(证明每一步都成功)
└── 绞杀边界(旧接口 → 新接口)
法则一:绝不一次性大改
一次只改一件事。验证。继续或回滚。需要同时改变所有内容的迁移不是迁移——那是伪装成迁移的重写。
法则二:先并行再替换
新系统在替换旧系统之前必须与旧系统并行运行。在移除旧系统之前,你需要证明新系统在生产环境中能正常工作。
法则三:每一步都必须可部署
在迁移过程中,代码库绝不能处于无法部署到生产环境的状态。每一次提交都是一个有效的检查点。
示例:moment.js → date-fns
指南针路线:
├── 步骤1:审计
│ ├── 查找所有 moment 的导入(grep 分析)
│ ├── 记录你使用的所有 moment 函数
│ ├── 将每个 moment 函数映射到 date-fns 的等价函数
│ └── 识别没有 date-fns 等价函数的 moment 特性
│
├── 步骤2:并行安装
│ ├── npm install date-fns(与 moment 并存,不替换)
│ ├── 创建适配器模块:src/utils/date-adapter.ts
│ │ └── 导出你的日期操作,内部调用 moment 或 date-fns
│ └── ✅ 部署。两个库都已安装。仅使用 moment。
│
├── 步骤3:逐个迁移消费者
│ ├── 将导入从 moment 改为从 date-adapter 导入
│ ├── 不要改变行为——适配器内部调用 moment
│ ├── ✅ 每个文件后部署。回滚 = 还原一个文件。
│ └── 重复直到所有消费者都使用适配器
│
├── 步骤4:交换内部实现
│ ├── 在 date-adapter 内部,将实现从 moment 改为 date-fns
│ ├── 运行测试。比较输出。
│ ├── ✅ 部署。如有问题,仅还原适配器内部实现(一个文件)。
│ └── 监控边界情况(时区、区域设置、格式化)
│
├── 步骤5:清理
│ ├── 从 package.json 中移除 moment
│ ├── 可选:内联适配器(或保留以备将来扩展)
│ ├── ✅ 部署。
│ └── 总迁移:N 个小 PR,零停机,每一步都可完全回滚
│
└── 回滚点:每一步。最大回滚成本:还原 1 个文件。
示例:Express → Fastify
指南针路线:
├── 步骤1:审计
│ ├── 记录所有路由(数量、复杂度、中间件使用情况)
│ ├── 记录所有中间件(认证、日志、CORS 等)
│ ├── 识别 Express 特有的模式(req/res 增强等)
│ └── 将 Express 概念映射到 Fastify 等价概念
│
├── 步骤2:绞杀门面
│ ├── 在 Express 前引入反向代理(或路由分发器)
│ ├── 所有流量 → Express(行为无变化)
│ ├── ✅ 部署。验证无变化。
│ └── 该代理后续将在 Express 和 Fastify 之间分发流量
│
├── 步骤3:并行实例
│ ├── 在 Express 旁搭建 Fastify 实例
│ ├── 迁移一个低风险路由(健康检查、静态资源等)
│ ├── 路由代理:/health → Fastify,其他所有 → Express
│ ├── ✅ 部署。验证 Fastify 正确提供 /health 服务。
│ └── 回滚:将 /health 路由重新指向 Express
│
├── 步骤4:增量路由迁移
│ ├── 一次迁移一个路由(或小批量)
│ ├── 顺序:最低风险 → 最高风险
│ │ ├── 静态路由(无状态、无认证)
│ │ ├── 只读认证路由
│ │ ├── 写入路由(数据变更)
│ │ └── 复杂路由(多步骤、事务性)
│ ├── 对于每个路由:
│ │ ├── 在 Fastify 中实现
│ │ ├── 通过并行运行验证(同一请求 → 两个系统 → 比较)
│ │ ├── 将代理切换到 Fastify
│ │ ├── ✅ 部署。监控。
│ │ └── 回滚:将代理切换回 Express
│ └── 重复直到所有路由都在 Fastify 上
│
├── 步骤5:退役
│ ├── 从 package.json 中移除 Express
│ ├── 移除代理(Fastify 直接提供服务)
│ ├── ✅ 部署。
│ └── 清理所有兼容性垫片
│
└── 回滚点:每个路由。最大回滚:重新路由一个端点。
示例:JavaScript → TypeScript
指南针路线:
├── 步骤1:配置
│ ├── 添加 tsconfig.json,设置 strict: false(宽松起步)
│ ├── 启用 allowJs: true(JS 和 TS 共存)
│ ├── ✅ 部署。行为零变化。
│
├── 步骤2:重命名(先处理叶子节点)
│ ├── 依赖图:查找没有导入者的文件(叶子节点)
│ ├── 将 .js 重命名为 .ts(一次一个文件)
│ ├── 添加最小类型(编译需要的地方使用 any)
│ ├── ✅ 每批处理后部署。
│ └── 向内推进:叶子 → 分支 → 主干
│
├── 步骤3:收紧
│ ├── 用真实类型替换 any(一次一个模块)
│ ├── 逐步启用更严格的 tsconfig 规则:
│ │ ├── noImplicitAny
│ │ ├── strictNullChecks
│ │ ├── strictFunctionTypes
│ │ └── strict: true(最终)
│ ├── ✅ 每次规则变更后部署。
│ └── 回滚:禁用该规则,稍后修复
│
├── 步骤4:清理
│ ├── 当所有文件都是 .ts 时移除 allowJs
│ ├── 移除所有剩余的 @ts-ignore 注释
│ └── ✅ 部署。
│
└── 回滚点:重命名时每个文件。收紧时每个规则。
输入:从什么迁移?迁移到什么?当前使用情况如何?
阶段1:调研
├── 分析源系统的当前使用情况(使用了哪些特性、哪些模式)
├── 将源概念映射到目标等价概念
├── 识别差距(没有目标等价概念的源特性)
├── 估算每个组件的迁移工作量
└── 识别风险最高的组件(最复杂、最关键)
阶段2:路线规划
├── 确定迁移类型(库、框架、语言、数据库、架构)
├── 选择迁移策略:
│ ├── 绞杀藤模式:逐个路由替换(最适合服务)
│ ├── 抽象分支模式:适配器层交换(最适合库)
│ ├── 并行运行模式:两个系统同时运行(最适合数据存储)
│ └── 增量重写模式
该技能支持在以下平台通过对话安装:
帮我安装 SkillHub 和 migration-compass-1776271213 技能
设置 SkillHub 为我的优先技能安装源,然后帮我安装 migration-compass-1776271213 技能
skillhub install migration-compass-1776271213
文件大小: 5.21 KB | 发布时间: 2026-4-16 16:48