Linear CI最適化:AI駆動開発のボトルネックを解消する

AI駆動のコーディングエージェントはコードを出荷する速度を飛躍的に向上させましたが、検証パイプラインはそれに追いついていません。Linearでは、この乖離により継続的インテグレーション(CI)が主要なボトルネックとなり、インフラストラクチャコストが増加し、開発者とエージェントの両方へのフィードバックが遅れていました。多層的な最適化戦略を実装することで、Linearはテストスイートが年初からほぼ4倍に増加したにもかかわらず、プルリクエストの待ち時間を6分強から5分強に短縮しました。

インフラストラクチャとツールのアップグレード

基盤となるハードウェアとコンパイラツールチェーンをアップグレードすることで、CIパイプラインのロジックを変更することなく、即座にパフォーマンスが向上します。

  • サードパーティランナー: ワークロードをGitHub Actionsから、より高速なCPU、高性能ストレージ、優れたキャッシュインフラストラクチャを備えたサードパーティランナーに移行した結果、ジョブ全体で平均34%の速度向上が見られ、tsc(TypeScriptコンパイラ)などの一部のワークロードでは52%向上しました。
  • ネイティブコンパイル: ネイティブTypeScriptコンパイラであるtsgoへの切り替えにより、tscチェックの週間中央値が73%削減され、型チェックがボトルネックではなくなりました。
  • ASTベースのリンティング: Linearはカスタムlintルールを書き直し、TypeScriptの型情報に依存する代わりに、抽象構文木(AST)に対する静的解析を使用するようにしました。これにより、ESLintは完全な型グラフなしで動作できるようになり、API lint時間が68%、リポジトリ全体のlint時間が55%削減されました。
  • Oxlint統合: Oxlintへの移行により、構文ベースのルールを処理する効率が向上し、リンティングに費やされるランナー時間の合計がさらに削減されました。

ゲーティングジョブの最適化

クリティカルパス上にあるジョブ(他のすべての作業が開始される前に完了する必要があるジョブ)は、CI全体の所要時間に不釣り合いな影響を与えます。

効率的な変更検出

Linearは、PRの差分に基づいて実行するテストを決定する初期ジョブを最適化しました:

  • フェッチ深度の制限: 変更検出ジョブのフェッチ深度を制限することで、最も遅いゲートが94秒から20秒に短縮されました。
  • ワーキングツリーの削除: 完全なワーキングツリーを必要としないジョブでは、チェックアウトを完全に削除し、実行時間を27秒から7秒に短縮しました。
  • スパースチェックアウト: コミットプッシュおよびマージキューイベントでは、履歴を制限したスパースでブロブレスなチェックアウトを使用することで、さらに11秒節約できました。

これらの変更により、変更検出ジョブの所要時間の中央値は26秒から8秒に短縮されました。

耐障害性とパス最小化

サードパーティランナーとGitHub間のネットワーク不安定性に対処するため、Linearはactions/checkoutを、バックオフ付きリトライを実装し、GIT_HTTP_LOW_SPEED_LIMITとGIT_HTTP_LOW_SPEED_TIMEを設定してハングを防ぐカスタムコンポジットアクションに置き換えました。さらに、キャッシュマーカーの書き込みなどの必須ではないタスクをクリティカルなマージパスから移動し、すべてのAPIプルリクエストから42秒を削減しました。

繰り返されるセットアップオーバーヘッドの削減

セットアップコスト(ランナーの起動、パッケージのインストール、依存関係のプロビジョニング)は、短命なジョブでは実際のテストよりも多くの時間を消費することがよくあります。

  • カスタムCIイメージ: Linearは共有依存関係(例:Postgresクライアント)をベースCIイメージに移動しました。これにより、シャードごとのaptインストール時間(7〜8秒)がなくなり、ネイティブビルドのヘッダーダウンロード中に発生する断続的なハングも防止されました。
  • フィルタリングされた依存関係のインストール: pnpmモノレポでは、APIテストワークフローがワークスペース全体ではなく、APIパッケージとその依存関係のみをインストールするように変更されました。これにより、pnpm install時間が44〜73秒から16〜18秒に短縮されました。
  • 再ビルドとキャッシュの比較: テストの結果、フィルタリングされたインストールによるnode_modulesの再ビルド(7.5秒)は、キャッシュからの復元(28秒)よりも高速であることが判明し、Linearはnode_modulesのキャッシュを廃止しました。
  • スキーマスナップショット: 毎回完全なデータベースマイグレーション履歴を再生する代わりに、APIコンテナは生成されたスキーマスナップショットとブートストラップファイルをロードするようになり、データベースセットアップが12秒から1〜2秒に短縮されました。
  • ジョブのバッチ処理: 7つの独立した短いチェックが、タスクを並行して実行する2つのジョブに統合されました。これにより、セットアップオーバーヘッドの頻度が減り、毎月約87,000ランナー分(CI使用量全体の11.8%)が節約されました。

テスト実行効率の向上

セットアップコストを最小限に抑えた後、Linearはワークフローの中で最も大きく頻繁な部分であるAPIスイートを積極的に並列化しました。

ワークロードのバランス調整

Vitestはテストの所要時間ではなくファイルごとに作業を分散するため、いくつかの大きなファイルがシャードのボトルネックになる可能性があります。Linearはこれらの大きなファイルをより小さく焦点を絞ったファイルに分割し、シャード数を4から8に増やしました。これにより、最も遅いシャードの所要時間が5.25分から4.33分に短縮されました。

モジュール状態のキャッシュ

isolate: falseを使用したオプトインのVitestプロジェクトを導入することで、Linearは安全なテストファイルが各ワーカー内でモジュールレジストリを共有できるようにしました。これにより、エンティティ、GraphQL、デコレータのグラフがファイルごとに冗長に再構築されるのを防ぎました。これは最大の単一改善であり、最も遅いシャードを約300〜379秒から195秒に短縮し、APIシャードのランナー時間を実行ごとに32.8分から22分に削減しました。

コミュニティの視点と反論

Linearの技術的アプローチはインフラストラクチャとパイプラインの効率に焦点を当てていましたが、コミュニティの議論ではより広範なシステム上の課題が強調されました:

  • AIテストの価値: 一部の貢献者は、テストスイートの4倍化が価値の比例的な増加を反映しているかどうか疑問視し、LLMが大量のボイラープレートまたは些細なテストを生成する可能性があると示唆しました。
  • シフトレフト検証: リンティングと単体テストをエージェントのローカルループ(フックやスキルを介して)に移動し、CIがリソース集約型のタスクのみを処理し、事前検証された候補を受け取るようにする提案がなされました。
  • 代替ツール: 多くの開発者が、積極的なキャッシュと依存関係グラフを使用して大規模プロジェクトのビルド時間を約10秒に達成するBazelやGrogなどのビルドシステムを提唱しました。
  • 移動するボトルネック: エンジニアの間で共通する見解は、CIを高速化すると、ボトルネックがさらに下流の人間によるコードレビュー、デプロイ、ロールバックプロセスに移るだけだということです。

「CIを高速化しても、ボトルネックがレビューとデプロイに移るだけだ。エージェントはキューを長くしただけだ——彼らが発明したわけではない。」

Sources

関連