ひとことで言うと#
クラウド上で動くSaaSアプリを移植性が高く、スケールしやすく、保守しやすく作るための12の原則。Herokuの共同創業者が、何百ものアプリの運用経験からまとめた方法論。
押さえておきたい用語#
- ステートレス(Stateless)
- アプリケーションのプロセスがリクエスト間で状態を保持しない設計のこと。セッション情報はRedis等の外部ストアに保存する。スケールアウトの前提条件。
- 環境変数(Environment Variable)
- DB接続情報やAPIキーなどの環境固有の設定値を外部から注入する仕組みを指す。コードにハードコードせず、デプロイ先ごとに切り替える。
- ポートバインディング(Port Binding)
- アプリ自身がHTTPポートをバインドしてサービスを公開する仕組みのこと。外部のWebサーバー(Apache等)に依存しない自己完結型の設計。
- バックエンドサービス(Backing Service)
- DB、キャッシュ、メールサーバーなど、アプリが利用する外部サービスをアタッチされるリソースとして扱う考え方のこと。接続先をURLで切り替え可能にする。
- グレースフルシャットダウン
- プロセスが終了シグナルを受けた際に、処理中のリクエストを完了させてから安全に停止する仕組みである。Twelve-FactorのFactor IX(廃棄容易性)に対応。
Twelve-Factor Appの全体像#
こんな悩みに効く#
- 本番環境と開発環境の差異でデプロイのたびに問題が起きる
- 環境固有の設定がコードにハードコードされていて移行できない
- スケールアウトしようとすると、ステートフルな設計が邪魔になる
基本の使い方#
コードベース、依存関係、設定を正しく管理する。
- I. コードベース: 1つのリポジトリで複数デプロイを管理する
- II. 依存関係: すべての依存を明示的に宣言する(package.json、requirements.txtなど)
- III. 設定: 環境固有の設定は環境変数で管理する。コードにハードコードしない
ポイント: 「git cloneして環境変数を設定すれば動く」状態が理想。
外部サービスの扱いとデプロイのパイプラインを整備する。
- IV. バックエンドサービス: DB、キャッシュ、メールなどはアタッチされるリソースとして扱う
- V. ビルド・リリース・実行: ビルド→リリース→実行の3段階を厳密に分離する
- VI. プロセス: アプリはステートレスなプロセスとして実行する
- VII. ポートバインディング: アプリ自身がポートをバインドしてHTTPサービスを公開する
ポイント: ステートレスが最重要。セッションはRedisなど外部ストアに保存する。
スケール、環境一致、ログ、管理タスクを整備する。
- VIII. 並行性: プロセスモデルでスケールアウトする
- IX. 廃棄容易性: 高速な起動とグレースフルシャットダウンを実現する
- X. 開発/本番一致: 開発・ステージング・本番の環境差を最小にする
- XI. ログ: ログはイベントストリームとして扱い、標準出力に流す
- XII. 管理プロセス: マイグレーション等は1回限りのプロセスとして実行する
ポイント: ログをファイルに書かず標準出力に流すのは、コンテナ時代のベストプラクティス。
具体例#
状況: 従業員50名のSaaS企業。Rails製のモノリスアプリで、DB接続情報がコードにハードコード、セッションはローカルファイルに保存、ログはアプリ内でファイルに書き込み、デプロイは手動でサーバーにSSH。
Factor III適用: DB接続文字列を環境変数DATABASE_URLに移行。.envファイルで開発環境を管理。
Factor VI適用: セッションをローカルファイルからRedisに移行。これでインスタンスを増やしてもセッションが共有できる。
Factor XI適用: ログ出力先をファイルから標準出力に変更。ログの収集はFluentdやCloudWatch Logsに任せる。
Factor V適用: CI/CDパイプラインを構築し、ビルド→リリース→実行を自動化。
| 指標 | Before | After |
|---|---|---|
| デプロイ手順 | SSH+手動(30分) | git pushで自動(3分) |
| スケールアウト | 不可(セッション問題) | 水平スケール可能 |
| 開発環境構築 | 手順書で半日 | docker-compose upで5分 |
| 本番と開発の差異 | 大きい(障害の原因) | ほぼ同一(Docker) |
デプロイ30分→3分、環境構築半日→5分。12のうち4つ(III, V, VI, XI)を適用しただけでこの結果。全部一度にやる必要はない。
状況: 創業直後の3人チーム。新規SaaSプロダクトをNext.js + Node.js + PostgreSQLで構築。最初からTwelve-Factorに準拠して設計。
設計判断:
- Factor I: モノレポ(Turborepo)で、フロントとAPIを1リポジトリ管理
- Factor II:
package.jsonとpackage-lock.jsonで依存を完全固定 - Factor III: 全設定を環境変数。Vercel/Railwayの環境変数管理を活用
- Factor VI: セッションはJWT。サーバーサイドの状態はRedis
- Factor XI:
console.logで標準出力。Datadogで収集
6ヶ月後の状況:
| 指標 | 結果 |
|---|---|
| 新メンバーの環境構築 | 15分(git clone + npm install + .env設定) |
| デプロイ頻度 | 1日3〜5回 |
| 開発→本番の環境差異 | Docker Composeで完全一致 |
| ユーザー数10倍時のスケール対応 | 設定変更のみ(コード変更ゼロ) |
ユーザー数が10倍になった時のスケール対応——コード変更ゼロ、設定変更のみ。3人チームでも最初からTwelve-Factorに準拠しておけば、成長時に「設計のやり直し」は起きない。
状況: 従業員45名の地方SIer。顧客向けに運用しているJava製業務アプリ(Tomcat + Oracle DB)をAWSに移行したい。現状はオンプレミス1台のサーバーで稼働。DB接続はJNDI設定、ログは/var/log/app/にファイル出力、デプロイはWARファイルを手動配置。
段階的Twelve-Factor化:
| Phase | Factor | 対応内容 | 期間 |
|---|---|---|---|
| 1 | III | DB接続をJNDIから環境変数に移行 | 1週間 |
| 2 | XI | ログをSLF4J→標準出力に変更、CloudWatch Logsで収集 | 3日 |
| 3 | VI | HTTPセッションをElastiCache(Redis)に移行 | 1週間 |
| 4 | V | GitHub Actions + ECSでCI/CDパイプライン構築 | 2週間 |
| 5 | IX | コンテナ化、グレースフルシャットダウン対応 | 1週間 |
| 指標 | Before | After |
|---|---|---|
| デプロイ時間 | 45分(手動) | 8分(自動) |
| 月間運用コスト | 120万円(オンプレ保守込み) | 68万円(AWS) |
| 障害復旧時間 | 平均4時間 | 平均15分 |
| スケール対応 | 不可 | ECS Auto Scaling |
運用コスト120万→68万円、障害復旧4時間→15分。レガシーJavaでもTwelve-Factor化は可能だった。もっとも効果が大きかったのはFactor III(設定の外部化)とFactor VI(ステートレス化)の2つ。
やりがちな失敗パターン#
- 12個全部を一度に適用しようとする — 既存アプリの全面改修は現実的ではない。最もインパクトが大きい項目(III: 設定の外部化、VI: ステートレス化)から段階的に適用する
- 設定ファイルを環境変数に移しただけで満足する — 環境変数がカオスになりがち。命名規則を統一し、必要な環境変数の一覧をドキュメント化する
- 「ステートレス」を誤解する — アプリにステートがないのではなく、ステートを外部サービスに保存するという意味。一時的な計算結果はメモリに持ってOK
- ログをファイルに書き続ける — コンテナ環境ではファイルシステムは揮発性。標準出力に流してログ基盤で収集するアーキテクチャに切り替える
まとめ#
Twelve-Factor Appは、クラウド時代のアプリケーション設計の基本ルール集。12個すべてを完璧に守る必要はないが、特に設定の外部化、ステートレス化、ログのストリーム化は現代のアプリ開発では必須。既存アプリにも少しずつ適用できるので、できるところから始めよう。