単純なTrue/Falseでは不十分な場合:コードにフィーチャーフラグを配置する

新しい機能を作りました。コードはコンパイルされ、テストも通り、リリースの準備は整っています。しかし、全員に一度にスイッチを切り替えたいわけではありません。まずは社内のテスターだけ、あるいはアジアのユーザーだけ、トラフィックの10%だけ、というように段階的に公開したいケースがあります。

ここでフィーチャーフラグが役立ちます。フィーチャーフラグを使うと、デプロイとリリースを分離できます。つまり、新しいコードを本番環境に配置しても、全員に見えるようにする必要はありません。しかし、コードを条件分岐の混乱した塊にせずに、どのようにフラグを配置すればよいのでしょうか。

最もシンプルなフラグ:ブール値スイッチ

フィーチャーフラグの核心は、条件分岐です。フラグがオンの場合は新しいコードを実行し、オフの場合は古いコードを実行します。最も基本的な形は次のようになります。

if fiturXEnabled:
    show_new_feature()
else:
    show_old_feature()

ここで、fiturXEnabled はブール変数です。True に設定するとユーザーに新機能が表示され、False に設定すると旧機能が表示されます。シンプルです。

ただし、注意点があります。フラグの値はコード実行前に決定されている必要があります。再デプロイせずに値を変更したい場合は、コード自体の外部から値を読み取る必要があります。設定ファイル、環境変数、リモートサービスなどが考えられます。フラグのリモート制御については後述しますが、ここでは単純なブールフラグで十分なケースに焦点を当てましょう。

ブールフラグは、機能が小さく、オン/オフの2状態しか必要ない場合に適しています。例えば、以前は管理者のみが使用できた「レポート印刷」ボタンを、すべてのユーザーに開放したいとします。printReportForAllUsers というフラグを追加します。True の場合、ボタンは全員に表示されます。False の場合、管理者のみに表示されます。これで完了です。

単純では不十分な場合:条件付きフラグ

より細かい制御が必要な場合もあります。新しい機能を特定のIDを持つユーザーだけに表示したい、特定の地域のユーザーだけに表示したい、トラフィックの10%だけにランダムに表示したい、といったケースです。単純なブール値では対応できません。

ここで条件付きフラグが必要になります。フラグ自体は依然としてブール値ですが、その値を決定するロジックがコンテキストに依存します。最もシンプルなアプローチは、フラグの外側に条件を追加することです。

if fiturXEnabled and user.id in trial_user_list:
    show_new_feature()
else:
    show_old_feature()

これでも機能しますが、trial_user_list は別途管理する必要があります。リストが頻繁に変更される場合、再デプロイせずに更新する方法が必要になり、複雑さが増します。

よりクリーンなアプローチは、フラグプロバイダーを使用することです。フラグプロバイダーは、コンテキスト(ユーザーID、地域、デバイスタイプなど)を受け取り、事前定義されたルールに基づいてフラグ値を返す関数またはライブラリです。コードは次のようになります。

if flag_provider.is_enabled("featureX", user=current_user):
    show_new_feature()
else:
    show_old_feature()

内部的には、フラグプロバイダーがルールをチェックします。このユーザーはトライアルグループに属しているか、地域は一致するか、トラフィックの割合に達しているか、などです。フラグを使用するたびにそのロジックを書き直す必要はありません。

コードをクリーンに保つ

フラグが増えるほど、コード内の条件分岐も増えます。規律がなければ、読みづらく、保守が難しいネストされた if 文になってしまいます。

いくつかの原則が役立ちます。

1つのフラグに1つの責務。 複数の条件を1つのフラグに詰め込まないでください。地域とユーザーロールの両方で可視性を制御する必要がある場合は、2つの別々のフラグを使用します。その方が明確で、後で削除しやすくなります。

適切なレベルにフラグを配置する。 フラグは、機能のエントリポイントにできるだけ近い場所に配置します。UIコンポーネントの場合はレンダリング関数、APIエンドポイントの場合はハンドラーが適切です。同じフラグをコードの複数のレイヤーに散らばらせるのは避けてください。

フラグ削除の計画を立てる。 追加するすべてのフラグは一時的なコードです。機能が完全にロールアウトされたら、古いコードパスとフラグを削除します。フラグは見つけやすく、削除しやすいように記述してください。関連するフラグはグループ化し、featureX_enabledfeatureX_percentage のような一貫した命名規則を使用します。

実践的なチェックリスト

次のフィーチャーフラグを書く前に、以下のポイントを確認してください。

  • この機能は単純なブール値で制御できるか、それとも条件付きロジックが必要か?
  • フラグの値はどこから取得するか?ハードコード、設定ファイル、環境変数、リモートサービスのいずれか?
  • フラグは適切なレベル、つまり機能が使用される場所の近くに配置されているか?
  • フラグは単一で明確な責務を持っているか?
  • 後でフラグと古いコードパスを削除する計画はあるか?

次に来るもの

フラグがコードに配置されたら、次は再デプロイせずにその値を変更する方法です。設定ファイルの再読み込み、環境変数の更新、専用のダッシュボードなどを通じて、フラグをリモートで制御する方法が必要です。ここにフィーチャーフラグの真の力があります。機能のオン/オフ、段階的なロールアウト、問題発生時の迅速な対応を、デプロイパイプラインに触れることなく実行できます。

しかし、フラグが乱雑であれば、これらはすべて無意味です。コード内にクリーンで適切に配置されたフラグから始めましょう。残りは後からついてきます。