再デプロイなしでフィーチャーフラグを制御する

想像してみてください。あなたのチームは新しいチェックアウトフローをフィーチャーフラグの背後に隠してリリースしました。ステージングではすべて問題なく動作していました。しかし、本番デプロイから1時間後、モニタリングにカート放棄の急増が表示されました。新しいフローを即座に無効化する必要があります。

その機能を無効化するために、新しいリリースをカットし、ビルドパイプラインを待ち、本番に再デプロイする必要があるとしたら、それは問題です。古いコードが再び稼働する頃には、すでに収益を失い、ユーザーをイライラさせていることでしょう。

これこそが、フィーチャーフラグはデプロイパイプラインに触れることなく、実行時に制御可能でなければならない理由です。アプリケーションが動作している最中にフラグの値を変更できる能力こそが、フィーチャーフラグを単に「フラグ」と名付けられた設定と区別するものです。

最もシンプルなアプローチ: 設定ファイル

フラグを制御する最も直接的な方法は、サーバー上の設定ファイルを使うことです。アプリケーションは起動時にファイルからフラグ値を読み取り、プロセスを再起動せずにリロードをトリガーできます。

例えば、シンプルな flags.json ファイルは次のようになります:

{
  "new-checkout": {
    "enabled": true,
    "description": "ステップを簡略化した新しいチェックアウトフロー"
  },
  "dark-mode": {
    "enabled": false,
    "description": "ダークモードUIの切り替え"
  },
  "recommendation-engine": {
    "enabled": true,
    "rollout-percentage": 25,
    "description": "パーソナライズドレコメンデーションの段階的ロールアウト"
  }
}

この方法は、サーバーが1台か2台の場合にうまく機能します。ファイルを編集し、SIGHUPシグナルを送信するか、定期的なリロード間隔を待てば、アプリケーションが新しい値を取得します。

しかし、このアプローチはすぐに破綻します。アプリケーションが10台のサーバーで動作している場合、10個のファイルを編集する必要があります。1台のサーバーが更新を見逃すと、一部のユーザーだけが新しいフローを目にすることになります。また、変更を行うために本番サーバーへのシェルアクセス権限を持つ誰かが必要になり、ボトルネックと監査証跡の問題が発生します。

環境変数: 改善されているが、依然として限界がある

環境変数は一歩進んだ方法です。アプリケーション起動時に NEW_CHECKOUT_ENABLED=true を設定し、フラグ値をプロセスのライフサイクル全体で利用可能にします。これは特に環境間での差異化に役立ちます。ステージングでは true、本番では false と設定できます。

明らかな制限は、プロセスを再起動せずに環境変数を変更できないことです。日中に機能を停止する必要がある場合、新しい変数値でアプリケーションを再起動しなければなりません。その再起動には、アプリケーションの起動時間やコネクションプールのウォームアップによって、数秒から数分かかる可能性があります。

Kubernetesのようなコンテナ化環境では、ConfigMapを通じてコンテナイメージを再ビルドせずに環境変数を更新できます。しかし、変更を反映するにはPodの再起動が依然として必要です。一部のオーケストレーターはローリング再起動を自動的に処理しますが、古い値が有効な短いウィンドウが存在します。

真の解決策: リモートフラグダッシュボード

チームが複数のサービスにわたってリアルタイム制御を必要とする場合、専用のフラグ管理システムが必要です。これにより、フラグ値を変更するためのWebインターフェースまたはAPIが提供され、アプリケーションは中央のサービスからそれらの値を読み取ります。

実際の動作は次のとおりです:

  1. アプリケーションはフラグ管理プラットフォームの小さなSDKを統合します
  2. 実行時に、アプリケーションはプラットフォームに「ユーザーID 12345に対してフラグ new-checkout は有効ですか?」と問い合わせます
  3. プラットフォームは、ダッシュボードで設定したルールに基づいて応答します
  4. ダッシュボードでフラグ値を変更すると、すべてのアプリケーションインスタンスが数秒以内にそれを取得します

