AIコーディングでテスト4倍、LinearはCIの何を削ったか

gen
gen

@gennnn ・ 38本

サムネイル

AIコーディングでPRを出す速度は上がったのに、CIの緑待ちで全部まとめて止まっている。そんなボトルネック、身に覚えはありませんか?

Linearが2026年9月21日に公開したCI改修の記録は、この詰まりへの答えとしてかなり具体的でした。施策を「少人数チームでも今日削れるもの」と「規模が要るもの」に仕分けたので、自分のCIのどこから手を付けるか決める材料にしてください。

テストが4倍でも、LinearのCI待ちは短くなった

Linearはブログの冒頭で、エージェントのおかげでコードを出す速度は跳ね上がったのに、変更を検証する側が同じペースで追いついていないと書いています。テストスイートは年初からほぼ4倍に膨らみ、今ではテストの大半をエージェントが書いているそうです。

普通なら、CIはテストの量に比例して遅くなります。ところがPRの待ち時間は6分超から5分強に縮み、テスト1件あたりのランナー時間はほぼ半分になりました。4倍に増えた荷物を、前より短い時間で運んでいる計算です。これ、マジでヤバい数字です。

CIの施策、少人数チームで真似できるのはどれか?

魔法の一手はなく、遅い箇所を1つずつ潰した積み上げです。Linearの施策と数字を、少人数チームで真似しやすいかどうかで並べました。

施策
Linearでの効果
少人数チームでは
型チェックをネイティブ版コンパイラ tsgo に切り替え
tsc チェックの週次中央値が73%減
今日できる
型情報に頼るESLintの自作ルールを構文解析だけで書き直し
API側のlintが68%減、リポジトリ全体のlintが55%減
ルールの数しだいで数日
セットアップの見直し3点
シャードあたり110〜140秒が67〜73秒に、約44%減
今日できる
差分検出ジョブのfetch深さ制限とキャッシュマーカーの分離
最も遅いゲートが94秒から20秒、マージ経路で42秒短縮
今日できる
短いチェック7本を2ジョブに統合
月に約87,000ランナー分、CI使用量全体の11.8%を削減
ジョブが多いなら効く
Vitestのシャードを4から8へ
初期ベンチでクリティカルなジョブが約19%速く、約19%安く
テストが増えてから
テストファイル間でモジュールを共有する設定
月間コスト約17%減、APIシャードの合計が1回32.8分から22分に
効くけれど危ない
GitHub Actionsからサードパーティのランナーへ移行
ジョブが平均34%高速化、tsc は52%短縮
予算と運用しだい

あなたのCIで一番長いステップはどれでしょう? 「今日できる」の行に当てはまるものが、最初の一手です。ランナー移行は効果こそ大きいものの、移行先も費用もブログに書かれておらず、小さなチームが真っ先に手を出す場所ではないと僕は見ています。

tsgoとESLint、型の重さは今日から削り始められる

tsgo はTypeScriptのコンパイラをGoで書き直したネイティブ版です。Linearは型チェックをこれに切り替えて週次中央値を73%削り、ボトルネックが型チェックから完全に外れました。当時は tsgo という別コマンドでしたが、2026年7月8日にTypeScript 7.0として正式リリースされ、今は通常の typescript パッケージの tsc がそのままネイティブ版です。

lintのほうは、自作ルールの一部が関数のような構文やガードのパターンを見分けるために型情報を使っていました。そのせいでlintのたびに型のグラフを丸ごと組み立てる必要があり、CIでも特にメモリを食うジョブだったそうです。ルールを抽象構文木の静的解析だけで判定するよう書き直すと、ESLintがTypeScriptに依存しなくなり、API側のlintは68%、リポジトリ全体のlintは55%短くなりました。2つの数字は範囲ではなく、測った対象が違う別の指標です。

構文だけで動くルールは移植しやすく、そのあとのOxlintへの移行も楽になったと書かれています。

TypeScript 7.0は安定したプログラム向けAPIをまだ公開しておらず、typescript-eslintのようにコンパイラへアクセスするツールは7.1待ちだと公式が書いています。型情報つきのlintを減らしておけば、TS7へ移る足かせも1つ減るというのが僕の見立てです。

