前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
返回 AI 情报前线
All News · 全部资讯8655
  • Jev:按输入计费的决策模型,输出免费
  • Langflow OSS 认证 RCE 漏洞 CVE-2026-17633
  • 小米 MiMo-V2.6 开源:AA 指数最高开源模型
  • xAI发布Grok 4.7:编程强化模型,百万词元2美元起
  • AWS发布Strands Harness:开源Agent框架,Token成本降28%
  • Spec-Driven 开发:摆脱 Vibe Coding 的工程化实践
  • AI 编程助手 CSS 好看但上生产就崩的根因与修复
  • Agent 诊断结果上线前如何验证:分离决策与执行
  • AI编码导致CI成瓶颈?我们重新设计了CI流程
  • 从氛围记忆到确定性自主:AI 原生基础设施设计思路
  • 用 Agent 流程开发简单 SPA 的实战经验
  • AI 生成 React UI 的真实问题:从第二页开始组件漂移
  • vLLM 深度解析:高吞吐 LLM 推理的性能瓶颈
  • AWS 开源 AI 编程助手,成本比 Claude Code 低 45%
  • 用Docker Compose构建可复现的AI Agent评测环境
  • 阿里发布Qwen-Image-2.1:7B开源图像生成编辑模型
  • Benchling 用 Bedrock AgentCore 为多租户 AI Agent 构建深度防御安全架构
  • 苹果Mac mini 2026款:M6/M5 Pro芯片,AI性能最高提升4倍
  • Mac Studio 2026:M5 Ultra 支持最高 512GB 统一内存,可本地运行超大模型
  • Grok 4.7登陆GitHub Copilot,面向Agent化编程
  • AI 安全是工程问题:Agent 堆栈每一层的防护实践
  • Agent 系统的瓶颈不是模型,是架构设计
  • 防御式 Agent 架构:Schema 注入与超时控制
  • 自研研究 Agent 拦截 AI 编程幻觉:文档先行策略
  • 像物理学家一样剪枝 LLM:区块移除的伊辛模型优化
  • Mac mini上的AI开发新范式:OpenClaw与Codex实战
  • Cloudflare Python Workers 正式上线,可用纯 Python 开发边缘应用
  • 浏览器直接给 ESP32 烧录 Claude 写的宏,无需 IDE 或工具链
  • Google 发布 Agent 安全风险报告:5 万美元循环消耗与凭证窃取案例
  • 多 Agent 合规流水线:用 RAG + 自修正架构对抗 WCAG 幻觉问题
  • Anthropic 发布金融领域 Claude 参考智能体套件
  • OpenAI 披露强化学习模型在上下文压缩时插入 Prompt 注入
  • Jev 决策模型实测:0.3秒完成意图分类和工具路由,$0.00004/次
  • Kimi Code Desktop 上线:图形界面 + 内置终端 + Git 状态,支持 Swarm 多 Agent 协
  • StepFun Step 5 Preview:600B 总参数 MoE 模型,1M 超长上下文,10 月开源
  • JSON-Render:通吃 React/Vue/Svelte/React Native 等 10+ 框架的生成式 UI
  • Agent-Native:让 Agent 能力同时暴露给 LLM 工具调用和 UI 操作的 TypeScript 框架
  • 清华联合无问芯穹开源具身智能体RPent,GPT-6 Astra注入机器人
  • AI Agent工具调用边界case:路由错误比想象中更脆弱
  • 已加载 39 / 8655
8.0
热点
AI SCORE
编程提效2026-09-22 03:23

AI编码导致CI成瓶颈?我们重新设计了CI流程

Hacker News#CI/CD#AI编程#DevOps
Editor brief · 编辑速览

AI辅助编程大幅增加CI负载,Linear团队分享如何改造CI pipeline以应对高频提交和大规模自动化测试。

文章思维导图
Knowledge map
拖拽缩放
Full translation

完整中文译文

今年早些时候,我在 Linear 里打开 Linear,发现我们的 CTO Tuomas 给我分配了一个 Issue,标题是"CI 成本过高"。与此同时,他还希望我让 CI 变得更快。

AI 编程 Agent 让代码交付速度呈指数级提升,但对这些变更的验证却没能跟上同样的速度。每个 PR 仍然需要通过 CI 流程,于是当开发加速时,CI 就成了瓶颈——推高了基础设施成本,也让开发者和 Agent 等待反馈的时间越来越长。

