Slide 1

Slide 1 text

CI/CDパフォーマンス改善の勘所 @mizchi at Offers Deep Dive

Slide 2

Slide 2 text

About https://x.com/mizchi Node.js とフロントエンドの専門家 120万*達成率で御社のフロントエ ンドの高速化をやります

Slide 3

Slide 3 text

Q: なんでCI/CD高速化するんだっけ? CI 待ち時間はあらゆる作業に対しての純粋なオーバーヘッドだから 人間がレビューを受ける回数増やすため レビュワーはCI通らないと見ない/見る必要ない デプロイ回数を増やすため(Four Keys) 直近のタスクを覚えておけず、フロー効率が下がるため 自分の本音: 15分超えたら仕事にならない

Slide 4

Slide 4 text

CI: 目標設定 ~5分 タスクに連続性が感じられる範囲 ~15分 休憩を挟みつつタスクを忘れずにおける範囲 それ以上 待ち時間に別作業をやり始める(ような圧を感じる) スイッチングコストでフロー効率が悪化

Slide 5

Slide 5 text

アプリケーション/モジュールの設計パターン 単一モジュール 利用される最小の利用単位 単一アプリケーション デプロイを伴うサーバー/クライアントアプリケーション モノレポ packages/* , apps/* 複数のモジュール/アプリケーションで構成。内部依存がある 同一リポジトリ内に複数のモノレポ 複雑。コンウェイ的な事情。大抵 shared/** な共有ライブラリもある 分割統治: コスパがいい単一ライブラリ+ユニットテストを最小単位で考える状態を作る

Slide 6

Slide 6 text

CIチューニング方針 目安: 単体テスト: 5分以内 / E2E 込み 15分以内 トップダウンに並列化 / ボトムアップにマイクロチューニング 過度に複雑にならない範囲で ワークフローを並列化する 前提が強いと再設計が大変で、腐りやすい。多少愚直なぐらいでいい モジュール単位のテスト自体に柔軟性がほしい。特にテストランナー設定 共通セットアップ(テンプレート)部分を強めにチューニング

Slide 7

Slide 7 text

CIログ読む 時間かかってる順に取り組む 分割されている場合、最後に終了するワークフローから逆算 alt text

Slide 8

Slide 8 text

共通セットアップ git checkout / setup-xxx clone 外部ライブラリのインストール(npm install) Docker? どれだけキャッシュしようがイメージの転送量速度ボトルネック CI 内は docker 消すのが一番手っ取り早いが... 全体 5m 達成するなら、目安 90s 以内に抑えたい

Slide 9

Slide 9 text

turborepo から学ぶこと https://turbo.build/ 依存関係からトポロジカルソート モジュール単位の (git) diff で変更範囲を判定 変更があったら下位モジュールのタスク実行/結果をキャッシュ diff がなければキャッシュを再利用 差分がなければ何も実行しないのが理想

Slide 10

Slide 10 text

モジュール境界の設計 モジュール抽出は担当責務の宣言 責務外の外部レイヤ(DB/サーバー/ブラウザ)に可能な限り干渉しない テスト駆動/DDD/クリーンアーキテクチャの発想が必要 逆に、テストが書きやすいコードを考えると、モジュール単位が決まる 結合テストは関連モジュールのセットアップだけでビルドできるようにしたい 単一アプリケーション内の utils/* が膨らんでいる場合、それはモジュール

Slide 11

Slide 11 text

E2E やるか問題 前提として、テストピラミッドで考える 第5回 テストピラミッド ~自動テストの信頼性を中長期的に保つ最適なバラ ンス~ | gihyo.jp E2E は高コスト低リターン 同じテストがユニットテストで書けるなら、ユニットテストで書く 導入に迷うぐらいなら、やるな E2E を CI に導入したら5分以内はほぼ無理 ユニットテストのカバレッジが80%超えてまだ不安だったら検証する

Slide 12

Slide 12 text

E2E(playwright/puppeteer) に手を染めたら考えること 導入/管理は高コストな反面、テストケースの追加は低コスト スモークテストは内部設計の理解なく書けてしまう 定期的にテストを棚卸しすること 「それユニットテストで良くない?」 安易なウェイト処理を行わない 適切なイベント駆動ならE2Eでも高速に動く waitFor(...) 最終的に金で並列化する(しかない) playwright test --shard=1/8 https://playwright.dev/docs/test-sharding @playwright/test はリトライや水平分割が良く出来てておすすめ

Slide 13

Slide 13 text

最後に: 結局素振りが一番大事 小さいリポジトリで素振りする 初期設計者以外は知見溜まりづらい 自分で同じ構成のものを素組しよう 過度に今の状態に最適化しない コード量や複雑さに応じてボトルネックは移動する 共通設定に強い前提があると、プロジェクト全体の健全性を損ねる E2E との付き合い方を決めること コツとしては、とにかくユニットテストに寄せる。E2E を増やさない

Slide 14

Slide 14 text

失敗集 git の履歴が膨らんで checkout だけで 2分超えた workspaces 未対応の npm@6 で npm link でハックしてたら再現できなくなった xargs で rollup をスレッド数だけ並列化したらM1を2台破壊した yarn v1 にしかない --modules-folder を使ったら他に再現できる環境がなくなっ た 型だけ定義する types モジュールを切り出したら循環参照が解決できなくなった nx の生成するボイラープレートが古くて負債を再生産し続けていた 共通セットアップの docker network 上のエンドポイントに全モジュールが依存して 剥がせない モジュール分割せずに新旧E2Eテストランナーを複数入れたら、 dependencies がデ ッドロック