このアプローチは調整の問題を解決します。1つの場所で1つの値を変更するだけで、すべてのサービスのすべてのインスタンスがほぼ即座に更新を認識します。サーバーへのSSHアクセスは不要、ローリング再起動も不要、インスタンス間での値の不整合リスクもありません。

LaunchDarkly、Split、Flagsmithなどのプラットフォームは、この機能を標準で提供しています。フラグ値を確実に配布し、パフォーマンスオーバーヘッドを避けるためのキャッシュ、そして詳細なターゲティングルールを提供するという難しい部分を処理してくれます。

チームに適した方法を選ぶ

適切なアプローチは、チームの現在地と将来の方向性によって異なります。

以下のフローチャートは、どのアプローチが状況に適しているかを判断するのに役立ちます:

flowchart TD A[サーバーの台数は?] -->|少ない| B[設定ファイル] A -->|多い| C[リアルタイム制御が必要?] C -->|はい| D[リモートダッシュボード] C -->|いいえ| E[環境変数] B --> F[メリット: シンプル、新しいツール不要] B --> G[デメリット: サーバーごとに手動、監査なし] E --> H[メリット: オーケストレーターで一元管理] E --> I[デメリット: 再起動が必要、短い遅延] D --> J[メリット: 即時、監査可能、ユーザー単位] D --> K[デメリット: 外部依存、コスト]

設定ファイル は、1~2台のサーバーを持つ小規模チームに適しています。運用オーバーヘッドが最小限で、新しいプラットフォームを学ぶ必要もありません。ただし、アプリケーションが完全な再起動なしで設定をリロードできることを確認してください。

環境変数 は、すでにKubernetesや同様のオーケストレーションを使用している場合にうまく機能します。ConfigMapを通じてフラグ値を管理し、オーケストレーターに再起動を任せることができます。これは専用プラットフォームに投資する前の良い中間点です。

リモートダッシュボード は、複数のサービスがあり、リアルタイム制御が必要な場合、またはエンジニア以外のメンバーがフラグを管理したい場合に必要になります。プロダクトマネージャーは特定のユーザーセグメントに対して機能を有効化できます。エンジニアはスマートフォンから問題のある機能を停止できます。運用チームは誰がいつ何を変更したかを監査できます。

フラグ制御のための実践的チェックリスト

メカニズムを決定する前に、以下の点を確認してください:

  • コードをデプロイせずにフラグ値を変更できますか?
  • 変更は許容可能な時間枠(数秒、数時間ではない)内に反映されますか?
  • 複数のチームメンバー(エンジニア、プロダクトマネージャー、運用)が資格情報を共有せずにフラグを変更できますか?
  • 誰がいつ何を変更したかの監査証跡はありますか?
  • グローバルだけでなく、特定のユーザーやセグメントに対してフラグを変更できますか?
  • このメカニズムはすべての環境(ステージング、本番、カナリア)で機能しますか?

これらのいずれかに「いいえ」と答えた場合、フラグ制御メカニズムを改善する必要があります。

最も重要なこと

核となる原則はシンプルです。フラグを変更するのにデプロイが必要なら、それはフィーチャーフラグを使っているのではありません。たまたま「フラグ」と名付けられた設定を使っているのです。

選択するメカニズムは、チームの規模と運用成熟度に一致する必要があります。シンプルに始めましょう。しかし、50のマイクロサービスにわたって1台のサーバーにも触れずに機能を無効化する必要がある日に備えて計画してください。その日は必ず来ます。そしてその時、リモートフラグ制御を適切に設定しておいて良かったと思うでしょう。

目標は、初日から完璧なフラグシステムを構築することではありません。目標は、本番で何か問題が発生したときに、最初のアクションがデプロイパイプラインを開始することではなく、フラグ値を変更することであることを確実にすることです。