今年早些时候,我打开线性(Linear)发现首席技术官图马斯(Tuomas)给我分配了CI成本很高的问题,还让我加快CI速度。智能体让代码发布更快,但验证未跟上,每个PR仍须通过CI,开发加速使CI成瓶颈,推高成本并延长等待。我们优化PR等待时长与运行时间,测试套件今年以来几乎翻两番,等待从超6分钟降至刚过5分钟,单次测试运行时间大致减半。
正文
今年早些时候,我们CTO Tuomas在Linear给我派了个“CI成本太高”的任务,同时也要我加快CI。AI代理让提交代码快了指数级,但验证没跟上,每个PR还要过CI,开发加速后CI成了瓶颈,推高基础设施成本,开发者和代理等反馈更久。在Linear提升CI性能时,我们优化了PR等待CI的时间和消耗的runner时间。尽管测试套件年初以来几乎翻两番,PR等待时间从超6分钟降到刚过5分钟,单次测试runner时间减半。


以一月第一周为基准的测试套件性能,白线追踪单测试机器时间,在增加测试分片时飙升(分片缩短等待但耗更多机器时间),checkout停滞时也有 spike。
我们大致从四方面改进CI: - 升级基础设施和工具 - 优化门控其他作业的作业 - 减少重复设置 - 让测试执行更高效
Linear代码库主要是TypeScript,但这些优化多语言通用。
升级基础设施和工具
早期收获几乎不需优化CI本身。把负载从GitHub Actions迁到第三方runner(更快CPU、高性能存储、更好缓存),同口径对比切换前后两天,作业平均快34%,tsc等负载降52%。另改用原生TS编译器tsgo,tsc检查周中位数降73%,瓶颈完全移出类型检查。
脱离类型检查器做lint
lint是早期目标。部分自定义lint规则依赖TS类型信息,每次lint需建全类型图,成最耗内存的CI作业之一。改写为基于AST静态分析,不依赖类型信息识别类函数结构和守卫模式,让ESLint彻底弃用TS,API lint时间降68%,全仓库lint降55%,内存也大降。去掉类型依赖也让后来迁移Oxlint更容易,Oxlint本身减少了lint的runner分钟。
优化门控其他作业的作业
基础设施和单项检查变快后,从系统看CI,注意到前置小作业。每次运行先查PR改动路径和相同输入下测试是否已过。作业级门控让跳过的不占runner,但也使它们处于关键路径。八个API测试分片都要等它们完成,小延迟也至关紧要。
每个作业只取所需
多个工作流以变更检测作业决定下一步;如查diff是否含数据库迁移并输出信号调度相关CI检查。这些作业检出完整工作树,但只需子集。我们限制获取深度,最慢门控从94秒降至20秒;完全不需要工作树的作业移除检出,时间从27秒到7秒。对提交推送和合并队列事件(需比路径),稀疏无blob有限历史检出足够,再省11秒上下。


变更检测作业中位时长从26降至8秒,p90从31到12秒,最慢从138到37秒。
让检出更韧性
换底层runner后,actions/checkout时间变长有时挂起。因第三方runner在GitHub网外,靠直连IP,供应商查是链路间歇降级。多个工作流以检出开始,停滞可取延迟整段CI。为抗网络不稳,我们用自复合动作替actions/checkout,退避重试,设GITHTTPLOWSPEEDLIMIT和TIME让停滞连接约30秒中止而非挂起,并用检出缓存(黏盘持久git镜像)。关键路径作业等检出空闲大减。
最小化关键路径上的东西
并非所有关键路径作业都需在。合并前最后检查写缓存标记,使PR测试通过后还占合并队列。我们把它移入测试分片完成后但不门控的作业,为每个API PR和合并队列省42秒。这些改动在缓存未命中时为API PR所需检查省约1分钟,也减runner启动。
减少重复设置
接着看每作业重复的设置成本:启runner、装包、备构建依赖。开销使仅秒级有用工作的作业耗数分钟设施时间。几步应对:
在CI镜像预装共享依赖
API测试分片每次用apt装同款Postgres客户端耗7-8秒。移入含Node和客户端的小CI基础镜像,分片始于就绪环境。后发现设时下载原生构建头偶尔挂,便加进镜像,缩短长尾。
只装每作业所需依赖
Linear代码库是pnpm工作区单体仓。API测试工作流装整个工作区,仅需API包及其依赖。限制装API包使pnpm安装从44-73秒降至16-18秒。同法用于API相关作业,它们原装全仓并上传后续几乎不命中的依赖缓存。
重建更快时不缓存
我们试缓存node_modules,发现重建更快。缓存键依赖常变锁文件,即使命中恢复约28秒,而过滤安装约7.5秒。缓存只加保存时间和变数,无可见好处。
这三改使每分片设置时间降约44%,从110-140秒到67-73秒。


此外还有可完全避免的重复设置。
避免重放未变的设置
部分设置仅输入变时需重复。API容器每次重放全数据库迁移史,即使PR未改schema。改为加载生成的schema快照和引导文件,每容器数据库设置从约12秒降至1-2秒。
将短检查批进更少作业
七个独立检查各启runner、检仓库、装依赖,仅做秒级工作。我们合并为两个作业,内部并发跑七任务,使付同样设置开销次数从七降二。按六月用量,月省约87,000 runner分钟,相当总CI用量11.8%。


