我给日报写的那条流水线,最早是一堆任务并行跑的。后来我把它改回了单线程顺序执行。
不是并行不好。是并行之后,我得多花时间核对每一步跑完没有,那段时间没人替我出,只能从看结果的时间里扣。省下来的,还不够还回去。
AI 提的速,如果没人管最慢那一步,就会被那一步一分不剩地吃掉。
9 月 21 日 Linear 官方复盘了今年几乎一整年的 CI 优化,起点只是 CTO 派的一张工单:CI 成本太高。文里的原话是:agent 让发布代码的速度指数级变快,但验证这些改动没有以同样速度跟上。每个 PR 还是得过 CI,于是 CI 变成瓶颈。
数字比结论更说明问题:测试套件今年翻了近 4 倍,做完四类优化之后,PR 等待时间才从 6 分多降到 5 分多。
他们最大的收益不是「跑得更快」,是把 setup 的固定开销砍掉。原文的算法很硬:setup 要 110 到 140 秒时,8 个分片光 setup 就得花 15 到 19 分钟,比测试本身还多;setup 降到约 40 秒后,8 个分片的总 setup 反而比优化前的 4 个分片更少。
这解释了为什么「把测试拆得更细」经常没用:并行度的价格是固定开销乘以分片数,先降固定开销,再谈并行。 顺序反了,你只是花更多机器时间,换回一点等待时间。
还有一个反直觉的:他们试过缓存 node_modules,结论是重新装更快。缓存命中恢复约 28 秒,按包过滤的全新安装约 7.5 秒。缓存不是免费的,它只是把成本从安装挪到了恢复和校验上。
我改造过一套跑了多年的老系统,第一件事也不是写新代码,而是先摸清哪些环节是「每次都得从头再来一遍」的固定开销。老系统的慢,多半不是某一段算得慢,是同样的准备动作被重复做了太多次。
那篇复盘里最容易被划过去的一句,我认为最值钱:因为 agent 现在写出了他们大多数的测试,他们同步更新了各自的 agent skills,把这一性能开关纳入进去,让生成的测试默认就遵守同样的约束。
换句话说,他们没打算在 CI 里拦违规测试,而是把约束写进了 agent 自己会读的那份文件,让它第一次就不违规。
同一件事上,他们对风险的处置也该抄。收益最大、风险也最高的一步(让部分测试文件共享模块注册表)没有默认打开,而是每个文件一行 opt-in 注释显式声明;说不清能不能安全共享状态的那几个文件,一律留在隔离模式里。
两条合起来是个很朴素的原则:约束写在产出端,比写在验收端便宜一个数量级。 写在验收端,你得先让 AI 生成违规的东西,再靠人 review 抓回来;写在产出端,它第一次就不生成。我重构那套后端时定的第一条规矩也是这个形状:业务层里不许出现框架的上下文对象。理由不是那个对象危险,而是不该出现的依赖,一旦允许它出现,就再也收不回来。
反面教材今天也有一个。xAI 发布 Grok 4.7 时称它「更仔细地检查自己的产出」,但马斯克本人公开发帖说,本该让模型更仔细推理的那段训练流程,实际惩罚了写长回答的行为,于是模型学会了在难题上提前放弃、跳过自检,正好是宣传语的反面。
规矩写反了,行为会朝你不想要的方向长得很好。 所以「把约束写进能力包」有个前提:约束本身必须可核对。每个文件一行显式注释是可核对的,你能搜出全部适用范围;训练目标是隐式的,只能事后发现。
同一个 24 小时里,还有两家在同样的位置做同一件事:Codex 放开了子 agent 的交互请求,但把临时线程默认设成只读;Qwen Code 让工作流脚本传的工具白名单只能收窄、不能扩权。宽的是「能问什么」,窄的是「能做什么」。
一、给你自己的流水线量一个数。 跑一次「空 job」,只做 checkout 和装依赖,看它花多少秒。把这个数乘以你的分片数,就是你并行度真正的上限。如果它已经吃掉单次运行的一半,瓶颈就不在测试,在准备。
二、打开你团队的 agent 能力包,看里面有没有一条是成本或性能约束。 通常是 CLAUDE.md、AGENTS.md 或某个 skill 文件。没有就加一条,而且要写成可核对的判据,因为写「性能要优化」这种话没用。
三、把「缓存」当成待验证假设,而不是默认答案。 挑你最慢的一个 job 做对照:缓存命中恢复的耗时,对比全新安装。Linear 那次结果是缓存更慢。