フィーチャーフラグとは:デプロイとリリースを切り離す技術
要約
フィーチャーフラグとはコード内の条件分岐で、ビルドなしにデプロイとリリースを切り離す。SLOゲート付きの段階的ロールアウトがブラスト半径を制御する本番パターンだ。キルスイッチは50ms以下のローカル評価と明示的フォールバックが必須。フラグ負債は削除期限とオーナー管理で防ぐ。
フィーチャーフラグとは:デプロイとリリースを切り離す技術
フィーチャーフラグとは、アプリケーションコード内の条件分岐で、新しいビルドをシップすることなく、特定のユーザー、セッション、環境に対してどのコードパスを実行するかを制御する仕組みだ。new_checkout_flow というフラグをユーザーベースの99%に対して false に設定しているなら、そのコードは2週間前にデプロイ済みだ。まだオンにしていないだけ。この仕組みが確立する契約は明確だ: デプロイとリリースは2つの独立したイベントになる。継続的デプロイを規模でおこなうチームにとって、この契約は根幹となる。
デプロイとリリースは別々のイベントだ
ほとんどのチームは、痛いロールアウトの失敗を経験してからこの違いを学ぶ。火曜日の午後に機能がシップされ、水曜日の朝にはリクエストトレースで何かが変な挙動を始め、誰かが調査を開始した時点ではdiffが4コミットと2つのサービス境界にまたがっている。そのシナリオで原因を特定するのは本当に難しい。
フィーチャーフラグは精度を強制する。フラグの背後にあるコードがマージされてデプロイされると、本番環境で不活性な状態で存在し続ける。デプロイが成功したことを確認し、数時間または数日間ベースラインのメトリクスを観察する。それから1%のトラフィックに対してフラグを有効にする。これで帰属可能な変数が1つになった。フラグが有効なパスでエラーレートが上昇しても、コードdiffをデバッグしているのではない。設定値を切り替えているだけだ。
この分離は主に速度の話ではない。ブラスト半径の話だ。1%のセッションに影響するリリースが問題を起こしても、数分で回復できる。100%のセッションに影響するリリースが問題を起こすと、大規模インシデントになる。
実装自体はシンプルだ。TypeScriptでのフラグ評価はこうなる:
const showNewCheckoutFlow = flagClient.variation(
'new_checkout_flow',
{ userKey: session.userId, custom: { plan: user.plan } },
false // フラグサービスに到達できない場合のデフォルト
);
if (showNewCheckoutFlow) {
return renderNewFlow(cart);
}
return renderLegacyFlow(cart);false のデフォルト値は形式的なものではない。フラグ評価サービスがネットワーク分断に陥った場合にユーザーが経験する挙動だ。偶発的にではなく、意図的に定義しろ。
本番環境で重要な4つのフラグタイプ
すべてのフィーチャーフラグが同じ運用目的を果たすわけではない。コードベースと管理ツールで同一視すると、インシデント時の混乱とフラグ負債の蓄積につながる。
リリースフラグは開発とロールアウト中の新機能をゲートする。一時的であることが前提だ。機能ブランチの作業開始時に作成し、機能が100%のユーザーに展開されてチームが安定性を確認した後に削除される。削除日も担当オーナーも決まっていないリリースフラグは、コードベースの恒久的な家具になる。
実験フラグはA/Bテストと多変量実験を動かす。分析のコホート識別子に紐付き、ライフサイクルは実験に制限される。実験が終わればフラグも一緒に消える。よくある失敗: 勝者のバリアントをフラグの背後に無期限に残す判断。2年後、その実験フラグはクリティカルパスの一部になり、どのバリアントが有効か誰も覚えていない。
opsフラグはキルスイッチとサーキットブレーカーだ。リリースフラグとは異なり、永続的なインフラとして設計される。disable_ml_recommendations のようなフラグは、ドキュメントを読まなくても午前3時に使えるものでなければならない。ローカルで評価され、文書化されたフォールバックを持ち、通常の運用中に定期的にテストされるべきだ。
パーミッションフラグはユーザー層、アカウントプラン、ベータコホートによるアクセスを制御する。設計上、長命だ。パーミッションフラグは経緯を知らない読み手にはリリースフラグのように見えることが多い。他のどこよりも明確な命名規則が重要だ。

SLOゲートなしのパーセンテージロールアウトはただの遅いデプロイだ
ほとんどのフィーチャーフラグ実装が止まるのはここだ。プラットフォームチームがロールアウトスケジュールを組む: 月曜日1%、火曜日5%、水曜日25%、金曜日100%。これをプログレッシブデリバリーと呼ぶ。
しかし、各ステップに検証条件のない「プログレッシブ」は、リスクの削減ではなくリスクの先延ばしだ。パーセンテージのダイヤルはエクスポージャーを制御する。安全性を検証しない。
段階的ロールアウトを運用上意味あるものにするのは、各ステージ間のSLOゲートだ。5%から25%に進む前に問わなければならない: フラグが有効なコードパスのエラーレートはSLOバジェット内か?p99レイテンシはコントロールグループと同じ範囲か?エラーバジェットはベースラインより速く消費されていないか?これらが計測されていなければ、ロールアウトスケジュールはタイムラインであって検証ループではない。
本番環境で機能するセットアップ: 2つのSLO評価ウィンドウを定義する。短いウィンドウ(15分)は高速な障害をキャッチする。長いウィンドウ(24時間または1トラフィックサイクル)は緩やかな劣化をキャッチする。どちらかが違反されたら、ロールアウトを停止してオンコールをページする。
パーセンテージはダイヤルだ。SLOウィンドウはゲートだ。両方必要だ。
キルスイッチの設計:必要になる前に作れ
キルスイッチはフォールバックではない。機能コードの最初の1行を書く前に存在しなければならない、ファーストクラスの設計決定だ。
プレッシャーの下で設計されたキルスイッチは、検証されていない前提を抱えたキルスイッチだ。アクティブなインシデント中に、PagerDutyの通知から開いたターミナルセッションで、5人がSlackスレッドを見ている前で初めてテストすることになる。opsフラグがセッションIDでユーザーをターゲットしていて、セッションサービスが現在劣化していることをそこで発見する。最悪のシナリオだ。

