プロダクション対応度チェックリスト
各エントリは確認すべき内容を示しています。CLAUDE.md で定義されたプロジェクトのステージ段階に基づいて、プロジェクトに適したものを適用してください。ステージが定義されていない場合は、ステージ(プロトタイプ、MVP、プロダクション、スケール)を推測して明記してください。レビューでは、そのステージに適合しないエントリを「未適用」としてマークし、それらが関連するようになるトリガーを記載してください。
1. セキュリティとアクセス制御
- 認可: ログイン時だけでなく、すべてのエンドポイントとアクセスされるすべてのオブジェクトに対して、サーバーサイドで権限を確認します(IDOR および BOLA を防止)。Supabase、Firebase、または同様のバックエンドを使用する場合は、行レベルセキュリティまたはセキュリティルールを有効にしてテストする必要があります。
- 入力検証: すべての信頼境界で、スキーマライブラリ(Zod、Pydantic、または同等のもの)を使用して、型、長さ、形式、範囲を検証します。クライアントサイドの検証はユーザーエクスペリエンスのためであり、セキュリティのためではありません。
- SQLインジェクション: パラメータ化クエリまたはORMバインディングのみを使用します。ソート列などの動的識別子は許可リスト方式で管理します。
- XSS: フレームワークのエスケープ機能に依存します。信頼できないデータで
innerHTML、dangerouslySetInnerHTML、v-htmlを使用しないか、DOMPurify などの検証済みライブラリでサニタイズします。コンテンツセキュリティポリシーを追加します。 - CSRF: クッキーベースのセッションの場合、SameSite クッキーと CSRF トークン、または状態変更リクエストの Origin チェックを使用します。GET で状態を変更しないでください。
- CORS: オリジンを正確に許可リスト化します。任意のオリジンを反映したり、クレデンシャル付きでワイルドカードを許可したりしないでください。CORS はアクセス制御ではありません。
- SSRF: サーバーがユーザー指定のURLを取得する場合、送信先を許可リスト化し、プライベートアドレス、ループバックアドレス、リンクローカルアドレス(IPv4 および IPv6、クラウドメタデータ
169.254.169.254を含む)をブロックし、リダイレクト後およびDNS解決後に再チェックします。 - シークレット管理: 環境変数またはシークレットマネージャーからシークレットをロードします。シークレットはコード、ログ、コンテナイメージ、git 履歴に決して表示されず、漏洩したものはローテーションされます。
- IAM: すべての人物、サービス、トークンに最小権限を適用します。環境ごと、サービスごとに個別のクレデンシャルを使用し、アプリケーションでルートキーや管理者キーを決して使用しないでください。
- OAuth: カスタム認証ではなく、実績のあるプロバイダーまたはライブラリを使用します。ブラウザおよびモバイルクライアントには Authorization Code with PKCE を使用し、正確なリダイレクト URI の一致、state の検証、最小限のスコープを使用します。
- JWT ローテーション: 有効期間の短いアクセストークン、再利用検出付きのローテーションするリフレッシュトークン、およびキーID (
kid) と JWKS によってローテーションされる署名キーを使用します。署名、アルゴリズム、発行者、対象者、有効期限を常に検証します。 - パスワードとセッションのセキュリティ: パスワードは Argon2id または bcrypt でハッシュ化します。セッションクッキーは HttpOnly、Secure、SameSite に設定します。アカウントに価値がある場合は MFA を提供します。
- TLS: 自動証明書更新、TLS 1.2 以上(1.3 を推奨)、HSTS を使用して、すべての場所で HTTPS を提供します。証明書検証を無効にしないでください。
- 転送中の暗号化: 公開トラフィックだけでなく、サービス、データベース、キャッシュ、キューの接続を含む内部トラフィックも暗号化します。
- 保存データの暗号化: データベース、ディスク、バックアップ、オブジェクトストレージを暗号化し、キーは KMS で管理します。機密性の高いデータにはフィールドレベル暗号化を検討します。
- セキュリティヘッダー: Content-Security-Policy(クリックジャッキング対策の frame-ancestors を含む)、Strict-Transport-Security、
X-Content-Type-Options: nosniff、Referrer-Policy を設定します。 - WAF: パブリックなプロダクションアプリの前に、管理されたルールセットを持つ Web アプリケーションファイアウォールを配置します。
- DDoS 対策: プロバイダーの保護(Cloudflare、AWS Shield、またはプラットフォームのデフォルト)とレート制限およびオートスケーリングの上限に依存し、攻撃が無制限の請求につながらないようにします。
- サプライチェーンセキュリティ: CI で依存関係の脆弱性スキャン(npm audit、pip-audit、Dependabot など)を実行します。すべての新しいパッケージが本物であり、正しく命名され、メンテナンスされていることを確認します。
- ファイルアップロード: 拡張子ではなくコンテンツで型をチェックし、サイズを制限し、ランダムな名前でオブジェクトストレージに保存し、プライベートファイルは有効期間の短い署名付き URL を通じて提供します。
- 監査ログ: 機密性の高いアクション(認証イベント、権限変更、データエクスポート、送金)について、誰が何をいつ行ったかを追記専用ストレージに記録します。
- プライバシーと個人情報: 最小限を収集し、個人データがどこにあるかを把握し、ログに記録せず、保持期間を定義し、GDPR や CCPA などの法律が適用される場合に削除とエクスポートをサポートします。
2. データとデータベース
- データベースインデックス: 成長するテーブルのフィルター、結合、ソートで使用される列(外部キーを含む)にインデックスを作成します。書き込みを遅くする冗長なインデックスは避けます。
- クエリ最適化: 低速または頻繁なクエリに対して
EXPLAINで計画を確認します。必要な列のみを選択し、WHERE 句内でインデックス列を関数でラップしないようにします。 - N+1 クエリ: アイテムごとに 1 回クエリを実行するループを見つけます。結合、Eager Loading、またはバッチ処理に置き換えます。
- コネクションプーリング: プールのサイズをデータベースの制限に合わせます。サーバーレスでは、すべてのインスタンスが独自の接続を開くため、プーラー(PgBouncer、RDS Proxy、またはプロバイダーのプーラー)を使用します。
- トランザクション: 成功するか失敗するかを一緒に行う必要がある複数ステップの書き込みを 1 つのトランザクションでラップし、分離レベルを意図的に選択します。
- 制約: NOT NULL、UNIQUE、外部キー、CHECK 制約を使用して、データベース内で整合性を強制します。アプリケーションレベルのチェックだけではレースコンディションに負けます。
- ページネーション: すべてのリストエンドポイントとクエリを、上限のあるページサイズでページネーションします。大規模または頻繁に変更されるデータには、カーソル(キーセット)ページネーションを推奨します。
- 金額と時刻: 金額は整数の最小単位または decimal 型で保存します。タイムスタンプは UTC で保存し、表示時にのみ変換します。
- 読み取りレプリカ: 読み取りがプライマリに過負荷をかける場合にのみ追加し、特にユーザーが自分の書き込みを読み取る場合のレプリケーションラグを処理します。
- レプリケーション: 同期か非同期か、フェイルオーバーでどれだけのデータが失われる可能性があるかを把握します。
- パーティショニング: 非常に大きなテーブルを(多くの場合時間で)パーティショニングして、インデックスとメンテナンスを管理しやすくし、パーティションを削除して古いデータを期限切れにします。
- シャーディング: インデックス作成、キャッシュ、レプリカ、垂直スケーリングの後の最後の手段として扱います。ホットスポットやクロスシャードクエリを回避するシャードキーを選択します。
- データベースマイグレーション: マイグレーションをバージョン管理し、レビューし、デプロイで自動化し、可能な場合は元に戻せるようにします。ダウンタイムゼロの場合は、拡張と縮小(expand and contract)を使用します。追加、バックフィル、切り替え、後続のリリースで削除します。破壊的な変更の前にはバックアップを取ります。
- スキーマバージョニング: データコントラクト(データベーススキーマ、イベントペイロード、API スキーマ)をバージョン管理し、デプロイ中に新旧のコードが並行して実行できるようにします。
3. 並行処理と分散システム
- レースコンディション: check-then-act コード(残高、在庫、一意のユーザー名、クーポン利用)を探します。アトミック更新、制約、トランザクション、またはロックで修正します。
- スレッドセーフ: スレッド、ワーカー、非同期タスク間で共有される可変状態を同期化するか排除します。モジュールレベルのグローバル変数は、リクエスト間で共有されることがよくあります。
- 楽観的ロック: 低競合のレコードに対して、更新時にバージョン列または ETag をチェックし、競合時には 409 または 412 を返します。
- 悲観的ロック: 高競合のクリティカルセクションには
SELECT ... FOR UPDATEを使用し、ロック時間を短く保ちます。 - デッドロック: 一貫した順序でロックを取得し、トランザクションを短く保ち、デッドロックエラーでリトライします。
- 分散ロック: プロセス間の調整にのみ使用し、期限付きのリースとフェンシングトークンを使用します。データベース制約やキューで十分な場合は、それらを優先します。
- 分散トランザクション: サービス間での 2 フェーズコミットは避けます。可能な限り、トランザクション境界を 1 つのデータベース内に収めます。
- Saga パターン: サービスにまたがるワークフローの場合、補償アクションを持つローカルトランザクションをチェーンし、すべてのステップをべき等にします。
- CAP 定理: ネットワーク分割中、システムは一貫性または可用性のいずれかを選択する必要があります。機能ごとに決定します。残高と在庫は一貫性を必要とし、フィードとカウンターは古さを許容できます。
- 結果整合性: これが許容される場合、古い読み取りに備えて UI とロジックを設計し、更新が収束するようにします。
- リーダー選出: 正確に 1 つのインスタンスが動作する必要がある場合(スケジューラー、シングルトンワーカー)、リースまたはプラットフォームプリミティブを使用し、ハードコードされたインスタンスは決して使用しません。
- ネットワーク分割: 任意の呼び出しが途中で失敗したり、応答なしで成功する可能性があることを想定します。タイムアウト、べき等性、調整を備えて設計します。
- クロックスキュー: 順序付けや正確性のために、マシン間のクロックに決して依存しないでください。データベースタイムスタンプ、シーケンス番号、論理クロックを使用し、トークン有効期限チェックでは小さな余裕を許容します。
- サービスディスカバリ: DNS、プラットフォーム、またはレジストリを通じてサービスを解決し、ハードコードされた IP は使用しません。
4. 耐障害性とフォールトトレランス
- タイムアウト: すべての外部呼び出し(HTTP、データベース、キャッシュ、キュー、LLM API)に、呼び出し側の独自の期限に収まる接続およびリクエストのタイムアウトを設定します。
- リトライ: べき等な操作でのみ、一時的な障害(タイムアウト、接続エラー、429、502、503、504)をリトライし、試行回数を制限します。すべてのレイヤーで同時にリトライすることは避け、負荷が増大するのを防ぎます。
- 指数バックオフ: ジッターを伴う指数バックオフを行い、
Retry-Afterヘッダーを尊重します。 - サーキットブレーカー: 繰り返しエラーが発生した後、障害が発生している依存関係の呼び出しを停止し、フォールバックでフェイルファストし、回復をプローブします。
- べき等性: リトライされる可能性のある操作(支払い、注文、Webhook ハンドラー、ジョブ)にべき等性キーまたは自然キーを付与し、重複が追加の影響を与えないようにします。
- バックプレッシャー: キュー、バッファ、同時実行数を制限します。過負荷の場合は、無制限の作業を受け入れる代わりに、負荷を切り捨てるか、429 または 503 を返します。
- バルクヘッド: リソースを分離(個別のプールまたはワーカー)して、1 つの低速な依存関係がすべてを枯渇させないようにします。
- グレースフルデグラデーション: 依存関係がダウンした場合に何がまだ機能するかを決定します(キャッシュデータ、読み取り専用モード、機能の一時的非表示など)。
- グレースフルシャットダウン:
SIGTERMを受信したら、新しい作業の受け入れを停止し、進行中のリクエストとジョブを完了してから、接続を閉じます。 - ヘルスチェック: ロードバランサーまたはプラットフォーム向けに、軽量なヘルスエンドポイントを公開します。
- Liveness & Readiness プローブ: Liveness プローブはスタックしたプロセスのみを検出し、外部サービスに依存してはなりません(1 つのデータベースの障害で全てが再起動するのを防ぐため)。Readiness プローブはインスタンスがトラフィックを受信するかどうかを決定し、依存関係をチェックしても構いません。
- フェイルオーバー: プライマリデータベース、ゾーン、またはプロバイダーが障害を起こした場合に何が起こるかを把握します。アップタイムが重要な場合は自動化し、テストします。
5. メッセージング、バックグラウンド処理、リアルタイム
- メッセージキュー: 低速、リトライ可能、またはバースト性の高い作業(メール、メディア処理、サードパーティ呼び出し)をリクエストパスからキューとワーカーに移動します。
- 配信セマンティクス: 少なくとも 1 回の配信を想定するため、コンシューマーはべき等である必要があります。システムがキーごとに保証しない限り、順序を想定しないでください。
- デッドレターキュー: リトライを使い果たしたメッセージをアラートとリプレイパス付きの DLQ にルーティングします。サイレントにドロップしたり、無限にリトライしたりしないでください。
- Pub/Sub: 複数の独立したコンシューマーへのファンアウトに使用し、定義されたイベントスキーマと所有権を持ちます。
- イベント駆動型アーキテクチャ: デカップリングがデバッグのコストに見合う場合に採用します。イベントをバージョン管理されたコントラクトとして扱い、リクエストをそれらにわたってトレース可能に保ちます。
- トランザクショナルアウトボックス: データベース書き込みと同時にイベントを公開する必要がある場合、両方を 1 つのトランザクション(アウトボックステーブル)で書き込み、非同期に中継します。これにより、二重書き込み問題を回避します。
- Cron ジョブ: スケジュールされたジョブをべき等にし、2 回実行したり重複しても安全で、一度に 1 つのインスタンスに制限し、実行の失敗や欠落を監視します。
- WebSocket: 接続時に認証し、ハートビートを送信し、バックオフで再接続し、複数のインスタンスを計画します(共有 Pub/Sub またはスティッキーセッション)。
- Server-Sent Events: 通知、進行状況、LLM トークンストリーミングなどのサーバーからクライアントへの一方向ストリームには、WebSocket よりも SSE を推奨します。
- ロングポーリング: WebSocket と SSE が利用できない場合のフォールバックとして、タイムアウトと保持接続数の制限を設けて使用します。
- Webhook: 受信 Webhook の場合、シグネチャ(HMAC)を検証し、古いタイムスタンプを拒否してリプレイを防止し、迅速に確認応答して非同期に処理し、イベント ID で重複排除します。送信 Webhook の場合、ペイロードに署名し、バックオフでリトライし、イベント形式をバージョン管理します。
6. トラフィック、エッジ、ネットワーキング
- レート制限: パブリック、認証、および高負荷なエンドポイント(ログイン、サインアップ、パスワードリセット、OTP、LLM 呼び出し)で、ユーザー、API キー、IP ごとに制限します。
Retry-Afterと共に 429 を返します。 - 負荷分散: ヘルスチェックされたインスタンス間でトラフィックを分散します。アプリサーバーはステートレスに保ち、任意のインスタンスが任意のリクエストを処理できるようにします。
- リバースプロキシ: プロキシで TLS を終端し、圧縮し、本文サイズとタイムアウト制限を適用します。
X-Forwarded-Forは既知のプロキシからのみ信頼し、クライアント IP が偽装されないようにします。 - API ゲートウェイ: 複数のサービスまたはパブリック API がある場合、認証、レート制限、ルーティング、検証を集中化します。単一のアプリにはめったに必要ありません。
- CDN: 静的アセットとメディアを、長いキャッシュ有効期間とコンテンツハッシュ化されたファイル名で CDN を通じて提供します。
- DNS: 移行前に TTL を下げ、可能な限りコードとしてレコードを管理し、伝搬遅延を想定します。
- TCP vs UDP: デフォルトでは TCP ベースのプロトコルを使用します。UDP は、損失を許容するレイテンシに敏感なトラフィック(リアルタイムメディア、ゲーム)にのみ、アプリケーションレベルの処理と共に使用します。
- HTTP/2 & HTTP/3: 多重化と高速接続のために、エッジまたは CDN で有効にします。これは通常、設定であり、アプリケーションコードではありません。
- gRPC: 厳密なコントラクトまたはストリーミングを必要とする内部サービス呼び出しに、すべての呼び出しにデッドラインを設定して使用します。ブラウザでは gRPC-Web またはゲートウェイが必要です。
7. キャッシュ
- キャッシュ: 必要性を測定した後にのみキャッシュし、各キャッシュのキー、TTL、対象を定義します。ユーザー固有または権限依存のデータを共有キャッシュに汎用キーで保存しないでください。
- エッジキャッシュ:
Cache-Controlを明示的に設定し、パーソナライズされたレスポンスは private または no-store とマークし、鮮度が許容範囲を超えた場合に stale-while-revalidate を使用します。
...