AI 写代码之后 CI 变慢,Linear 怎么把等待压下来
Linear 2026-09-21 复盘:测试量近四倍,PR 等待从 6 分钟多降到 5 分钟出头,单测机器时间约减半。可照搬的是门禁、安装和分片,不是他们的云厂商。

Linear 工程师 Mufeez Amjad 在 2026 年 9 月 21 日写下他们今年的 CI 改造。起因是 CTO 丢来一张单:「CI 成本高」,顺便要求更快。背景句写得很直:Agent 让改代码变快,每张 PR 仍要过 CI,于是 ai 编程之后 ci 变慢,机器账单和等待一起上去。他们的结果:测试套件从年初到现在将近四倍,PR 等待从 6 分钟多降到 5 分钟出头,每个测试摊到的机器时间大约减半。若什么都不做,按他们的估算,今天这套测试要等大约 11 分钟。
先换机器和编译器,再改流水线
迁出 GitHub 托管 runner、换到 CPU 和磁盘更快、缓存更好的第三方 runner 之后,切换前后各一天的对比里,任务平均快 34%,其中 tsc 一类下降约 52%。随后改用原生 TypeScript 编译器 tsgo,tsc 检查的周中位数下降 73%,类型检查不再是瓶颈。
自定义 lint 有几条依赖类型信息,每次都要建整张类型图。他们改成只看语法树,ESLint 可以丢掉 TypeScript:API 包 lint 时间降 68%,全仓库 lint 降 55%。这也让后来迁到 Oxlint 更顺,因为纯语法规则好搬。Oxlint 本身再减少 lint 占用的 runner 分钟。
这些数字是 Linear 的 TypeScript monorepo。别的语言没有 tsgo,但「门禁任务不要做它用不上的类型检查」仍然成立。Agent 把测试写进 CI 之后,黑盒等待怎么拆,可对照 Claude 进 CI 的黑盒。
关键路径上的小事更贵
变更检测决定后面跑不跑数据库检查。这些任务原先检出整棵树。限制 fetch 深度后,最慢的门禁从 94 秒降到 20 秒;完全不需要工作区的任务从 27 秒降到 7 秒。变更检测中位数从 26 秒到 8 秒,p90 从 31 秒到 12 秒,最慢一次从 138 秒到 37 秒。
换 runner 之后 actions/checkout 变慢甚至挂起,因为机器在 GitHub 网络外,链路上有间歇劣化。他们换成自己的 composite action:退避重试,并设置 GIT_HTTP_LOW_SPEED_LIMIT 与 GIT_HTTP_LOW_SPEED_TIME,卡住大约 30 秒就放弃,同时用粘滞盘上的 git mirror 当 checkout 缓存。
合并前写缓存标记会让测试已过的 PR 继续堵在合并队列。把写入挪到不挡合并的任务上,每张 API PR 的合并路径少约 42 秒。
重复安装比测试本身贵
API 测试分片每次用 apt 装同一个 Postgres 客户端,要 7 到 8 秒。他们把 Node 和客户端放进 CI 基础镜像。pnpm workspace 原先装整仓,收成只装 API 包后,安装从 44–73 秒降到 16–18 秒。node_modules 缓存他们试过:命中也要约 28 秒恢复,过滤安装大约 7.5 秒,缓存更慢,于是放弃。三项合计,每个分片的准备时间大约降 44%,从 110–140 秒到 67–73 秒。
数据库迁移不必每次重放。schema 没变时加载生成的快照和 bootstrap,容器准备从约 12 秒降到 1–2 秒。七个各自起 runner 的短检查并成两个任务、内部并发,按 6 月用量大约每月少 87000 runner 分钟,约占当时 CI 用量的 11.8%。
分片和隔离要一起算
Vitest 按文件分片,超大文件会拖死一个分片。拆文件后从 4 个分片增到 8 个,关键任务大约快 19%、便宜 19%;一周后最慢分片从 5.25 分钟降到 4.33 分钟。
更大的一笔是允许部分文件 isolate: false,在 worker 内共享模块图。最慢分片从大约 300–379 秒降到约 195 秒,API 分片总机器时间从约 32.8 分钟降到 22 分钟,按他们的量大约每月省 17%。风险也最高:每个文件用注释显式加入,共享状态要拆掉;用了假定时器、拆不开的文件留在隔离项目里。因为多数新测试是 Agent 写的,他们改了对应 skill,让生成的测试默认遵守这个约定。
分片加倍会把准备时间也加倍。准备还在 110–140 秒时,8 个分片光准备就要 15–19 分钟,比测试还长。准备降到约 40 秒之后,8 片的准备总时间比以前 4 片还短。他们现在大约每周新增 2000 个测试,所以这套账要按周重算,不能改完一次就停。