在 Linear 为了让 CI 更加高效而做的各种优化中,我们重点关注了两个指标:PR 在 CI 上等待的时间,以及消耗的 runner 时间。尽管今年年初以来我们的测试套件增加了近四倍,但 PR 的等待时间从超过 6 分钟降到了仅 5 分钟出头,而每个测试消耗的 runner 时间大致减少了一半。

这张图是测试套件性能相对于 1 月第一周的指数。白线追踪的是每个测试的机器时间,当添加测试分片时会飙升(分片缩短了等待时间但消耗了更多机器时间),在 checkout 卡顿问题期间也有波动。

性能指标图表,蓝线追踪测试覆盖率增长,白线追踪机器时间变化,1 月至 9 月

性能指标图表,蓝线追踪测试覆盖率增长,白线追踪机器时间变化,1 月至 9 月

总的来说,我们从四个方向改进了 CI:

升级基础设施和工具链

优化前置关键路径上的任务

减少重复的初始化工作

提升测试执行效率

Linear 的代码库以 TypeScript 为主,但其中许多优化方法适用于各种语言和工具链。

升级基础设施和工具链

我们最早的一些收益几乎不需要对 CI 本身做任何优化。把工作负载从 GitHub Actions 迁移到第三方 runner,这些 runner 拥有更快的 CPU、更高性能的存储和更好的缓存基础设施,让我们在相同的流水线运行在更快的机器上。在切换前后两天的逐项对比中,作业平均快了 34%,像 tsc 这样的工作负载更是下降了 52%。

此外,更新工具链也带来了回报。切换到 tsgo(原生 TypeScript 编译器)后,tsc 检查的周中位数时间下降了 73%,幅度大到将类型检查完全移出了瓶颈位置。

linting 是另一个早期目标。我们有少量自定义 lint 规则依赖 TypeScript 类型信息——要么是强制某种约束,要么是应用某种 autofix。这意味着每次 lint 运行都要在评估这些规则之前构建完整的类型图,使得 linting 成为我们 CI 作业中内存消耗最大的任务之一。

我们用对抽象语法树的静态分析重写了这些规则,在不依赖类型信息的情况下识别函数风格的代码结构和 guard 模式。这让 ESLint 完全不再需要 TypeScript,API lint 时间减少了 68%,全仓库 lint 时间减少了 55%。内存占用也大幅下降。

移除对类型信息的依赖也让我们后来迁移到 Oxlint 变得容易许多,因为纯语法层面的规则迁移起来非常直接。Oxlint 本身进一步减少了 linting 所消耗的 CI runner 分钟数。

优化前置关键路径上的任务

底层基础设施和各环节检查都跑得更快之后,我们拉远视角,把 CI 当作一个系统来看。这让我们注意到那些挡在所有事情前面的小任务。每次运行都会先检查一个 PR 修改了哪些路径,以及这些测试是否已经在相同输入下通过。我们在这些检查的作业级别设置门控,这样被跳过的任务永远不会占用 runner,但同时也让它们直接落在了关键路径上。八个 API 测试分片在它们完成之前一个都不能启动,所以即使是很小的延迟也会被不成比例地放大。

我们的多个工作流都以一个变更检测任务作为开头,它来决定后续运行什么——例如,它会检查 diff 中是否包含数据库迁移,然后输出一个信号用于调度相关的数据库 CI 检查。这些任务当时在 checkout 完整的工作树,尽管它们只需要其中很小的一部分。我们限制了 fetch 深度,把这些门控中最慢的从 94 秒降到了 20 秒;对于那些根本不需要工作树的作业则完全移除了 checkout,将这些任务的时间从 27 秒减少到了 7 秒。对于 commit push 和 merge-queue 事件(在这些场景下确实需要 diff 路径),我们发现使用稀疏的、blobless 的 checkout 并限制历史记录就够了,又节省了大约 11 秒。

变更检测任务的中位持续时间从 26 秒降到了 8 秒,P90 从 31 秒降到了 12 秒,最慢的一次运行从 138 秒降到了 37 秒。

性能分布直方图:蓝柱(优化后 8 秒中位数)和灰柱(优化前 26 秒中位数),显示优化后加载时间减少

性能分布直方图:蓝柱(优化后 8 秒中位数)和灰柱(优化前 26 秒中位数),显示优化后加载时间减少

