🚀

「創る」ことに熱中するために。持続可能なCI/CDパイプラインの設計思想

に公開

はじめに

現代のWeb開発において、CI/CD(継続的インテグレーション/継続的デリバリー)は開発プロセスの「心臓部」です。しかし、ただツールを導入するだけでは、「ビルド時間が長すぎて待たされる」「E2Eテストが不安定でCIが落ちる」といった問題が発生し、かえって開発体験(DX)を損なうことになりかねません。

私は、エンジニアが本来の価値提供である「創る」ことにフルコミットできる環境を作るため、CI/CDパイプラインの最適化に徹底的にこだわっています。本記事では、私が開発・運用している Next.js Amplify Starter Kit に実装されている、実践的なCI/CDの設計思想と技術的な詳細をご紹介します。

全体アーキテクチャ

まずは、私が採用しているCI/CDパイプラインの全体像をご覧ください。

特徴は、「無駄な実行を省く賢い分岐」「多層的な品質チェック」 です。GitHub Actionsを中心に、AWS CDKによるインフラ管理、そしてローカル実行を前提としたE2Eテストを組み合わせています。

1. 時間とリソースを浪費しない「賢い条件分岐」

GitHub Actionsは従量課金(または特定枠内の無料分)であることが多いため、無駄なジョブ実行はコストと時間の浪費に直結します。私は以下の2つの手法で最適化しています。

動的な変更検知によるジョブ制御

ドキュメントの修正や、アプリに関係のないインフラコードの変更時に、アプリのビルドジョブを走らせる必要はありません。
本プロジェクトでは、git diff を用いた変更検知ジョブ(changes)を最初に実行し、その結果に基づいて後続のジョブを動的にスキップする構成にしています。

github/workflows/ci.yml
jobs:
  changes:
    runs-on: ubuntu-latest
    outputs:
      apps: ${{ steps.filter.outputs.apps }}
      infra: ${{ steps.filter.outputs.infra }}
    steps:
      - name: Detect Changes
        id: filter
        run: |
          # 変更ファイルを検知してフラグを設定
          CHANGED_FILES=$(git diff --name-only $BASE_SHA $HEAD_SHA)

          if echo "$CHANGED_FILES" | grep -qE '^apps/|^packages/'; then
            echo "apps=true" >> $GITHUB_OUTPUT
          fi
          # ... (判定ロジック)

  build:
    needs: changes
    # アプリ関連のファイル変更があった場合のみ実行
    if: ${{ needs.changes.outputs.apps == 'true' }}
    steps:
      - run: pnpm build

これにより、必要なパイプラインだけが実行され、リソースの節約と完了時間の短縮を実現しています。

Concurrencyによる無駄な並列実行の排除

プルリクエストに対して連続でコミットをプッシュした際、古いコミットに対するCIジョブは結果を待つだけ無駄です。 concurrency グループを設定し、新しいコミットが来たら自動的に古い実行をキャンセルするようにしています。

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

この1行を入れるだけで、開発のフィードバックループが劇的に速くなります。

2. インフラもコードで管理し、自動検知する

アプリケーションコードとインフラ(AWS)は密接に関係しています。本プロジェクトではAWS CDKを採用し、インフラもTypeScriptで管理していますが、CI/CDにおいてもそれらを統合しています。

apps/ ディレクトリ(アプリコード)だけでなく、 infra/ ディレクトリ(CDKコード)に変更があった場合も検知し、自動的に cdk deploy を実行するフローを組んでいます。
これらはすべて ci.yml という1つのワークフローファイル内で完結しており、アプリとインフラの連携をスムーズに管理しています。

github/workflows/ci.yml
# (実際の実装に近い形での抜粋)
jobs:
  deploy-infra:
    name: Deploy Infrastructure
    needs: cdk-check
    # mainブランチかつインフラ変更があった場合のみ実行
    if: github.ref == 'refs/heads/main' && needs.changes.outputs.infra == 'true'
    steps:
      # ... (AWS認証など)
      - name: CDK Deploy
        working-directory: infra
        run: pnpm cdk deploy --require-approval never

これにより、「コードはマージされたけどインフラ反映を忘れてエラーになった」というありがちなミスを撲滅しました。

3. 開発者を作業員にしない「完全自動化」

ライブラリのアップデート作業は、地味ですが重要なタスクです。これを手動で行うのは生産的ではありません。
Dependabotを導入し、npm, pip, github-actions の依存関係を毎日チェックしています。

さらに、GitHub CLI (gh) を活用して、リリースノートの自動生成やタグ打ちもスクリプト化しています。
「リリース作業もCLIで完結させる」ことで、GUIでポチポチする手間とミスを排除し、快適な開発者体験を提供しています。

# リリースノート作成とタグ打ちの自動化例
gh release create v2.2.5 --generate-notes

エンジニアがやるべきは「意思決定(マージボタンを押す)」だけであり、単純作業はすべてBotに任せる方針です。

まとめ

このCI/CDパイプラインは、「スピード」と「品質」、そして「開発者の幸福」 を最大化するように設計されています。

  1. 必要な処理だけを走らせる (Paths/Concurrency)
  2. インフラもアプリも一元管理・自動デプロイ (CDK Integration)

一方で、E2Eテストについてはリリースの安全性と開発スピードのバランスを考慮し、「CIパイプラインからは意図的に除外し、ローカルやNightly実行に切り出す」 戦略をとっています。
不安定なテストで開発のリズムを止めるのではなく、確実な環境で実行することで「急がば回れ」の品質保証を実現しています。

これらの知見は、オープンソースとして公開している Next.js Amplify Starter Kit に実装されています。
「環境構築やデプロイ周りの面倒事から解放されたい」方は、ぜひコードを覗いてみてください。

これからも、「創る」ことに熱中できる世界を目指して、開発基盤を磨き続けていきます。

Discussion