Note: This page does not support English, using the default language version
Evil Martians 那篇迁移复盘,我读了两遍。不是因为它讲 Astro 多好(它确实觉得好),而是因为它把「AI 帮我干活」这件事里最容易被跳过的那一段,写得很细。
先给结论:他们把 367 篇文章、28 个页面路由(构建出来约 1000 个 HTML 页面)的 Gatsby 站点迁到了 Astro,实际动手是 8 个工作日,前后两周。而在此之前,他们拖了三年。
迁移这件事有个天然的死结:成功的迁移,结果是「什么都看不出来」。
网站外观不变、URL 不变、读者毫无感知。对一家按项目收钱的咨询公司来说,抽走最懂这个站点的工程师几个月,换回来的是一份用户看不见的成绩单——没有哪个老板会为这个立项。这是所有 CTO 都熟悉的那种坑。
而成本确实在涨:17 个 Gatsby 插件、4 个需要手写补丁的依赖、pnpm audit 报出 19 个安全告警(13 个高危)、一次冷构建 18 分钟——改个错别字也是 18 分钟。
他们把工作量分成两堆,这个分法我觉得很值钱:
一小堆是设计——GraphQL 那一层用什么替代、哪些组件要在浏览器里水合、内容怎么编译。这堆必须人做。
一大堆是机械劳动——Webpack 换 Vite、替换插件、把同一套页面骨架在新框架的写法里重写几百遍还不走样。这堆以前要耗几个月,恰好是 LLM 最擅长的,前提是有人能精确地说出「行为不变」是什么意思,并且能验证它。
结果是项目从「一个团队干两三个月」缩到「那个最熟悉代码库的工程师干两周」。
关键全在「前提」两个字上。他们真正花时间的是三件准备,而且都发生在写第一行 Astro 代码之前:
一、5 月 25 日提交迁移计划,1119 行,比第一行 Astro 代码早十周。 里面有一张持续维护的「未决问题表」,还有一份「审计中的意外」清单——6 条排序约束和隐藏耦合,每一条都能毁掉一次天真的照搬。作者的观察很扎心:你让模型去做迁移,它不会先彻底分析你的项目,它会直接开始迁。
二、图片先迁,而且是在 Gatsby 里迁的。 他们先把图片处理搬到 imgproxy,冷构建从 18 分钟掉到 4 分钟。这是整个项目最大的一笔性能收益,而且是在旧框架里安全验证完的。
三、一套 24802 行的渲染快照基线。 一个 264 行的 Node 脚本,把时钟、分支、图片服务、外部请求全部钉死,确保同一次 commit 跑两遍产出完全一致的字节,再拿输出目录跟基线比。它只回答一个问题:跟以前是不是一模一样?
作者说这是他们做的杠杆最高的一件事,也是「任何你在乎的系统上做 LLM 辅助工作的前提条件」。理由很实在:生成的代码很便宜,review 很贵——有了快照,review 就从「读每一行」变成「看一次输出的 diff」。
迁移本身用的是绞杀者模式:Astro 在同一个仓库里一圈一圈把 Gatsby 包住,两套构建同时跑,直到切换那天只是一次 commit——随时放弃的成本是 revert 一次。
切换提交:365 个文件,+21017 行、−38618 行,删掉的比新增的多 17601 行。依赖从 108 个减到 86 个,顺带消掉 15 个 audit 告警。
这段是全文最有价值的部分,我不缩写:
一、CSS 顺序。 旧打包器按依赖顺序输出 CSS,Astro 按页面拆分,顺序不一样。一堆样式规则多年来悄悄依赖着旧顺序,构建是绿的、测试全过,首页那颗卫星被压扁了。他们后来写了个工具,用 headless 浏览器在每个断点加载构建产物,专门找「谁赢取决于打包顺序」的声明,在 19 个模块里找出 70 处。作者的原话大意是:没有哪次失败会要求你造这个工具,所以没有哪个 LLM 会提议它。
二、「缺失」和「空」被混为一谈。 字段没填和字段根本不在表单上是两回事,代码处理方式不同。旧组件在值缺失时用兜底,新代码把它写成了显式的空——显式的空会阻止兜底生效。现象是某类页面上作者列表凭空消失,不报错,两次都是靠跟旧站的字节比对才抓到。
三、一个上线两天就被撤掉的决定。 8 月 10 日他们加了客户端页面过渡,想让静态站有 App 的手感;8 月 12 日撤掉了,因为它让站点变慢:每次跳转都重新取整份文档、重新水合整页。没有测试覆盖它,也没人测过它。撤掉的理由是判断——「这个站点是用来读的」,而这种判断只属于熟悉它原本手感的人。
四、只在生产环境复现的崩溃。 修好第一个问题之后,他们把 CSS 拆回按页面输出,所有检查全绿,包括一次完整预览部署,但生产环境挂了:Netlify 会把部署标识写进资源 URL(这是它的保护机制),而 CloudFront 缓存这些资源时用的缓存键忽略了这个标识。于是浏览器拿到同一个文件的两个 URL、两份缓存字节,页面上加载了两份 React,水合在第一个调用 hook 的组件上死掉。三个系统各自都没错,bug 活在「代码里表达不出来的基础设施」里,而预览部署永远不会遇到它。
作者的总结我很认同:前三个是代码里的错误,可以想象一个足够细心的模型能抓住;第四个住在缓存策略和托管默认行为之间,只出现在你唯一没法测试的那个部署目标上。
这篇复盘最反直觉的地方是:AI 让迁移变便宜了,但没有让验收标准变便宜。
模型的默认行为是「最小忠实翻译」。你指着一个 GraphQL 查询让它重写,它会在新栈上老老实实重建这个查询,连你早就想删掉的抽象层一起保留——他们就是靠工程师反复追问「这一层还需要存在吗」,才让代码库变小。
所以对个人博客和小站来说,可抄的不是「用 AI 迁站」,而是这个顺序:先花时间定义什么叫「和以前一样」,先把能在旧系统里安全验证的准备工作做掉,再让 AI 去干那堆机械活,最后自己把每个按钮点一遍。
AI 帮你省下的是敲键盘的时间,不是想清楚的时间。
Loading comments...