在我们更换了底层 runner 基础设施之后,我们注意到作业中 checkout 时间(使用 actions/checkout)变长了,有时还会卡住。由于第三方 runner 位于 GitHub 网络之外,它们依赖直接 IP 链路来访问 GitHub。提供商追踪到这些卡顿源于该链路上的间歇性降级。我们的多个工作流都以 checkout 开头,所以一次卡住的 fetch 就可能延迟整个 CI 运行。

为了抵御网络不稳定的风险,我们将 actions/checkout 替换为自己编写的复合 action,它支持退避重试,并设置了 GIT_HTTP_LOW_SPEED_LIMIT 和 GIT_HTTP_LOW_SPEED_TIME,使卡住的连接在大约 30 秒后中止而不是无限挂起;同时使用 checkout cache,它在粘性磁盘上维护一个持久的 git mirror。其结果是,关键路径任务空闲等待 checkout 完成的情况大大减少。

并非关键路径上的每个作业都需要留在那里。我们当时是在合并前的最后一次检查中写入缓存标记,这意味着一个 PR 在其测试通过之后仍然可能卡在 merge queue 里。我们把这个写操作移到了一个在测试分片完成后才运行、但不阻挡任何后续步骤的作业中,这一改动为每个 API PR 和 merge-queue 条目在合并路径上节省了 42 秒。

综合来看,这些改动在缓存未命中时大约为 API PR 的必要检查节省了将近一分钟,同时也减少了 runner 的启动次数。

减少重复的初始化工作

接下来我们把注意力转向每个作业都在重复的初始化成本——启动 runner、安装依赖包、配置构建依赖等。这种开销意味着一个只做了几秒钟有用工作的作业最终可能消耗整整几分钟的基础设施时间。以下是我们针对这个问题采取的几项措施:

我们的 API 测试分片每次运行都要用 apt 安装相同的 Postgres 客户端,花费 7 到 8 秒。我们把它移到了一个包含 Node 和该客户端的小型 CI 基础镜像中,这样每个分片就可以从一个已经准备好的环境启动。后来我们又在镜像中添加了所需的原生构建头文件,因为发现在初始化过程中下载它们偶尔会卡住,缩短了长尾时间。

Linear 的代码库是一个以 pnpm workspace 管理的 monorepo。我们的 API 测试工作流当时在安装整个 workspace,尽管它只需要 API 包及其依赖。将安装限制为 API 包后,pnpm install 从 44-73 秒降到了 16-18 秒。我们对 API 相邻的作业也应用了相同的模式,它们之前各自都在安装完整仓库并上传依赖缓存,而后续运行几乎永远无法命中。

我们还测试了缓存 node_modules 的效果,发现直接重建反而更快。缓存 key 依赖于一个频繁变化的 lockfile,而且即使命中缓存也需要大约 28 秒来恢复,相比之下一次过滤后的安装大约只需 7.5 秒。缓存不仅增加了保存时间还给结果带来了变异性,却没有给我们带来任何明显的好处。

这三项改动合计将每个分片的初始化时间减少了约 44%,从 110-140 秒降到了 67-73 秒。

测试分片的 p95 持续时间

GitHub Actions 工作流对比,显示 test-api 运行各步骤的时间分解;左侧运行(4 分 57 秒)与右侧运行(3 分 14 秒),展示流水线各阶段性能提升

GitHub Actions 工作流对比,显示 test-api 运行各步骤的时间分解;左侧运行(4 分 57 秒)与右侧运行(3 分 14 秒),展示流水线各阶段性能提升

除此之外,还有一些其他形式的重复初始化工作我们可以完全避免。

有些初始化工作只在输入变化时才需要重复。例如,我们的 API 容器每次运行都在重放完整的数据库迁移历史,即使 PR 没有修改 schema。对于这些情况,我们切换为加载生成的 schema snapshot 和 bootstrap 文件,将每个容器的数据库初始化从大约 12 秒缩短到 1-2 秒。

有七个独立的检查各自都在启动一个 runner、checkout 仓库、安装依赖,然后只做了几秒钟的有用工作。我们把它们合并成了两个作业,然后在这两个作业内部并发运行这七个任务。这将我们支付相同初始化开销的次数从七次减少到了两次。根据 6 月的使用量计算,这一改动每月节省了大约 87,000 个 runner 分钟,相当于我们总 CI 用量的 11.8%。