本番環境で使うキルスイッチに交渉の余地がない3つの特性:
50ms以下のローカル評価: フラグチェックはリモートの評価サービスへのネットワーク呼び出しであってはならない。評価が劣化し得るサービスに依存していたら、インシデント対応パスに循環依存がある。
明示的なフォールバック値: フラグサービスが到達不能な場合、フラグは何を返すのか?文書化され、コードで定義され、テストされなければならない。「SDKのデフォルト」は答えではない。
依存関係の障害下でのテスト: キルスイッチはカオスエンジニアリングのローテーションに含まれるべきだ。認証が劣化したシナリオ、フラグサービス自体がダウンしたシナリオ、評価エンドポイントへのネットワークレイテンシが2秒を超えたシナリオで検証しろ。
インシデント対応をクリーンに実行するチームはリハーサルしたチームだ。キルスイッチはランブックの一部だ。退屈なものにしろ。
フラグ負債:誰もロードマップに載せない技術的負債
フィーチャーフラグを積極的に採用するチームはフラグ負債を蓄積する。目的を達成したが削除されなかったフラグだ。機能はシップされ、実験は終了し、ベータは終わった。フラグは残った。
50フラグなら些細な問題だ。分散システム全体で500フラグになると、積極的な運用リスクだ。各フラグはインシデント調査中にメンテナンス、テスト、理解が必要なコードブランチだ。午前2時に障害を理解しようとしている開発者は、関連するものを見つけるために40の条件分岐をトレースしたくない。
複数のプラットフォームチームで観察されるパターン: フラグ評価ミドルウェアがp99レイテンシのスタックフレームのトップ5に現れる。原因は大半の場合、リクエストごとに数十の条件を評価する複雑なターゲティングルールを持つ古いフラグで、誰も削除を決断しなかった2年前の意思決定の重みを背負っている。
対策は技術的というよりは組織的だ。作成されるすべてのフラグには3つの属性が必要だ: オーナー、タイプ(リリース、実験、ops、パーミッション)、期待される削除日。リリースフラグは完全ロールアウトから2スプリント以内に削除されるべきだ。実験フラグは実験終了時に削除する。フラグのインベントリはオンデマンドで監査可能であり、エンジニアリングヘルスダッシュボードに表示されるべきだ。
ターゲティングの粒度:エンタープライズ規模で崩壊する次元
ほとんどのフィーチャーフラグプラットフォームはパーセンテージベースのロールアウトと基本的なユーザー属性ターゲティングをサポートする。エンタープライズ規模で浮上するギャップはターゲティングの粒度だ。維持不可能なほど複雑にならずに、十分に具体的なロールアウトルールを表現できる能力。
プラットフォームチームレベルでの本番ロールアウトに有用なターゲティング階層:
環境レベル: 本番、ステージング、プレビュー。最初のゲートであって唯一ではない。
インフラセグメント: データセンター、アベイラビリティゾーン、Kubernetesクラスタ。地理的ブラスト半径の隔離に有用。
アカウントまたはテナント: B2B SaaSプラットフォームでは、ユーザー割合よりもアカウント単位のロールアウトの方が安全なことが多い。統計的なサンプルではなく、アカウント全体のトラフィックパターンを観察できる。
ユーザーコホート: ベータユーザー、内部ユーザー、アクティビティ層別パワーユーザー。
セッション属性: 実験フラグには有用だが、opsフラグには危険。
これをうまく実装しているプラットフォーム(LaunchDarklyとStatsigがSREチームに最も引用される)は、アクティブなロールアウト中にターゲティングロジックを変更するためのエンジニアリング時間を必要とせずに、複雑なルール合成を可能にする。そのセルフサービス能力が、30秒のロールアウト一時停止とプラットフォームチームへのチケットの違いだ。
これをうまく実装していないプラットフォームは、ターゲティングの粒度と運用のシンプルさの間のトレードオフを強制する。そのトレードオフは午前3時に浮上する。
ポストモーテムが繰り返し見つけること
機能に関連する本番インシデントのすべてのポストモーテムは、同じ一連の質問をする。機能はフラグの背後にゲートされていたか?ロールアウトは段階的だったか?ステージ間に検証条件はあったか?インシデントの前にキルスイッチはテストされていたか?
4つの答えがすべてYESなら、インシデントは調整の問題だ。閾値の設定が緩すぎた、ターゲティングルールにエッジケースがあった、ネットワーク分断下でのSDK評価の挙動が考慮されていなかった。これらは設定変更とランブックの更新で解決できる。
いずれかの答えがNOなら、インシデントはアーキテクチャの問題だ。フィーチャーフラグはデバッグの便宜ではない。デプロイのアーキテクチャ決定だ。その決定は機能がシップされる前に存在するか、まったく存在しないかだ。
次のリリースの前に答える価値のある質問: 今夜何か問題が起きたとしたら、このロールアウトについてポストモーテムは何と言うだろうか?