<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Linear on 奇诺分享 | 重在分享</title>
        <link>https://blog.ccino.org/tags/linear/</link>
        <description>Recent content in Linear on 奇诺分享 | 重在分享</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh-cn</language>
        <lastBuildDate>Tue, 22 Sep 2026 08:00:00 +0800</lastBuildDate><atom:link href="https://blog.ccino.org/tags/linear/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>AI 把写代码变快了，CI 第一个撑不住</title>
        <link>https://blog.ccino.org/p/ai-coding-ci-bottleneck-linear-2026/</link>
        <pubDate>Tue, 22 Sep 2026 08:00:00 +0800</pubDate>
        
        <guid>https://blog.ccino.org/p/ai-coding-ci-bottleneck-linear-2026/</guid>
        <description>&lt;img src="https://blog.ccino.org/p/ai-coding-ci-bottleneck-linear-2026/imgs/cover.png" alt="Featured image of post AI 把写代码变快了，CI 第一个撑不住" /&gt;&lt;p&gt;用 Agent 写代码的人对这种节奏应该不陌生。上午提一个想法，下午回来，Agent 已经交了三个 PR，改动看着都挺像样。你去泡了杯咖啡，回来发现第一个 PR 还挂在 CI 上，第二个排在合并队列里，第三个连测试都没开始跑。&lt;/p&gt;
&lt;p&gt;Linear 的工程师 Mufeez Amjad 今年碰上的也是这件事，只是规模大得多。年初，CTO 给他派了一张 issue，标题就叫「CI costs are high」，顺手还加了一句，把 CI 也弄快点。&lt;/p&gt;
&lt;p&gt;Linear 在 9 月 21 日把这件事写成了一份&lt;a class=&#34;link&#34; href=&#34;https://linear.app/now/ci-bottleneck-reworked&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;复盘&lt;/a&gt;。Agent 让他们出代码的速度指数级上涨，可每个 PR 还得过 CI，开发一加速，先排队的是 CI，基础设施账单跟着涨，人和 Agent 一起等反馈。这一年他们测试套件几乎翻了四倍，PR 等待时间却从 6 分多钟压到 5 分出头，单个测试烧掉的机器时间大约砍半。&lt;/p&gt;
&lt;p&gt;这些数字都是 Linear 自己公开的口径，看趋势可以，当行业平均值引用就不合适了。他们的代码库主要是 TypeScript，单仓库，Runner 也是特定的第三方环境。换一个仓库，结论未必一样。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://blog.ccino.org/p/ai-coding-ci-bottleneck-linear-2026/imgs/linear-ci-banner.png&#34;
	width=&#34;3904&#34;
	height=&#34;1600&#34;
	srcset=&#34;https://blog.ccino.org/p/ai-coding-ci-bottleneck-linear-2026/imgs/linear-ci-banner_hu_b5f5c9db0041b2c8.png 480w, https://blog.ccino.org/p/ai-coding-ci-bottleneck-linear-2026/imgs/linear-ci-banner_hu_d9364cda48978196.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Linear 官方复盘配图&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;244&#34;
		data-flex-basis=&#34;585px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;图片来自 &lt;a class=&#34;link&#34; href=&#34;https://linear.app/now/ci-bottleneck-reworked&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Linear 官方复盘&lt;/a&gt;。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://blog.ccino.org/p/ai-coding-ci-bottleneck-linear-2026/imgs/cover.png&#34;
	width=&#34;1672&#34;
	height=&#34;941&#34;
	srcset=&#34;https://blog.ccino.org/p/ai-coding-ci-bottleneck-linear-2026/imgs/cover_hu_972491040e4b99c8.png 480w, https://blog.ccino.org/p/ai-coding-ci-bottleneck-linear-2026/imgs/cover_hu_da8d2af58b844be1.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;AI 写代码变快后 CI 排队拥堵的示意封面&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;177&#34;
		data-flex-basis=&#34;426px&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;卡住流水线的经常是几个小任务&#34;&gt;卡住流水线的，经常是几个小任务