CI/CD 流水线批处理优化前后对比,显示测试工作流各步骤的执行时间和依赖关系,所有任务标记为成功完成

CI/CD 流水线批处理优化前后对比,显示测试工作流各步骤的执行时间和依赖关系,所有任务标记为成功完成

提升测试执行效率

随着每个测试分片的固定成本降了下来,我们可以更加激进地对 API 套件做并行化了。它是我们工作流中规模最大、执行频率最高的部分之一,所以对它的改进对合并时间有超大的影响。

Vitest(我们用于 TypeScript 测试套件的测试运行器)是按文件而非按单个测试的持续时间来分配工作的。这意味着几个异常大的测试文件可能主导一个分片,实际上拖累整个套件的完成进度——即使其他分片早就跑完了。

我们将这些大文件拆分成更小、更聚焦的文件,同时保留了测试的结构,然后评估了不同的分片和 runner 配置。我们在今年早些时候已经从三个分片增加到了四个;迁移到八个分片后,关键作业在初始基准测试中快了约 19%,费用也降低了约 19%。一周之后,最慢的分片从 5.25 分钟降到了 4.33 分钟。

Vitest 通常会隔离每个测试文件,这对我们来说意味着每个测试分片都要重建 entity、GraphQL 和 decorator 图。我们引入了一个可选的 vitest 项目,配置为 isolate: false,允许安全的文件在每个 worker 内部共享一个模块注册表。

模块状态缓存优化:优化前(每个文件独立状态)vs 优化后(单一共享状态),减少 worker 进程间的冗余重建

模块状态缓存优化:优化前(每个文件独立状态)vs 优化后(单一共享状态),减少 worker 进程间的冗余重建

这是我们单项性能改进中幅度最大的一次,在我们当前的体量下每月节省了约 17%。最慢的分片从大约 300-379 秒降到了约 195 秒,而每次运行的 API 分片 runner 时间从大约 32.8 降到了 22 分钟。

同时这也是正确性风险最高的优化。我们通过在每个文件上添加可选的 opt-in 注释使符合条件的情况变得明确,并添加了共享状态所需的 teardown。有少量文件使用了 fake timer 或共享状态,我们无法安全地解开它们,所以将它们保留在隔离项目中。而且由于现在 Agent 编写了我们大部分的测试,我们也更新了各自的 Agent skills 来考虑这个性能优化选项,这样生成的测试默认也会遵循相同的约束。

只有在每个分片的固定成本足够低的情况下,进一步分片才能带来收益——因为加倍分片数量也会让工作流的初始化时间翻倍。我们之前提到的初始化优化正是使八个分片变得可行的原因。如果每个分片需要 110-140 秒的初始化时间,八个分片光初始化就要花费 15-19 分钟的 runner 时间,比测试本身还长。现在初始化时间约为 40 秒,所以八个分片的总初始化时间比之前四个分片还少,同时将测试并行化的程度提高了一倍。

在初始化优化之前,测试作业使用 4 个分片并在初始化上花费 8.3 分钟。优化之后,我们可以运行 8 个分片而初始化时间仅需 7.5 分钟。

测试分片优化:从 4 个分片(初始化 8.3 分钟)增加到 8 个分片(初始化 7.5 分钟),实现更好的测试工作负载并行化

测试分片优化:从 4 个分片(初始化 8.3 分钟)增加到 8 个分片(初始化 7.5 分钟),实现更好的测试工作负载并行化

在系统中产生复合效应的改进

如果今年年初我们没有刻意努力改进 CI,当今的测试套件大约需要 11 分钟,是现在开发者等待时间的两倍。这项工作还没有结束。很显然,我们的代码库会继续增长;我们目前每周大约新增 2000 个测试。随着这种情况保持 CI 快速将是一项持续的工作,其中很大一部分将运用我们在这个过程中学到的方法来应对新的瓶颈。

CI/CD 各流水线阶段性能改进明细,显示工具链、门控作业、初始化和整体指标的时间减少百分比,范围从 -12% 到 -89%

CI/CD 各流水线阶段性能改进明细,显示工具链、门控作业、初始化和整体指标的时间减少百分比,范围从 -12% 到 -89%

Original source

本文由 AI 翻译整理自 Hacker News,原文版权归原作者所有。

阅读英文原文
上一篇
Agent 诊断结果上线前如何验证:分离决策与执行
下一篇
从氛围记忆到确定性自主:AI 原生基础设施设计思路