框架迁移
增量替换一个框架为另一个(如 Express→Fastify、Vue 2→3),非大爆炸。
为什么
框架迁移触及每个路由、每个中间件、每个测试 — 是软件中爆炸半径最大的变更。大爆炸迁移意味多周分支啥也不发,坏时(会坏)无法判断 200 个变更哪个导致。增量方式 — 绞杀者无花果模式 — 一次一个端点路由到新框架,旧框架作回退。每个迁移端点各自 PR、部署、回退。迁移期间持续发功能,且可随时停若优先级变。这是无 冻结迁移 500 路由应用的方式。
何时使用
当前框架 EOL 或阻塞需要的功能时。绝不为新鲜迁移 — 成本总比估高。
如何做
1. 前面放路由/代理(nginx 或进程内路由)可按路径路由到旧或新 2. 先选最低风险端点(只读、低流量路由)验证方法 3. 在新框架实现该端点;仅该路径路由到新应用 4. 生产双跑:影子流量到新,比较响应,diff 告警 5. 该端点切到新框架;保旧代码即时回退 6. 迁移下一端点;重复。各自 PR + 部署 + 监控窗口 7. 最后端点迁完则完全删旧框架 8. 全程在未迁端点发功能 — 迁移不可冻结产品
示例
# 绞杀者无花果:nginx 按路径路由到旧(Express)或新(Fastify)
location /api/health { proxy_pass http://fastify:3001; } # 已迁
location /api/ { proxy_pass http://express:3000; } # 未迁
# 影子流量:记同请求下旧新 diff
app.get('/api/orders', async (req, res) => {
const [oldRes, newRes] = await Promise.all([
expressHandler(req), fastifyHandler(req)
]);
if (JSON.stringify(oldRes) !== JSON.stringify(newRes)) {
logger.error({ oldRes, newRes, path: req.path }, '迁移 diff');
}
res.json(oldRes); // 切换前仍服旧
});
# 切换一个端点(各自 PR + 部署)
# nginx:把 /api/orders -> fastify:3001
# 监控错误率 1 小时;退化则回退 nginx(即时回退)
# 每个迁移端点一个 PR:
# "migrate: /api/health to fastify"
# "migrate: /api/orders to fastify"
# ... 500 个 PR 后完全删 Express。常见问题
- ⚠大爆炸迁移分支 — 6 周无功能,坏时无法 bisect 200 个交织变更。
- ⚠无影子流量 — 切换端点凌晨 3 点发现新框架日期序列化不同。
- ⚠迁移期间冻结功能 — 业务失耐心半路取消,永远留两框架。
最佳实践
- ✓用绞杀者无花果模式:代理按路径一次迁一端点 — 各自可部署可回退。
- ✓切换前每端点影子流量:同请求发旧新,任一响应 diff 告警。
- ✓迁移期间绝不冻结功能 — 在未迁端点发功能;迁移是后台非停世界事件。