&lt;/h2&gt;&lt;p&gt;很多人遇到 CI 变慢，第一反应是测试写多了，删几个，或者让 AI 把测试跑快点。Linear 的复盘里最有意思的观察在别处。&lt;/p&gt;
&lt;p&gt;每个 CI 运行的开头都有一个变更检测，判断这个 PR 动了哪些路径，同一份代码以前跑没跑过。它站在最前面。后面八个测试分片，全部等它完工，慢十几秒，八个分片一起干等。这个任务原本要把整个仓库 checkout 下来，实际上只需要一小部分。Linear 给 fetch 加了深度限制，最慢的一次从 94 秒掉到 20 秒；不需要工作区的任务干脆连 checkout 都去掉了。整体看，中位数从 26 秒到 8 秒，最差的一次也有 138 秒，改完落到 37 秒。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://blog.ccino.org/p/ai-coding-ci-bottleneck-linear-2026/imgs/linear-gate-histogram.png&#34;
	width=&#34;3904&#34;
	height=&#34;1992&#34;
	srcset=&#34;https://blog.ccino.org/p/ai-coding-ci-bottleneck-linear-2026/imgs/linear-gate-histogram_hu_637e1d3b178209b1.png 480w, https://blog.ccino.org/p/ai-coding-ci-bottleneck-linear-2026/imgs/linear-gate-histogram_hu_2cfce6afe307e029.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Linear 官方数据，变更检测任务优化前后的耗时分布&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;195&#34;
		data-flex-basis=&#34;470px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;图片来自 &lt;a class=&#34;link&#34; href=&#34;https://linear.app/now/ci-bottleneck-reworked&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Linear 官方复盘&lt;/a&gt;。中位数从 26 秒降到 8 秒。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://blog.ccino.org/p/ai-coding-ci-bottleneck-linear-2026/imgs/ci-pipeline-gate.png&#34;
	width=&#34;1672&#34;
	height=&#34;941&#34;
	srcset=&#34;https://blog.ccino.org/p/ai-coding-ci-bottleneck-linear-2026/imgs/ci-pipeline-gate_hu_a8ac59c3bde23b8b.png 480w, https://blog.ccino.org/p/ai-coding-ci-bottleneck-linear-2026/imgs/ci-pipeline-gate_hu_26701527a21a712c.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;变更检测门禁卡住整条 CI 流水线的示意&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;177&#34;
		data-flex-basis=&#34;426px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;还有一处更隐蔽。他们的合并前检查里顺手写了一个缓存标记，结果 PR 测试全过了，还得在合并队列里等这个动作做完。把写入挪到一个不阻塞任何事的任务里，每个 API PR 的合并路径省了 42 秒。&lt;/p&gt;
&lt;p&gt;这类改动单独看都很小，加起来大约一分钟。对一天几十个 PR 的团队，一分钟乘以几十，再乘以每天，就不是小钱了。&lt;/p&gt;
&lt;h2 id=&#34;重复劳动是隐形大头&#34;&gt;重复劳动是隐形大头
&lt;/h2&gt;&lt;p&gt;CI 的账单里有一大块花在重复上。每个任务都要起 Runner、装依赖、准备环境，一套下来往往一两分钟。有些任务干的活，只有几秒。&lt;/p&gt;
&lt;p&gt;有几笔账，Linear 算得挺细。API 测试的每个分片，每次都用 apt 装一遍同样的 Postgres 客户端，7 到 8 秒。他们把它塞进基础镜像，这几秒就从每个分片里消失了。pnpm 装依赖，原来每次装整个 monorepo，要 44 到 73 秒；改成只装 API 包需要的部分，16 到 18 秒。&lt;/p&gt;
&lt;p&gt;缓存那笔账最反直觉。直觉里缓存总比重装快，他们试了缓存 node_modules，发现重装反而更快。缓存 key 挂在经常变动的 lockfile 上，命中一次也要 28 秒恢复，过滤安装只要 7.5 秒。缓存没省下时间。保存要成本，恢复看运气。别的仓库不一定也是这个结论，可缓存到底划不划算，得实测，不能靠感觉。&lt;/p&gt;
&lt;p&gt;七个各自只干几秒钟活的小检查，原来每个都独占一个 Runner，走一遍 checkout 和依赖安装。合并成两个任务以后，按 6 月用量算，一个月省下大约 87,000 个 Runner 分钟，占他们全部 CI 用量的 11.8%。&lt;/p&gt;
&lt;p&gt;数据库那边也一样。每个容器每次都重放完整的迁移历史，哪怕这个 PR 根本没碰 schema。换成加载生成好的 schema 快照，12 秒变成 1 到 2 秒。&lt;/p&gt;
&lt;h2 id=&#34;分片的算术&#34;&gt;分片的算术
&lt;/h2&gt;&lt;p&gt;想跑得快，直觉做法是多开几个分片。Linear 把 API 测试从 4 个分片加到 8 个，关键任务快了大约 19%。复盘里把这件事的前提讲得很清楚，分片数量翻倍，setup 的总时间也跟着翻倍。&lt;/p&gt;
&lt;p&gt;原来每个分片的启动成本是 110 到 140 秒，8 个分片光 setup 就要 15 到 19 分钟，比测试本身还长。把前面那些重复安装的问题解决掉，每个分片启动降到 40 秒左右，8 个分片的 setup 总和才第一次低于原来 4 个分片的开销。并行度不是白来的。先把每个分片的固定开销压下去，多开分片才开始划算。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://blog.ccino.org/p/ai-coding-ci-bottleneck-linear-2026/imgs/linear-sharding-chart.png&#34;
	width=&#34;3904&#34;
	height=&#34;1892&#34;
	srcset=&#34;https://blog.ccino.org/p/ai-coding-ci-bottleneck-linear-2026/imgs/linear-sharding-chart_hu_2596b680dd72e7ed.png 480w, https://blog.ccino.org/p/ai-coding-ci-bottleneck-linear-2026/imgs/linear-sharding-chart_hu_ff7c9d937818a082.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Linear 官方数据，分片从 4 个加到 8 个前后的 setup 总耗时对比&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;206&#34;
		data-flex-basis=&#34;495px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;图片来自 &lt;a class=&#34;link&#34; href=&#34;https://linear.app/now/ci-bottleneck-reworked&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Linear 官方复盘&lt;/a&gt;。启动成本压下来之后，8 个分片的 setup 反而少于原来 4 个。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://blog.ccino.org/p/ai-coding-ci-bottleneck-linear-2026/imgs/sharding-setup-cost.png&#34;
	width=&#34;1672&#34;
	height=&#34;941&#34;
	srcset=&#34;https://blog.ccino.org/p/ai-coding-ci-bottleneck-linear-2026/imgs/sharding-setup-cost_hu_69d379f9495a2bdf.png 480w, https://blog.ccino.org/p/ai-coding-ci-bottleneck-linear-2026/imgs/sharding-setup-cost_hu_2320fa0087260275.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;分片固定启动成本与并行收益的算术示意&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;177&#34;
		data-flex-basis=&#34;426px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Vitest 按文件分配任务，几个特别大的测试文件能拖住整个分片。Linear 把大文件拆小，又调了分片配置，一周后最慢的分片从 5.25 分钟降到 4.33 分钟。&lt;/p&gt;
