出荷とローンチ
概要
自信を持って出荷しましょう。目標は単にデプロイすることではありません。監視を整え、ロールバック計画を準備し、成功の定義を明確に理解した上で、安全にデプロイすることです。すべてのローンチは、取り消し可能で、観測可能で、段階的であるべきです。
使用するタイミング
- 初めて機能を本番環境にデプロイするとき
- ユーザーに大きな変更をリリースするとき
- データやインフラストラクチャを移行するとき
- ベータ版やアーリーアクセスプログラムを開始するとき
- リスクを伴うデプロイ(すべてのデプロイ)
プリローンチチェックリスト
コード品質
- すべてのテストがパスする(単体テスト、結合テスト、E2Eテスト)
- ビルドが警告なしで成功する
- リンターと型チェックがパスする
- コードレビューが完了し承認されている
- ローンチ前に解決すべきTODOコメントが残っていない
- 本番コードに
console.logのデバッグ文が残っていない - エラーハンドリングが想定される障害モードをカバーしている
セキュリティ
- コードやバージョン管理にシークレットが含まれていない
- エコシステムの依存関係監査(
npm audit、pip-audit、cargo auditなど)でクリティカルまたは高重要度の脆弱性が検出されていない - すべてのユーザー向けエンドポイントで入力検証が行われている
- 認証と認可のチェックが実装されている
- セキュリティヘッダーが設定されている(CSP、HSTSなど)
- 認証エンドポイントにレート制限が設定されている
- CORSが特定のオリジンに設定されている(ワイルドカードではない)
パフォーマンス
- Core Web Vitalsが「良好」の閾値内にある
- クリティカルパスにN+1クエリがない
- 画像が最適化されている(圧縮、レスポンシブサイズ、遅延読み込み)
- バンドルサイズが予算内に収まっている
- データベースクエリに適切なインデックスが設定されている
- 静的アセットと繰り返しクエリに対してキャッシュが設定されている
アクセシビリティ
- すべてのインタラクティブ要素でキーボードナビゲーションが機能する
- スクリーンリーダーがページのコンテンツと構造を伝えられる
- 色のコントラストがWCAG 2.1 AA(テキストで4.5:1)を満たしている
- モーダルや動的コンテンツでフォーカス管理が正しく行われている
- エラーメッセージが説明的で、フォームフィールドに関連付けられている
- axe-coreやLighthouseでアクセシビリティの警告がない
インフラストラクチャ
- 環境変数が本番環境で設定されている
- データベースマイグレーションが適用されている(または適用準備ができている)
- DNSとSSLが設定されている
- CDNが静的アセット用に設定されている
- ログ出力とエラーレポートが設定されている
- ヘルスチェックエンドポイントが存在し、応答する
ドキュメント
- READMEが新しいセットアップ要件で更新されている
- APIドキュメントが最新である
- アーキテクチャ上の決定に対してADRが作成されている
- 変更ログが更新されている
- ユーザー向けドキュメントが更新されている(該当する場合)
フィーチャーフラグ戦略
デプロイとリリースを分離するために、フィーチャーフラグの背後に出荷します:
// フィーチャーフラグのチェック
const flags = await getFeatureFlags(userId);
if (flags.taskSharing) {
// 新機能: タスク共有
return <TaskSharingPanel task={task} />;
}
// デフォルト: 既存の動作
return null;
フィーチャーフラグのライフサイクル:
1. フラグOFFでデプロイ → コードは本番環境にあるが非アクティブ
2. チーム/ベータで有効化 → 本番環境での内部テスト
3. 段階的ロールアウト → ユーザーの5% → 25% → 50% → 100%
4. 各段階で監視 → エラー率、パフォーマンス、ユーザーフィードバックを確認
5. クリーンアップ → 完全ロールアウト後にフラグとデッドコードを削除
ルール:
- すべてのフィーチャーフラグには所有者と有効期限がある
- 完全ロールアウトから2週間以内にフラグをクリーンアップする
- フィーチャーフラグをネストしない(指数関数的な組み合わせが生じる)
- CIで両方のフラグ状態(オンとオフ)をテストする
段階的ロールアウト
ロールアウトの順序
1. ステージングにデプロイ
└── ステージング環境で完全なテストスイートを実行
└── クリティカルフローの手動スモークテスト
2. 本番環境にデプロイ(フィーチャーフラグOFF)
└── デプロイ成功の確認(ヘルスチェック)
└── エラー監視の確認(新しいエラーがないこと)
3. チームに対して有効化(内部ユーザーに対してフラグON)
└── チームが本番環境で機能を使用
└── 24時間の監視ウィンドウ
4. カナリアロールアウト(ユーザーの5%に対してフラグON)
└── エラー率、レイテンシ、ユーザー行動を監視
└── メトリクスの比較: カナリア vs ベースライン
└── 24〜48時間の監視ウィンドウ
└── すべての閾値をパスした場合のみ進行(以下の表を参照)
5. 段階的に増加(25% -> 50% -> 100%)
└── 各ステップで同じ監視を実施
└── どの時点でも以前のパーセンテージにロールバック可能
6. 完全ロールアウト(すべてのユーザーに対してフラグON)
└── 1週間監視
└── フィーチャーフラグをクリーンアップ
ロールアウト判断の閾値
各段階で進行、保留、またはロールバックを判断するための閾値:
| メトリクス | 進行(緑) | 保留して調査(黄) | ロールバック(赤) |
|---|---|---|---|
| エラー率 | ベースラインの10%以内 | ベースラインの10〜100%超過 | ベースラインの2倍超 |
| P95レイテンシ | ベースラインの20%以内 | ベースラインの20〜50%超過 | ベースラインの50%超 |
| クライアントJSエラー | 新しいエラータイプなし | セッションの0.1%未満で新しいエラー | セッションの0.1%超で新しいエラー |
| ビジネスメトリクス | 中立または改善 | 5%未満の低下(ノイズの可能性あり) | 5%超の低下 |
ロールバックすべきタイミング
以下の場合は直ちにロールバック:
- エラー率がベースラインの2倍を超えて増加
- P95レイテンシが50%を超えて増加
- ユーザーからの問題報告が急増
- データ整合性の問題が検出された
- セキュリティ脆弱性が発見された
監視と可観測性
監視すべきもの
アプリケーションメトリクス:
├── エラー率(全体およびエンドポイント別)
├── 応答時間(p50、p95、p99)
├── リクエスト量
├── アクティブユーザー数
└── 主要ビジネスメトリクス(コンバージョン、エンゲージメント)
インフラストラクチャメトリクス:
├── CPUおよびメモリ使用率
├── データベースコネクションプール使用率
├── ディスク空き容量
├── ネットワークレイテンシ
└── キューの深さ(該当する場合)
クライアントメトリクス:
├── Core Web Vitals(LCP、INP、CLS)
├── JavaScriptエラー
├── クライアント視点からのAPIエラー率
└── ページ読み込み時間
エラーレポート
// レポート機能付きエラーバウンダリの設定
class ErrorBoundary extends React.Component {
componentDidCatch(error: Error, info: React.ErrorInfo) {
// エラー追跡サービスに報告
reportError(error, {
componentStack: info.componentStack,
userId: getCurrentUser()?.id,
page: window.location.pathname,
});
}
render() {
if (this.state.hasError) {
return <ErrorFallback onRetry={() => this.setState({ hasError: false })} />;
}
return this.props.children;
}
}
// サーバーサイドのエラーレポート
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
reportError(err, {
method: req.method,
url: req.url,
userId: req.user?.id,
});
// 内部情報をユーザーに公開しない
res.status(500).json({
error: { code: 'INTERNAL_ERROR', message: '問題が発生しました' },
});
});
ローンチ後の確認
ローンチから最初の1時間:
1. ヘルスチェックエンドポイントが200を返すことを確認
2. エラー監視ダッシュボードを確認(新しいエラータイプがないこと)
3. レイテンシダッシュボードを確認(劣化がないこと)
4. クリティカルなユーザーフローを手動でテスト
5. ログが出力され、読み取り可能であることを確認
6. ロールバックメカニズムが機能することを確認(可能であればドライラン)
エラーバジェットリリースゲート
サービスのエラーバジェット(SLOが許容する失敗リクエストや時間の割合)によって、出荷が安全かどうかが決まります。客観的なゲートとして使用し、交渉の対象にしないでください:
バジェット残量 > 20% → 通常通り出荷、注意深く監視
バジェット残量 0〜20% → 低速ロールアウトのみ、高リスクな変更はなし
バジェット枯渇 → 機能開発を凍結、信頼性向上に集中
バジェットリセット → 通常ペースを再開、回復に貢献した修正を組み込む
カナリア中の高いバーン率(ベースラインペースよりも速くバジェットを消費している)は、上記ロールアウト閾値テーブルでの保留シグナルです。エラー率上昇と同じように扱ってください。
ロールバック戦略
すべてのデプロイには、実行前にロールバック計画が必要です:
## [機能/リリース]のロールバック計画
### トリガー条件
- エラー率 > ベースラインの2倍
- P95レイテンシ > [X]ms
- [特定の問題]に関するユーザー報告
### ロールバック手順
1. フィーチャーフラグを無効にする(該当する場合)
または
1. 以前のバージョンをデプロイ: `git revert <commit> && git push`
2. ロールバックを確認: ヘルスチェック、エラー監視
3. 連絡: チームにロールバックを通知
### データベースに関する考慮事項
- マイグレーション [X] にはロールバックあり: `npx prisma migrate rollback`
- 新機能によって挿入されたデータ: [保持 / クリーンアップ]
### ロールバックまでの時間
- フィーチャーフラグ: 1分未満
- 以前のバージョンの再デプロイ: 5分未満
- データベースロールバック: 15分未満
関連項目
- このチェックリストの前にすべての変更がクリアすべきプロジェクト全体の「完了の定義」については、
../../references/definition-of-done.mdを参照 - セキュリティ事前ローンチチェックについては、
../../references/security-checklist.mdを参照 - パフォーマンス事前ローンチチェックリストについては、
../../references/performance-checklist.mdを参照 - ローンチ前のアクセシビリティ確認については、
../../references/accessibility-checklist.mdを参照 - アラートルールとSLOに関連付けられた閾値については、
observability-and-instrumentationを参照
よくある言い訳
| 言い訳 | 現実 |
|---|---|
| 「ステージングで動作したから本番でも動作する」 | 本番環境はデータ、トラフィックパターン、エッジケースが異なります。デプロイ後に監視しましょう。 |
| 「これにフィーチャーフラグは必要ない」 | すべての機能はキルスイッチの恩恵を受けます。「単純な」変更でも問題を引き起こす可能性があります。 |
| 「監視はオーバーヘッドだ」 | 監視がないということは、ダッシュボードではなくユーザーの苦情から問題を発見することを意味します。 |
| 「監視は後で追加する」 | ローンチ前に追加してください。見えないものはデバッグできません。 |
| 「ロールバックは失敗を認めることだ」 | ロールバックは責任あるエンジニアリングです。壊れた機能を出荷することが失敗です。 |
| 「エラー率は問題ない、出荷を続けよう」 | 現在のエラー率だけでなく、バーン率も確認してください。個々の閾値が緑色でも、ベースラインより速くバジェットを消費している場合は保留シグナルです。 |
レッドフラグ
- ロールバック計画なしでデプロイする
- 本番環境に監視やエラーレポートがない
- ビッグバンリリース(すべてを一度に、ステージングなし)
- 有効期限や所有者のないフィーチャーフラグ
- デプロイ後最初の1時間を誰も監視していない
- 本番環境の設定をコードではなく記憶で行っている
- 「金曜の午後に出荷しよう」
- エラーバジェットが枯渇しているが、機能開発は変わらず続けている
確認
デプロイ前:
- プリローンチチェックリストが完了している(すべてのセクションが緑)
- フィーチャーフラグが設定されている(該当する場合)
- ロールバック計画が文書化されている
- 監視ダッシュボードが設定されている
- デプロイについてチームに通知済み
デプロイ後:
- ヘルスチェックが200を返す
- エラー率が正常
- レイテンシが正常
- クリティカルなユーザーフローが機能する
- ログが出力されている
- ロールバックがテスト済み、または準備完了を確認済み
出荷されたすべてのサービスについて:
- エラーバジェットポリシーが整っている: バジェットが20%を下回った場合と枯渇した場合の対処方法を把握している