CI/CD概念
継続的インテグレーション(CI)、継続的デリバリー(CD)、継続的デプロイメントの3段階を理解し、信頼性の高いパイプラインを設計する
CI/CD概念
継続的インテグレーション(CI)、継続的デリバリー(CD)、継続的デプロイメントの3段階を理解し、信頼性の高いパイプラインを設計する
この章で学ぶこと
- CI/CD/CDeploy の違いと段階的な導入アプローチを理解する
- パイプライン設計の原則とステージ構成を習得する
- ブランチ戦略とCI/CDの統合パターンを把握する
- テスト戦略とCI/CDの連携方法を実践できる
- モノレポ・マイクロサービスにおけるCI/CD設計パターンを理解する
前提知識
このガイドを読む前に、以下の知識があると理解が深まります:
- 基本的なプログラミングの知識
- 関連する基礎概念の理解
- DevOps概要 の内容を理解していること
1. CI/CD の3段階
1.1 全体像
コード変更 → CI → CD (デリバリー) → CD (デプロイメント)
+--------+ +------------------+ +-------------------+ +------------------+
| 開発者 | → | CI | → | CD (デリバリー) | → | CD (デプロイ) |
| コード | | ビルド・テスト | | ステージング反映 | | 本番自動反映 |
| 変更 | | 自動実行 | | 承認待ち | | 自動実行 |
+--------+ +------------------+ +-------------------+ +------------------+
^
手動承認ゲート
(デリバリーの場合)
1.2 継続的インテグレーション (CI)
開発者がコードを頻繁に(1日数回)メインブランチに統合し、その都度自動でビルドとテストを実行するプラクティス。Martin Fowler が2006年に体系化した概念で、「統合の地獄(Integration Hell)」を回避することが最大の目的である。
CIの核心原則:
- コードは1日に1回以上メインブランチに統合する
- 全てのコミットに対してビルドとテストが自動実行される
- ビルドが壊れたら最優先で修復する(10分ルール)
- テストは高速で信頼性が高くなければならない
# CI パイプラインの基本例 (GitHub Actions)
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Type check
run: npm run type-check
- name: Unit tests
run: npm test -- --coverage
- name: Build
run: npm run buildCIの導入チェックリスト:
□ ソースコードが単一のリポジトリで管理されている
□ ビルドが自動化されている(1コマンドでビルド可能)
□ テストスイートが存在し、自動実行される
□ コミットごとにCIが実行される設定がある
□ CIの結果が開発者に即座に通知される
□ ビルド失敗時の修復フローが確立されている
□ 10分以内にCIが完了する
□ メインブランチは常にグリーン(ビルド成功)状態を維持する
1.3 継続的デリバリー (Continuous Delivery)
CIに加え、リリース可能なアーティファクトを自動生成し、ステージング環境にデプロイする。本番デプロイは手動承認を経て実行。Jez Humble と David Farley が2010年に著書で定義した概念で、「いつでも安全にリリースできる状態」を常に維持することが目標である。
継続的デリバリーの原則:
- ソフトウェアは常にリリース可能な状態を維持する
- リリースはビジネス判断であり、技術的判断ではない
- デプロイプロセスは完全に自動化されている
- 本番デプロイと同一のプロセスが全環境で使用される
# 継続的デリバリーの例
name: Continuous Delivery
on:
push:
branches: [main]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm test && npm run build
- uses: actions/upload-artifact@v4
with:
name: build-output
path: dist/
deploy-staging:
needs: build-and-test
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/download-artifact@v4
with:
name: build-output
- run: ./scripts/deploy.sh staging
# ステージング環境でのスモークテスト
smoke-test-staging:
needs: deploy-staging
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run smoke tests against staging
run: |
npm ci
ENVIRONMENT=staging npm run test:smoke
env:
BASE_URL: https://staging.example.com
deploy-production:
needs: smoke-test-staging
runs-on: ubuntu-latest
environment:
name: production
# 手動承認が必要(GitHub の Environment Protection Rules)
steps:
- uses: actions/download-artifact@v4
with:
name: build-output
- run: ./scripts/deploy.sh production1.4 継続的デプロイメント (Continuous Deployment)
全てのテストに通過した変更が、人間の介入なしに本番環境へ自動デプロイされる。これは継続的デリバリーの延長であり、最も成熟したCI/CDの形態である。Facebook、Netflix、Etsy、GitHub自身がこの手法を採用している。
継続的デプロイメントの前提条件:
- 高いテストカバレッジ(ライン・ブランチともに80%以上)
- 包括的な自動テスト(ユニット、統合、E2E、パフォーマンス)
- 自動化されたロールバック機構
- カナリーデプロイまたはBlue-Greenデプロイの導入
- Feature Flag による機能の段階的公開
- 充実した監視・アラート(SLO/SLI ベース)
- 組織全体のリスク許容度と文化的成熟度
継続的デプロイメントのフロー:
Push → Build → Unit Test → Integration Test → E2E Test
→ Security Scan → Stage Deploy → Smoke Test
→ Canary Deploy (5%) → メトリクス監視 (15分)
→ Progressive Rollout (25% → 50% → 100%)
→ 異常検知時は自動ロールバック
# 継続的デプロイメントの実装例 (Kubernetes + ArgoCD 連携)
name: Continuous Deployment
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run test:all -- --coverage
- name: Check coverage threshold
run: |
COVERAGE=$(cat coverage/coverage-summary.json | jq '.total.lines.pct')
if (( $(echo "$COVERAGE < 80" | bc -l) )); then
echo "Coverage $COVERAGE% is below 80% threshold"
exit 1
fi
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Trivy vulnerability scan
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
severity: 'CRITICAL,HIGH'
exit-code: '1'
build-and-push:
needs: [test, security-scan]
runs-on: ubuntu-latest
outputs:
image-tag: ${{ steps.meta.outputs.tags }}
steps:
- uses: actions/checkout@v4
- name: Build and push Docker image
id: meta
uses: docker/build-push-action@v5
with:
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
update-manifests:
needs: build-and-push
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
repository: myorg/k8s-manifests
token: ${{ secrets.MANIFEST_REPO_TOKEN }}
- name: Update image tag
run: |
cd overlays/production
kustomize edit set image my-app=ghcr.io/${{ github.repository }}:${{ github.sha }}
- name: Commit and push
run: |
git config user.name "ci-bot"
git config user.email "ci@example.com"
git add .
git commit -m "chore: update my-app to ${{ github.sha }}"
git push
# ArgoCD が変更を検知して自動同期 → カナリーデプロイ1.5 3段階の段階的導入戦略
多くの組織は一足飛びに継続的デプロイメントに到達できない。以下の段階的アプローチが現実的である。
Phase 1: CI の確立 (1-3ヶ月)
├── 自動ビルドの設定
├── ユニットテストの整備 (カバレッジ 60% 目標)
├── Lint / 静的解析の導入
└── PR レビュープロセスの確立
Phase 2: 継続的デリバリーの確立 (3-6ヶ月)
├── ステージング環境の自動デプロイ
├── 統合テスト・E2Eテストの整備 (カバレッジ 80% 目標)
├── Environment Protection Rules の設定
├── デプロイ手順の自動化
└── ロールバック手順の確立
Phase 3: 継続的デプロイメントの確立 (6-12ヶ月)
├── カナリーデプロイの導入
├── 自動ロールバック機構の実装
├── SLO/SLI ベースの監視
├── Feature Flag の導入
└── 文化的な変革 (失敗を許容する文化)
2. CI/CDの違い比較
2.1 3段階の比較表
| 項目 | CI | CD (デリバリー) | CD (デプロイメント) |
|---|---|---|---|
| 自動ビルド | はい | はい | はい |
| 自動テスト | はい | はい | はい |
| ステージングデプロイ | 任意 | 自動 | 自動 |
| 本番デプロイ | 手動 | 手動承認後自動 | 完全自動 |
| リスク | 低 | 中 | 高(要成熟度) |
| 前提条件 | テスト基盤 | CI + 環境管理 | CD + 高いテストカバレッジ |
| 適するチーム | 全チーム | CI成熟チーム | CD成熟チーム |
| リリース頻度 | - | 週1-2回 | 日に数回〜数十回 |
| フィードバック速度 | 分 | 時間 | 分 |
| 導入期間 | 1-3ヶ月 | 3-6ヶ月 | 6-12ヶ月 |
2.2 パイプラインツール比較
| ツール | ホスティング | 設定形式 | 特徴 | 適用場面 |
|---|---|---|---|---|
| GitHub Actions | SaaS (GitHub) | YAML | GitHub統合、マーケットプレイス | GitHub利用プロジェクト |
| GitLab CI | SaaS / Self-hosted | YAML | GitLab統合、Auto DevOps | GitLab利用プロジェクト |
| CircleCI | SaaS | YAML | 高速、Docker最適化 | パフォーマンス重視 |
| Jenkins | Self-hosted | Groovy/YAML | 高い拡張性、プラグイン | エンタープライズ |
| Dagger | ローカル / CI | CUE/Go/Python | ポータブル、ローカル再現 | マルチCI環境 |
| AWS CodePipeline | SaaS (AWS) | JSON/YAML | AWS統合、CodeBuild連携 | AWS中心のプロジェクト |
| Azure DevOps | SaaS / Self-hosted | YAML | Azure統合、Boards連携 | Microsoft エコシステム |
| Buildkite | SaaS + Self-hosted | YAML | 高スケーラビリティ | 大規模組織 |
2.3 ツール選定のディシジョンツリー
CI/CD ツール選定:
GitHub を使っている?
├── Yes → GitHub Actions (第一選択)
│ ├── セルフホステッドランナーが必要? → Actions + Self-hosted Runner
│ └── AWS デプロイが中心? → Actions + aws-actions/*
└── No → GitLab を使っている?
├── Yes → GitLab CI/CD
└── No → エンタープライズ要件?
├── Yes → Jenkins / Azure DevOps
└── No → マルチCI環境が必要?
├── Yes → Dagger
└── No → CircleCI / Buildkite
3. パイプライン設計原則
3.1 パイプラインステージの標準構成
+-------+ +------+ +------+ +--------+ +--------+ +--------+
| Lint | → | Build | → | Test | → | Scan | → | Stage | → | Prod |
| 静的 | | 構築 | | 検証 | | 脆弱性 | | 環境 | | 環境 |
| 解析 | | | | | | 検査 | | デプロイ| | デプロイ|
+-------+ +------+ +------+ +--------+ +--------+ +--------+
~10s ~30s ~2min ~1min ~3min ~3min
← 高速フィードバック → ← 信頼性確保 →
(失敗は早く検知して即座に修正) (段階的に本番に近づける)
3.2 フィードバック速度の原則
# パイプライン最適化: 高速フィードバックファースト
pipeline_stages = {
"lint": {"time": "10s", "fail_rate": "20%", "order": 1},
"type_check": {"time": "15s", "fail_rate": "10%", "order": 2},
"unit_test": {"time": "60s", "fail_rate": "15%", "order": 3},
"build": {"time": "30s", "fail_rate": "5%", "order": 4},
"integration_test":{"time": "180s", "fail_rate": "8%", "order": 5},
"e2e_test": {"time": "300s", "fail_rate": "5%", "order": 6},
"security_scan": {"time": "60s", "fail_rate": "3%", "order": 7},
}
# 原則: 失敗率の高いステージを先に実行
# 原則: 実行時間の短いステージを先に実行
# 原則: 独立したステージは並列実行フィードバック速度の目標値:
| フェーズ | 目標時間 | 含まれる処理 |
|---|---|---|
| ローカルチェック | 30秒以内 | Lint、フォーマット |
| PR CI | 10分以内 | ビルド、テスト、静的解析 |
| ステージングデプロイ | 15分以内 | ビルド、テスト、デプロイ |
| 本番デプロイ | 30分以内 | 全テスト、承認、デプロイ、検証 |
3.3 並列化とキャッシュ
# 並列化によるパイプライン高速化
name: Optimized CI
on: [push]
jobs:
# Phase 1: 独立したジョブを並列実行
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run lint
type-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run type-check
unit-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm test -- --coverage
- name: Upload coverage
uses: actions/upload-artifact@v4
with:
name: coverage
path: coverage/
# Phase 2: Phase 1 全てが成功したら実行
build:
needs: [lint, type-check, unit-test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci && npm run build
- uses: actions/upload-artifact@v4
with:
name: build-output
path: dist/
# Phase 3: E2E テスト (マトリクスで並列化)
e2e-test:
needs: build
runs-on: ubuntu-latest
strategy:
matrix:
shard: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npx playwright install --with-deps
- run: npx playwright test --shard=${{ matrix.shard }}/4
- uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-report-${{ matrix.shard }}
path: playwright-report/
# Phase 4: セキュリティスキャン (Phase 2 と並列実行可能)
security-scan:
needs: [lint, type-check, unit-test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Snyk
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}3.4 パイプラインのメトリクス
パイプラインの健全性を測定するために以下のメトリクスを追跡する。
CI/CD メトリクス (DORA メトリクス):
1. デプロイ頻度 (Deployment Frequency)
エリート: オンデマンド(1日複数回)
高: 1日1回〜週1回
中: 週1回〜月1回
低: 月1回未満
2. リードタイム (Lead Time for Changes)
エリート: 1時間未満
高: 1日〜1週間
中: 1週間〜1ヶ月
低: 1ヶ月超
3. 変更失敗率 (Change Failure Rate)
エリート: 0-15%
高: 16-30%
中: 16-30%
低: 46-60%
4. 復旧時間 (Time to Restore Service)
エリート: 1時間未満
高: 1日未満
中: 1日〜1週間
低: 1週間超
# CI メトリクスの収集例
name: CI Metrics
on:
workflow_run:
workflows: ["CI"]
types: [completed]
jobs:
collect-metrics:
runs-on: ubuntu-latest
steps:
- name: Calculate CI duration
uses: actions/github-script@v7
with:
script: |
const run = context.payload.workflow_run;
const duration = (new Date(run.updated_at) - new Date(run.created_at)) / 1000;
const status = run.conclusion;
// メトリクスをDatadog等に送信
console.log(`CI Duration: ${duration}s, Status: ${status}`);
console.log(`Branch: ${run.head_branch}`);
console.log(`Commit: ${run.head_sha}`);4. ブランチ戦略とCI/CD
4.1 トランクベース開発
main ─────●────●────●────●────●────●── (常にデプロイ可能)
│ │ │ │ │ │
└─●──┘ └─●──┘ └─●──┘
短命ブランチ 短命ブランチ 短命ブランチ
(数時間〜1日) (数時間〜1日) (数時間〜1日)
特徴:
- main ブランチが常にデプロイ可能
- フィーチャーブランチは短命(最大1-2日)
- Feature Flag で未完成機能を隠す
- 継続的デプロイメントと相性が良い
トランクベース開発のCI/CD設定:
# トランクベース開発向けCI/CD
name: Trunk-Based CD
on:
push:
branches: [main]
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run lint
- run: npm test -- --coverage
- run: npm run build
deploy:
needs: ci
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy with feature flags
run: |
# Feature Flag の状態を反映してデプロイ
./scripts/deploy.sh \
--image ghcr.io/myorg/app:${{ github.sha }} \
--feature-flags-config flags.json
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}4.2 GitHub Flow
main ─────●─────────●─────────●──────── (保護ブランチ)
│ ↑ │
└──●──●──PR──merge──┘
feature/xxx
(PR レビュー必須)
特徴:
- シンプルなブランチモデル
- PR ベースのコードレビュー
- CI が PR 上で実行される
- main マージ後にデプロイ
4.3 Git Flow
main ───────────────●──────────────●──── (リリースタグ)
↑ ↑
develop ──●──●──●───┤──●──●──●────┤──── (統合ブランチ)
│ │ │ │ │ │ │ │
└──┘ │ │ └──┘ │ │
feature │ │ feature│ │
│ │ │ │
release │ release │
│ │ │ │
└──→───┘ └──→┘
特徴:
- 複雑だがリリース管理に厳密
- main, develop, feature, release, hotfix ブランチ
- 長期メンテナンスのソフトウェアに適する
- CI/CD との統合は複雑になりがち
# Git Flow 向けCI/CD
name: Git Flow CI/CD
on:
push:
branches: [main, develop, 'release/**', 'hotfix/**']
pull_request:
branches: [main, develop]
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm test && npm run build
deploy-dev:
if: github.ref == 'refs/heads/develop'
needs: ci
runs-on: ubuntu-latest
environment: development
steps:
- run: ./deploy.sh development
deploy-staging:
if: startsWith(github.ref, 'refs/heads/release/')
needs: ci
runs-on: ubuntu-latest
environment: staging
steps:
- run: ./deploy.sh staging
deploy-production:
if: github.ref == 'refs/heads/main'
needs: ci
runs-on: ubuntu-latest
environment: production
steps:
- run: ./deploy.sh production4.4 ブランチ戦略の比較
| 項目 | トランクベース | GitHub Flow | Git Flow |
|---|---|---|---|
| 複雑さ | 低 | 低 | 高 |
| ブランチ数 | 最小 | 少 | 多 |
| リリース頻度 | 非常に高い | 高い | 中〜低 |
| Feature Flag 必要度 | 高 | 中 | 低 |
| CI/CD との相性 | 最高 | 良好 | 複雑 |
| 適用場面 | SaaS、Web | 一般的なプロジェクト | パッケージ、ライブラリ |
| チーム規模 | 小〜大 | 小〜中 | 中〜大 |
5. テスト戦略
5.1 テストピラミッド
/\
/ \
/ E2E \ 少数・高コスト・遅い
/------\
/ 統合 \ 中程度
/テスト \
/------------\
/ ユニット \ 多数・低コスト・速い
/ テスト \
/------------------\
推奨比率:
Unit : Integration : E2E = 70 : 20 : 10
5.2 各テストレベルの特徴
| テストレベル | 実行時間 | カバレッジ目標 | テスト対象 | ツール例 |
|---|---|---|---|---|
| Unit | ミリ秒〜秒 | 80%+ | 関数、クラス | Jest, Vitest, pytest |
| Integration | 秒〜分 | 60%+ | API、DB連携 | Supertest, TestContainers |
| E2E | 分〜十分 | 主要フロー | ユーザーシナリオ | Playwright, Cypress |
| Performance | 分〜時間 | SLO目標 | レスポンスタイム | k6, Artillery |
| Security | 分 | 脆弱性ゼロ | 依存関係、コード | Snyk, Trivy, tfsec |
5.3 CI でのテスト実行例
# テストピラミッドに基づくCI設定
name: Test Pipeline
on: [push]
jobs:
unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run test:unit -- --coverage --coverageThreshold='{"global":{"branches":80}}'
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true
integration-tests:
needs: unit-tests
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_DB: testdb
POSTGRES_PASSWORD: test
ports: ['5432:5432']
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis:7
ports: ['6379:6379']
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run test:integration
env:
DATABASE_URL: postgresql://postgres:test@localhost:5432/testdb
REDIS_URL: redis://localhost:6379
e2e-tests:
needs: integration-tests
runs-on: ubuntu-latest
strategy:
matrix:
shard: [1, 2, 3]
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npx playwright install --with-deps
- run: npm run test:e2e -- --shard=${{ matrix.shard }}/3
- uses: actions/upload-artifact@v4
if: failure()
with:
name: e2e-results-${{ matrix.shard }}
path: |
playwright-report/
test-results/
performance-tests:
needs: integration-tests
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Run k6 load tests
uses: grafana/k6-action@v0.3.1
with:
filename: tests/performance/load-test.js
env:
K6_CLOUD_TOKEN: ${{ secrets.K6_CLOUD_TOKEN }}5.4 テスト品質の指標
# Mutation Testing でテストの品質を測定
name: Mutation Testing
on:
schedule:
- cron: '0 3 * * 1' # 毎週月曜 AM3時
jobs:
mutation:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- name: Run Stryker Mutation Testing
run: npx stryker run
- name: Check mutation score
run: |
SCORE=$(cat reports/mutation/mutation.json | jq '.schemaVersion' )
echo "Mutation Score: $SCORE%"
# 70% 以上を要求6. モノレポにおけるCI/CD
6.1 モノレポのCI/CD課題
モノレポの構成例:
monorepo/
├── packages/
│ ├── web/ # フロントエンド
│ ├── api/ # バックエンド
│ ├── shared/ # 共通ライブラリ
│ └── mobile/ # モバイルアプリ
├── package.json
└── turbo.json
課題:
- 全パッケージのCI実行は時間がかかりすぎる
- 変更されていないパッケージのテストは無駄
- パッケージ間の依存関係を考慮する必要がある
6.2 Affected 戦略
# Turborepo を使った affected ビルド
name: Monorepo CI
on: [push, pull_request]
jobs:
determine-affected:
runs-on: ubuntu-latest
outputs:
web: ${{ steps.filter.outputs.web }}
api: ${{ steps.filter.outputs.api }}
shared: ${{ steps.filter.outputs.shared }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
web:
- 'packages/web/**'
- 'packages/shared/**'
api:
- 'packages/api/**'
- 'packages/shared/**'
shared:
- 'packages/shared/**'
test-web:
needs: determine-affected
if: needs.determine-affected.outputs.web == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npx turbo test --filter=web...
test-api:
needs: determine-affected
if: needs.determine-affected.outputs.api == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npx turbo test --filter=api...
# Turborepo を使った一括実行(affected のみ)
turbo-ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: npm ci
- name: Build and test affected packages
run: npx turbo build test lint --filter='...[HEAD~1]'
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}6.3 Nx を使ったモノレポCI
# Nx の affected コマンドを使用
name: Nx Monorepo CI
on: [push, pull_request]
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: nrwl/nx-set-shas@v4
- run: npm ci
- name: Lint affected
run: npx nx affected -t lint --parallel=3
- name: Test affected
run: npx nx affected -t test --parallel=3 --ci
- name: Build affected
run: npx nx affected -t build --parallel=3
- name: E2E affected
run: npx nx affected -t e2e --parallel=17. マイクロサービスのCI/CD
7.1 サービスごとの独立パイプライン
# サービスごとのCI/CDパイプライン
name: User Service CI/CD
on:
push:
branches: [main]
paths:
- 'services/user-service/**'
- 'libs/common/**'
jobs:
ci:
runs-on: ubuntu-latest
defaults:
run:
working-directory: services/user-service
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm test
- run: npm run build
build-image:
needs: ci
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/build-push-action@v5
with:
context: services/user-service
push: true
tags: |
ghcr.io/myorg/user-service:${{ github.sha }}
ghcr.io/myorg/user-service:latest
deploy:
needs: build-image
runs-on: ubuntu-latest
environment: production
steps:
- name: Deploy user-service
run: |
kubectl set image deployment/user-service \
app=ghcr.io/myorg/user-service:${{ github.sha }}7.2 契約テスト (Contract Testing)
マイクロサービス間のAPI互換性を保証するため、契約テストをCIに組み込む。
# Pact による契約テスト
name: Contract Tests
on: [push]
jobs:
consumer-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- name: Run consumer contract tests
run: npm run test:contract:consumer
- name: Publish pacts
run: |
npx pact-broker publish pacts/ \
--broker-base-url=${{ secrets.PACT_BROKER_URL }} \
--consumer-app-version=${{ github.sha }}
provider-verification:
needs: consumer-tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- name: Verify provider contracts
run: npm run test:contract:provider
env:
PACT_BROKER_URL: ${{ secrets.PACT_BROKER_URL }}
PACT_PROVIDER_VERSION: ${{ github.sha }}8. デプロイ戦略との連携
8.1 Blue-Green デプロイ
# Blue-Green デプロイの CI/CD 連携
name: Blue-Green Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Determine current environment
id: current
run: |
CURRENT=$(aws elbv2 describe-target-groups \
--names prod-active | jq -r '.TargetGroups[0].Tags[] | select(.Key=="color") | .Value')
if [ "$CURRENT" = "blue" ]; then
echo "deploy_to=green" >> "$GITHUB_OUTPUT"
else
echo "deploy_to=blue" >> "$GITHUB_OUTPUT"
fi
- name: Deploy to inactive environment
run: ./deploy.sh ${{ steps.current.outputs.deploy_to }}
- name: Run smoke tests
run: ./smoke-test.sh ${{ steps.current.outputs.deploy_to }}
- name: Switch traffic
run: |
aws elbv2 modify-listener \
--listener-arn ${{ secrets.ALB_LISTENER_ARN }} \
--default-actions Type=forward,TargetGroupArn=${{ steps.current.outputs.deploy_to }}-tg-arn8.2 カナリーデプロイ
# カナリーデプロイの CI/CD 連携
name: Canary Deploy
on:
push:
branches: [main]
jobs:
canary:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Deploy canary (5%)
run: |
kubectl apply -f k8s/canary-deployment.yaml
kubectl set image deployment/app-canary \
app=ghcr.io/myorg/app:${{ github.sha }}
- name: Wait and monitor (15 minutes)
run: |
for i in $(seq 1 15); do
ERROR_RATE=$(curl -s "$PROMETHEUS_URL/api/v1/query?query=rate(http_requests_total{status=~'5..'}[5m])" | jq '.data.result[0].value[1]')
echo "Minute $i: Error rate = $ERROR_RATE"
if (( $(echo "$ERROR_RATE > 0.01" | bc -l) )); then
echo "Error rate exceeded threshold, rolling back"
kubectl rollout undo deployment/app-canary
exit 1
fi
sleep 60
done
- name: Progressive rollout
run: |
for WEIGHT in 25 50 75 100; do
echo "Rolling out to ${WEIGHT}%"
kubectl patch deployment/app-production \
-p "{\"spec\":{\"replicas\":$((WEIGHT * TOTAL_REPLICAS / 100))}}"
sleep 300 # 5分間監視
done9. アンチパターン
アンチパターン1: CI シアター
問題:
CI パイプラインは存在するが、形骸化している。
- テストカバレッジが低く、テストが通っても品質が保証されない
- 失敗したテストを skip や ignore で無視
- "CI が通った = 安全" という誤った安心感
症状:
✗ テストカバレッジ 20% で "CI 通りました"
✗ @skip アノテーションが 50 個以上
✗ CI は通るが本番障害が頻発
✗ フレーキーテスト(不安定なテスト)を放置
改善:
1. カバレッジ閾値を設定(最低80%)
2. @skip テストの定期棚卸し
3. 本番障害ごとに回帰テスト追加
4. Mutation Testing の導入
5. フレーキーテストの専用トラッカーで管理
アンチパターン2: 巨大モノリシックパイプライン
問題:
1つのパイプラインに全てを詰め込み、実行時間が30分〜1時間超。
フィードバックが遅く、開発者がCIを無視するようになる。
Push → Lint → Build → Unit → Integration → E2E → Deploy
| 30分〜1時間 |
改善:
1. ステージの並列化
2. 変更されたパッケージのみテスト (affected)
3. テストの分割実行 (sharding)
4. キャッシュの活用
5. 差分ビルド
目標: PR の CI は 10 分以内に完了
アンチパターン3: 環境差異の放置
問題:
ローカル、CI、ステージング、本番で環境が異なる。
"ローカルでは通るのにCIでは失敗する" が頻発。
症状:
✗ ローカルは Node 18、CI は Node 20
✗ CI にはデータベースがなく、統合テストをスキップ
✗ ステージングと本番でインフラ構成が異なる
改善:
1. Docker / Dev Containers で開発環境を統一
2. .node-version, .tool-versions で言語バージョンを固定
3. CI に services: でデータベース等を起動
4. IaC で全環境のインフラを管理
5. 環境変数の管理を一元化
アンチパターン4: 手動リリースプロセス
問題:
CI は自動化されているが、リリースは手動。
"リリース手順書" が存在し、手順を間違えるリスクがある。
症状:
✗ 30ステップのリリースチェックリスト
✗ リリースのたびに "今回のリリース担当" を決める
✗ リリース日は残業確定
改善:
1. リリースプロセスをワークフローに定義
2. semantic-release / Release Please で自動バージョニング
3. Environment Protection Rules で承認フローを自動化
4. ロールバック手順もワークフローで定義
10. セキュリティとCI/CD
10.1 サプライチェーンセキュリティ
# 依存関係の脆弱性スキャン
name: Security Scan
on:
push:
branches: [main]
schedule:
- cron: '0 9 * * 1' # 毎週月曜
jobs:
dependency-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Dependency Review (PR のみ)
if: github.event_name == 'pull_request'
uses: actions/dependency-review-action@v4
with:
fail-on-severity: high
- name: Trivy vulnerability scan
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
severity: 'CRITICAL,HIGH'
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload scan results
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-results.sarif'
sast:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: CodeQL Analysis
uses: github/codeql-action/init@v3
with:
languages: javascript
- uses: github/codeql-action/analyze@v3
container-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t app:scan .
- name: Scan container image
uses: aquasecurity/trivy-action@master
with:
image-ref: 'app:scan'
severity: 'CRITICAL,HIGH'
exit-code: '1'10.2 CI/CDパイプラインのセキュリティベストプラクティス
1. 最小権限の原則
- GITHUB_TOKEN の permissions を明示的に設定
- 各ジョブに必要最小限の権限のみ付与
- サードパーティアクションはSHAでピン留め
2. シークレット管理
- Environment Secrets で環境ごとに分離
- OIDC でクラウドプロバイダーに認証(長期キー不要)
- シークレットのローテーションを自動化
3. サプライチェーン保護
- Dependabot / Renovate で依存関係を自動更新
- ロックファイル(package-lock.json)をコミット
- npm audit / pip audit をCIに組み込み
- SLSA / Sigstore でビルドの証明
4. ブランチ保護
- main ブランチへの直接プッシュを禁止
- PR レビュー必須(最低2名)
- CI 成功必須でマージ
- 署名付きコミット推奨
実践演習
演習1: 基本的な実装
以下の要件を満たすコードを実装してください。
要件:
- 入力データの検証を行うこと
- エラーハンドリングを適切に実装すること
- テストコードも作成すること
# 演習1: 基本実装のテンプレート
class Exercise1:
"""基本的な実装パターンの演習"""
def __init__(self):
self.data = []
def validate_input(self, value):
"""入力値の検証"""
if value is None:
raise ValueError("入力値がNoneです")
return True
def process(self, value):
"""データ処理のメインロジック"""
self.validate_input(value)
self.data.append(value)
return self.data
def get_results(self):
"""処理結果の取得"""
return {
'count': len(self.data),
'data': self.data
}
# テスト
def test_exercise1():
ex = Exercise1()
assert ex.process(1) == [1]
assert ex.process(2) == [1, 2]
assert ex.get_results()['count'] == 2
try:
ex.process(None)
assert False, "例外が発生するべき"
except ValueError:
pass
print("全テスト合格!")
test_exercise1()演習2: 応用パターン
基本実装を拡張して、以下の機能を追加してください。
# 演習2: 応用パターン
from typing import List, Dict, Optional
from datetime import datetime
class AdvancedExercise:
"""応用パターンの演習"""
def __init__(self, max_size: int = 100):
self._items: List[Dict] = []
self._max_size = max_size
self._created_at = datetime.now()
def add(self, key: str, value: any) -> bool:
"""アイテムの追加(サイズ制限付き)"""
if len(self._items) >= self._max_size:
return False
self._items.append({
'key': key,
'value': value,
'timestamp': datetime.now().isoformat()
})
return True
def find(self, key: str) -> Optional[Dict]:
"""キーによる検索"""
for item in reversed(self._items):
if item['key'] == key:
return item
return None
def remove(self, key: str) -> bool:
"""キーによる削除"""
for i, item in enumerate(self._items):
if item['key'] == key:
self._items.pop(i)
return True
return False
def stats(self) -> Dict:
"""統計情報"""
return {
'total_items': len(self._items),
'max_size': self._max_size,
'usage_percent': len(self._items) / self._max_size * 100,
'uptime': str(datetime.now() - self._created_at)
}
# テスト
def test_advanced():
ex = AdvancedExercise(max_size=3)
assert ex.add("a", 1) == True
assert ex.add("b", 2) == True
assert ex.add("c", 3) == True
assert ex.add("d", 4) == False # サイズ制限
assert ex.find("b")['value'] == 2
assert ex.remove("b") == True
assert ex.find("b") is None
stats = ex.stats()
assert stats['total_items'] == 2
print("応用テスト全合格!")
test_advanced()演習3: パフォーマンス最適化
以下のコードのパフォーマンスを改善してください。
# 演習3: パフォーマンス最適化
import time
from functools import lru_cache
# 最適化前(O(n^2))
def slow_search(data: list, target: int) -> int:
"""非効率な検索"""
for i in range(len(data)):
for j in range(i + 1, len(data)):
if data[i] + data[j] == target:
return (i, j)
return (-1, -1)
# 最適化後(O(n))
def fast_search(data: list, target: int) -> tuple:
"""ハッシュマップを使った効率的な検索"""
seen = {}
for i, num in enumerate(data):
complement = target - num
if complement in seen:
return (seen[complement], i)
seen[num] = i
return (-1, -1)
# ベンチマーク
def benchmark():
import random
data = list(range(5000))
random.shuffle(data)
target = data[100] + data[4000]
start = time.time()
result1 = slow_search(data, target)
slow_time = time.time() - start
start = time.time()
result2 = fast_search(data, target)
fast_time = time.time() - start
print(f"非効率版: {slow_time:.4f}秒")
print(f"効率版: {fast_time:.6f}秒")
print(f"高速化率: {slow_time/fast_time:.0f}倍")
benchmark()ポイント:
- アルゴリズムの計算量を意識する
- 適切なデータ構造を選択する
- ベンチマークで効果を測定する
トラブルシューティング
よくあるエラーと解決策
| エラー | 原因 | 解決策 |
|---|---|---|
| 初期化エラー | 設定ファイルの不備 | 設定ファイルのパスと形式を確認 |
| タイムアウト | ネットワーク遅延/リソース不足 | タイムアウト値の調整、リトライ処理の追加 |
| メモリ不足 | データ量の増大 | バッチ処理の導入、ページネーションの実装 |
| 権限エラー | アクセス権限の不足 | 実行ユーザーの権限確認、設定の見直し |
| データ不整合 | 並行処理の競合 | ロック機構の導入、トランザクション管理 |
デバッグの手順
- エラーメッセージの確認: スタックトレースを読み、発生箇所を特定する
- 再現手順の確立: 最小限のコードでエラーを再現する
- 仮説の立案: 考えられる原因をリストアップする
- 段階的な検証: ログ出力やデバッガを使って仮説を検証する
- 修正と回帰テスト: 修正後、関連する箇所のテストも実行する
# デバッグ用ユーティリティ
import logging
import traceback
from functools import wraps
# ロガーの設定
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s [%(levelname)s] %(name)s: %(message)s'
)
logger = logging.getLogger(__name__)
def debug_decorator(func):
"""関数の入出力をログ出力するデコレータ"""
@wraps(func)
def wrapper(*args, **kwargs):
logger.debug(f"呼び出し: {func.__name__}(args={args}, kwargs={kwargs})")
try:
result = func(*args, **kwargs)
logger.debug(f"戻り値: {func.__name__} -> {result}")
return result
except Exception as e:
logger.error(f"例外発生: {func.__name__}: {e}")
logger.error(traceback.format_exc())
raise
return wrapper
@debug_decorator
def process_data(items):
"""データ処理(デバッグ対象)"""
if not items:
raise ValueError("空のデータ")
return [item * 2 for item in items]パフォーマンス問題の診断
パフォーマンス問題が発生した場合の診断手順:
- ボトルネックの特定: プロファイリングツールで計測
- メモリ使用量の確認: メモリリークの有無をチェック
- I/O待ちの確認: ディスクやネットワークI/Oの状況を確認
- 同時接続数の確認: コネクションプールの状態を確認
| 問題の種類 | 診断ツール | 対策 |
|---|---|---|
| CPU負荷 | cProfile, py-spy | アルゴリズム改善、並列化 |
| メモリリーク | tracemalloc, objgraph | 参照の適切な解放 |
| I/Oボトルネック | strace, iostat | 非同期I/O、キャッシュ |
| DB遅延 | EXPLAIN, slow query log | インデックス、クエリ最適化 |
設計判断ガイド
選択基準マトリクス
技術選択を行う際の判断基準を以下にまとめます。
| 判断基準 | 重視する場合 | 妥協できる場合 |
|---|---|---|
| パフォーマンス | リアルタイム処理、大規模データ | 管理画面、バッチ処理 |
| 保守性 | 長期運用、チーム開発 | プロトタイプ、短期プロジェクト |
| スケーラビリティ | 成長が見込まれるサービス | 社内ツール、固定ユーザー |
| セキュリティ | 個人情報、金融データ | 公開データ、社内利用 |
| 開発速度 | MVP、市場投入スピード | 品質重視、ミッションクリティカル |
アーキテクチャパターンの選択
| アーキテクチャ選択フロー |
|---|
| ① チーム規模は? |
| ├─ 小規模(1-5人)→ モノリス |
| └─ 大規模(10人+)→ ②へ |
| ② デプロイ頻度は? |
| ├─ 週1回以下 → モノリス + モジュール分割 |
| └─ 毎日/複数回 → ③へ |
| ③ チーム間の独立性は? |
| ├─ 高い → マイクロサービス |
| └─ 中程度 → モジュラーモノリス |
トレードオフの分析
技術的な判断には必ずトレードオフが伴います。以下の観点で分析を行いましょう:
1. 短期 vs 長期のコスト
- 短期的に速い方法が長期的には技術的負債になることがある
- 逆に、過剰な設計は短期的なコストが高く、プロジェクトの遅延を招く
2. 一貫性 vs 柔軟性
- 統一された技術スタックは学習コストが低い
- 多様な技術の採用は適材適所が可能だが、運用コストが増加
3. 抽象化のレベル
- 高い抽象化は再利用性が高いが、デバッグが困難になる場合がある
- 低い抽象化は直感的だが、コードの重複が発生しやすい
# 設計判断の記録テンプレート
class ArchitectureDecisionRecord:
"""ADR (Architecture Decision Record) の作成"""
def __init__(self, title: str):
self.title = title
self.context = ""
self.decision = ""
self.consequences = []
self.alternatives = []
def set_context(self, context: str):
"""背景と課題の記述"""
self.context = context
return self
def set_decision(self, decision: str):
"""決定内容の記述"""
self.decision = decision
return self
def add_consequence(self, consequence: str, positive: bool = True):
"""結果の追加"""
self.consequences.append({
'description': consequence,
'type': 'positive' if positive else 'negative'
})
return self
def add_alternative(self, name: str, reason_rejected: str):
"""却下した代替案の追加"""
self.alternatives.append({
'name': name,
'reason_rejected': reason_rejected
})
return self
def to_markdown(self) -> str:
"""Markdown形式で出力"""
md = f"# ADR: {self.title}\n\n"
md += f"## 背景\n{self.context}\n\n"
md += f"## 決定\n{self.decision}\n\n"
md += "## 結果\n"
for c in self.consequences:
icon = "✅" if c['type'] == 'positive' else "⚠️"
md += f"- {icon} {c['description']}\n"
md += "\n## 却下した代替案\n"
for a in self.alternatives:
md += f"- **{a['name']}**: {a['reason_rejected']}\n"
return md11. FAQ
Q1: CI/CD パイプラインの理想的な実行時間は?
PR 上の CI は 10 分以内が目標。開発者の集中力が途切れる前にフィードバックを返す必要がある。10分を超える場合は並列化、キャッシュ、テスト分割、差分ビルドを検討する。デプロイパイプラインは 15 分以内が目安。Google の研究(DORA)によると、エリートチームのリードタイムは1時間未満である。
Q2: 継続的デプロイメントを導入する前提条件は?
(1) 高いテストカバレッジ(80%以上)、(2) 自動化されたロールバック機構、(3) カナリーデプロイ/Feature Flag、(4) 充実した監視・アラート、(5) 組織の信頼とリスク許容度。これら全てが揃わないまま導入するとインシデントが頻発する。段階的に導入し、まず継続的デリバリーを確立することを推奨する。
Q3: モノレポの CI/CD はどう設計するか?
変更されたパッケージのみを対象にテスト・ビルドする「affected」戦略が基本。Nx、Turborepo、Bazel などのモノレポツールが差分検知を提供する。加えて、パッケージ間の依存関係グラフに基づいて、影響を受ける下流パッケージも含めてテストする。Remote Cache(Turborepo Cloud, Nx Cloud)を活用するとCIの高速化に大きく貢献する。
Q4: フレーキーテスト(不安定なテスト)にどう対処するか?
まず不安定なテストを特定し、専用のトラッカー(Issue, スプレッドシート等)で管理する。根本原因は多くの場合、(1) テスト間の依存関係、(2) 非同期処理の待機不足、(3) 外部サービスへの依存、(4) 時間依存のロジックである。短期的には retry オプションでリトライし、中長期的には根本原因を修正する。GitHub Actions では retry-on-error アクションを使用できる。
Q5: CI/CD のコストを最適化するには?
(1) キャッシュの活用(依存関係、ビルド成果物)、(2) 差分テスト(affected のみ)、(3) テストのシャーディング(並列化)、(4) 不要なワークフローの paths フィルタ、(5) スケジュール実行の見直し、(6) セルフホステッドランナーの検討(大量実行時)。GitHub Actions の場合、パブリックリポジトリは無料、プライベートは月間2,000分(Free)から50,000分(Enterprise)。
Q6: Feature Flag とCI/CDの関係は?
Feature Flag はトランクベース開発と継続的デプロイメントの実現に不可欠な技術。未完成の機能をフラグで隠しながらメインブランチにマージし、本番にデプロイできる。これにより長寿命フィーチャーブランチを避け、マージコンフリクトのリスクを大幅に削減する。LaunchDarkly、Unleash、Flagsmith、AWS AppConfig 等のサービスが利用可能。
FAQ
Q1: このトピックを学ぶ上で最も重要なポイントは何ですか?
実践的な経験を積むことが最も重要です。理論だけでなく、実際にコードを書いて動作を確認することで理解が深まります。
Q2: 初心者がよく陥る間違いは何ですか?
基礎を飛ばして応用に進むことです。このガイドで説明している基本概念をしっかり理解してから、次のステップに進むことをお勧めします。
Q3: 実務ではどのように活用されていますか?
このトピックの知識は、日常的な開発業務で頻繁に活用されます。特にコードレビューやアーキテクチャ設計の際に重要になります。
まとめ
| 項目 | 要点 |
|---|---|
| CI | 頻繁な統合 + 自動ビルド・テスト |
| CD (デリバリー) | リリース可能状態を常に維持 + 手動承認デプロイ |
| CD (デプロイメント) | 全自動本番デプロイ(高成熟度が前提) |
| パイプライン設計 | 高速フィードバック、並列化、キャッシュ |
| テスト戦略 | テストピラミッド(Unit 70 : Integration 20 : E2E 10) |
| ブランチ戦略 | トランクベース(推奨)、GitHub Flow、Git Flow |
| モノレポ | affected 戦略 + Remote Cache |
| セキュリティ | 依存関係スキャン、SAST、最小権限 |
| メトリクス | DORA メトリクスで改善を定量化 |
| 目標時間 | CI 10分以内、デプロイ15分以内 |
次に読むべきガイド
- GitHub Actions 基礎 -- CI/CD を実現する具体的ツール
- デプロイ戦略 -- Blue-Green、Canary 等の戦略
- Infrastructure as Code -- インフラ自動化の基礎
- GitOps -- Gitを中心としたデプロイモデル
参考文献
- Jez Humble, David Farley. Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation. Addison-Wesley, 2010.
- Martin Fowler. "Continuous Integration." https://martinfowler.com/articles/continuousIntegration.html
- Google. "Trunk-Based Development." https://trunkbaseddevelopment.com/
- Charity Majors. "Test in Production." https://increment.com/testing/i-test-in-production/
- DORA Team. "Accelerate: State of DevOps Report." https://dora.dev/
- Nicole Forsgren, Jez Humble, Gene Kim. Accelerate: The Science of Lean Software and DevOps. IT Revolution Press, 2018.
- Sam Newman. Building Microservices, 2nd Edition. O'Reilly Media, 2021.
- GitHub. "GitHub Actions Documentation." https://docs.github.com/en/actions