&lt;h2 id=&#34;隔离放宽到哪一步代价谁担&#34;&gt;隔离放宽到哪一步，代价谁担
&lt;/h2&gt;&lt;p&gt;整个复盘里最大的一笔单项收益来自放宽测试隔离。Vitest 默认每个测试文件独立跑，各自的模块注册表都要重建。Linear 开了一个 opt-in 的项目配置，让声明安全的文件共享模块状态。最慢分片从 300 到 379 秒掉到 195 秒左右，API 分片的总 Runner 时间从每次 32.8 分钟降到 22 分钟。&lt;/p&gt;
&lt;p&gt;这也是正确性风险最高的一步。他们的做法是把资格写明白，每个参与共享的文件都要加显式标记，配套补上共享状态的清理。用了 fake timer 或者共享状态理不清的文件，留在原来的隔离项目里不动。&lt;/p&gt;
&lt;p&gt;复盘里有一句话我最在意。Linear 说，他们的大部分测试如今出自 Agent 之手，所以连给 Agent 用的技能文档也一并更新，让生成的测试默认遵守这套约束。工具的行为规范没法只写在人的脑子里，还得写进 Agent 能读到的规则里。团队改流程，等于同时给人和 Agent 改。&lt;/p&gt;
&lt;h2 id=&#34;普通团队能带走什么&#34;&gt;普通团队能带走什么
&lt;/h2&gt;&lt;p&gt;Linear 的具体手段没法直接照搬。仓库、工具链、Runner、用量，每家都不一样，缓存该不该用、分片开几个、隔离放不放宽，答案都会不同。能带走的是方法。&lt;/p&gt;
&lt;p&gt;想判断 CI 是否已经变成瓶颈，先记三个数。PR 从提交到反馈的平均等待，每天花在 Runner 上的分钟数，还有关键路径上最慢的那个门禁。有了一个月的走势，哪里在恶化一目了然，优化也才有对照。&lt;/p&gt;
&lt;p&gt;然后看关键路径。排在流水线最前面的任务，哪怕只慢十几秒，后面所有任务都得等它。把流程图画出来，找出所有「每个人都得等它」的节点，先动它们。没人等的任务，慢一点也不要紧。&lt;/p&gt;
&lt;p&gt;现在多了一个变量。代码产量的增长没有上限，验证资源的增长有预算上限。以前 CI 慢一点，忍一忍就过去了。现在一个 Agent 可以不停提 PR，CI 顶不住的时候，它不会自己停下来等，它只会继续往队列里塞。排队的人从工程师变成了工程师，外加一队不知疲倦的机器人。&lt;/p&gt;
&lt;p&gt;复盘结尾还有一笔反事实的账。年初要没动手，今天跑一遍测试要 11 分钟，差不多是现在的两倍。他们每周还在新增大约 2,000 个测试。这笔账没有终点，下个季度大概率还要再算一遍。&lt;/p&gt;
&lt;p&gt;验证系统接不住产量的时候，写代码再快，也只是把队列堆得更高。&lt;/p&gt;
&lt;h2 id=&#34;参考来源&#34;&gt;参考来源
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://linear.app/now/ci-bottleneck-reworked&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;AI coding has made CI a bottleneck, so we reworked ours to keep up（Linear 官方复盘）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://news.ycombinator.com/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Hacker News&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以上数据均来自 Linear 的公开自述，其环境为 TypeScript 单体仓库加特定第三方 Runner，数值结论不一定能迁移到其他仓库和工具链。&lt;/p&gt;
</description>
        </item>
        
    </channel>
</rss>