あなたの設定で、parserOptions.projectService や project を前提にしたルールはいくつ動いていますか? 型情報がなくても書けそうなものが、書き換え候補です。

セットアップとクリティカルパスは、ジョブが少なくてもCIに効く

各シャードが毎回払う固定費も削っています。APIのシャードは実行のたびにaptで同じPostgresクライアントを入れて7〜8秒使っていたので、CIのベースイメージに焼き込みました。pnpmのワークスペースでは、インストール対象をAPIパッケージに絞って44〜73秒を16〜18秒にしています。

一番の罠は node_modules のキャッシュでした。ヒットしても復元に約28秒かかり、対象を絞ったインストールなら約7.5秒で済んだので、キャッシュのほうを外しています。3点合わせて、シャードあたりのセットアップは約44%減りました。

キャッシュ復元に何秒かかっているか、見たことはありますか? GitHub Actionsのログにはステップごとの所要時間が出るので、restoreとinstallの秒数を並べれば判断できます。

マージまでの経路では、変更検出を担うゲートジョブでgitのfetch深さに上限を設け、最も遅いものを94秒から20秒にしました。作業ツリーが要らないジョブからはcheckoutそのものを外して27秒を7秒に。マージ前の最終チェックに相乗りしていたキャッシュマーカーの書き込みを切り離し、APIのPRとマージキューのエントリごとに42秒縮めています。短い独立チェック7本を2ジョブにまとめて中で並行実行する変更も、セットアップの重複を減らしてCI使用量全体の11.8%を浮かせました。

Vitestのモジュール共有は、一番効いて一番危ない

Vitestのシャードは今年の早い時期に3から4へ、今回さらに8へ増やしました。ただしブログには、シャードを増やして得をするのは1シャードあたりの固定費が低いときだけだとあります。倍にすればセットアップの時間も倍かかるので、前のセクションの削減とセットで効く施策です。

単独で最大の効果を出したのは、テストファイル間でモジュールを共有させる設定でした。Vitestは既定でテストを分離された環境で実行します(isolate オプション、既定値は true)。Linearは安全なファイルだけを集めた isolate: false のプロジェクトをオプトインで用意し、ワーカー内でモジュールのレジストリを共有させました。月間コストで約17%の削減、最も遅いシャードは300〜379秒から約195秒へ、APIシャードの合計ランナー時間は1回あたり約32.8分から22分に縮んでいます。

一方で、Linear自身がこれを正しさのリスクが最も高い施策だと書いています。共有された状態が別のテストに漏れると、単体では通るのに一緒に走らせると落ちる、という厄介な壊れ方をしかねません。Linearは対象ファイルすべてにオプトインのコメントを明示させ、共有状態の後始末も足しました。リポジトリ全体に一括で isolate: false を当てるのは罠です。

AIエージェントが書くテストに、CIの制約を渡す

個人的に一番アツいのはここです。テストの大半をエージェントが書くLinearは、社内のエージェント向けスキルも更新し、生成されるテストが最初からこのオプトインの制約に従うようにしました。

人間がCIを速くしても、エージェントが制約を知らないテストを量産したら元に戻ります。スキルの中身は公開されていないので、以下は CLAUDE.md や AGENTS.md に足すならこう書く、という僕の例です。

## テストを書くときのルール
- モジュールのトップレベルに可変の状態を置かない
- グローバルやシングルトンを書き換えたら afterEach で元に戻す
- 共有プロジェクトに入れるファイルは、先頭のオプトインコメントを必ず付ける

今日やるなら、順番はこうです。

  1. CIのログでステップごとの秒数を見て、キャッシュ復元とインストールを比べる
  2. 型チェックをTypeScript 7.0の tsc に切り替える(型情報つきlintを使うなら6.0と併用する)
  3. 型情報を前提にしたlintルールを数える
  4. エージェントの指示書に、テストの書き方の制約を足す

あなたのCIで一番遅い1ステップ、今日のうちに見つけてみませんか?