让测试执行更高效
每测试分片固定成本降后,可更激进并行API套件。它最大且最常执行,改进对合并时间影响特大。
按测试运行器视角平衡工作
我们TS测试套件用的Vitest按文件而非单测试时长分发工作。少数超大测试文件可主导分片,即使其他分片早完也拖住整套。我们把大文件拆小且聚焦,保测试结构,评估不同分片配置。年初已从三到四分片;到八分片使关键作业初测快约19%便宜19%。一周后最慢分片从5.25分降至4.33分。
Vitest通常隔离每测试文件,对我们意味每分片重建实体、GraphQL、装饰器图。我们引入opt-in vitest项目设isolate:false,让安全文件在各worker共享模块注册。


这是最大单项性能提升,按我们的量月省约17%。最慢分片从约300-379秒降至195秒,API分片总runner时间从约32.8分降至22分每运行。
也是正确性风险最高的优化。我们在每文件用opt-in注释明定资格,加共享状态必要拆解。少数文件用假定时器或共享状态无法安全理清,留隔离项目。因代理现写多数测试,我们更新相应代理技能以纳入此性能opt-in,使生成测试默认守同约束。
分片受设置开销限制
进一步分片仅在每分片固定成本低时划算,因分片数翻倍也翻倍工作流设置时间。前述设置优化使八分片可行。若每分片110-140秒,八分片仅设置就耗15-19分钟runner时间,超测试本身。现设置约40秒,八分片总设置时间少于先前四分片,同时测试并行翻倍。


跨系统复合的改进
若年初不刻意改进CI,今测试套件需约11分钟,近双倍于现在开发者等待。工作未止。代码库续增,现周加约2,000测试。保持CI快是持续努力,多用此过程所学对新瓶颈。


评论
社区普遍感受到 AI 代理编码正将 CI 推向瓶颈,主流应对是换用更快 runner 与缓存构建工具;但部分人指出真正瓶颈在人类验收或测试本身无效,单纯提速意义有限。
代理编码正严重拖累 CI,必须加速构建与测试
- 论据:AI 高频提交与测试套件膨胀让流水线拥堵,有人用 Bazel 和定制 runner 缓解(较多人) > 代理编码给 CI 造成巨大负担,我用 Bazel 和提升 runner 来改善(@mraza007)
托管 CI 如 GitHub Actions 太慢,应迁往自托管或第三方 runner
- 论据:GitHub Actions 方便但性能与可靠性差,迁移到更快 CPU、更好缓存的 runner 明显提速且省钱(较多人) > 离开 GitHub Actions 用更快机器跑同流水线确实更快,不出意外(@classictraffic)
Bazel 类缓存构建工具能大幅压缩构建时间
- 论据:承担初期成本后,Bazel 热缓存可使巨型项目构建降至十秒级,也有更易用的替代方案(较多人) > 若能付得起搭建成本,Bazel 热缓存下大项目构建也能约十秒(@azkalam)
多数单元测试只是装饰,端到端测试才有意义
- 论据:代理和人类都倾向改测试迎合代码,单元测试用例多 trivial;端到端能抓用户体验回归(较多人) > 测试套件翻两番,但单元测试像化妆品,只为覆盖率而非原本目的(@yieldcrv)
真正瓶颈在人类验收而非 CI 速度
- 论据:CI 再快,人工确认功能正确、客户满意仍耗时;有团队 CI 数千测试两分钟但卡在人测(少数人) > 瓶颈不在 CI,在于人工测试是否真做出客户想要且喜欢的东西(@aliclark)
系统应拆成更小隔离的契约服务以适配代理
- 论据:代理在边界清晰的小服务中迭代更顺畅,只需满足契约即可内部演进(少数人) > 系统应更小、隔离、面向契约,代理可在内部自由迭代(@CharlieDigital)
加速 CI 只是把瓶颈推到部署回滚
- 论据:CI 加速后,部署与回滚无法同尺度扩展,会成为新堵点(少数人) > CI 提速后,下一个瓶颈会移到部署和回滚(@pushpendraw)
现有 CI/CD 范式割裂,需统一调度或彻底重写
- 论据:构建与测试本属一体,需理解全链的调度器才能真正提速;也有人主张废弃现有流水线重做(少数人) > 要解决需停止把构建测试分开扩展,需调度器懂全链才能加速(@shykes)
有价值的补充
- 有开发者指出 Linear 直到估值十亿级才操心 CI 优化,小团队不必过早焦虑(@frangonf)
- 建议测试负载按耗时而非文件均衡可提升性能(@d_sc)
- 猜想 GitHub Actions 中断与其对自托管 runner 收费将用户推回劣质基础设施有关,反让替代云更可靠(@hollow-moe)
- 玩笑折射情绪:Linear 本已完成功能,却忙着变成下一个 Jira,AI 编码也没解放它(@algesten)