フィーチャーフラグの運用に関するベストプラクティス
前回の記事でも述べたように、フィーチャーフラグを導入することで大きなメリットを得られる一方、デメリットとして、コードやフラグ管理の複雑性が増大してしまう可能性があります。このような複雑性を抑え、フィーチャーフラグを上手く運用していくためのプラクティスを説明します。なお、ここで説明しきれなかったプラクティスについては、martinfowler.comの記事[注2]やサードパーティー製サービスを提供する企業による書籍[注6][注7]が参考になります。
- [注7] 『Managing Feature Flags』(Adil Aijaz, Pato Echagüe、O'Reilly Media, Inc.、January 2018)
コードの実装に関するプラクティス
フィーチャーフラグを使用したコードの複雑性を抑え、メンテナンスしやすく、テストしやすいコードにするためのプラクティスを2つ説明します。ここで説明するプラクティスの他にも、一般的なプログラミングの設計原則を適用することも有効です。
1つのフィーチャーに関するフラグを複数のレイヤーに挿入することは避ける
フィーチャーフラグをコードに実装する際のよくあるミスとしては、1つのフィーチャーに関するフラグを複数のレイヤーに挿入してしまうことです。例えば、何らかの機能追加の際に、フロントエンドのUIレイヤーのコードとバックエンドのAPIレイヤーのコードに挿入してしまうといったケースです。この場合、片方のレイヤーではフラグがオンだが、もう片方のレイヤーではオフになって意図した通りに動作しないといった問題が発生する可能性があります。また、フラグを取り除く際にも全てのレイヤーから取り除く必要があり、バグの原因となる可能性があります。
この問題の対処法としては、機能追加について考える際に、どのレイヤーにフラグを挿入するかについても考えることです。基本戦略としては、最上位のレイヤーにフラグを挿入することが望ましいです。こうすることで、フラグによる条件分岐がさまざまなレイヤーに散らばることを避けられますし、また、ユーザの情報やリクエストの情報をフラグの値の判断用に受け渡しやすくなります。ただし、データベースのマイグレーションのためにフラグからエンドポイントを取得するといったケースでは、コードのコアの部分にフラグによる条件分岐を挿入する必要があり、全てを最上位のレイヤーに置けば良いというわけではない点には注意が必要です。
振る舞いを切り替えたい箇所からフィーチャーフラグへの依存を分離する
新旧の機能を切り替えるコードをフィーチャーフラグを使って実装する際、以下のように実装するのがシンプルな方法です。
const featureFlagSdk = getFeatureFlagSdk(); function someFunction() { if (featureFlagSdk.getBooleanValue("new-feature-1-enabled")) { runNewFeature1(); } else { runOldFeature(); } }
これで問題ない場合もありますが、変化に対して脆い方法だと言えます。例えば、フィーチャーがオンになる条件を変えたい場合や、フラグの種類を単純なオン・オフ用のフラグからABテスト用のフラグに変えたいといった場合に、ロジックの内部のコードを変更する必要があります。これは、フィーチャーフラグの利用が進むにつれて、コードベース全体がフィーチャーフラグシステムへの依存を強めていくことを意味します。また、ロジックが直接フィーチャーフラグシステムに依存することで、上手くモック化できずユニットテストが書きにくいという問題も発生します。
この問題の対処法としては、振る舞いを切り替えたい箇所からフィーチャーフラグへの依存を分離することです。抽象化レイヤーを挟んで、ロジックからは抽象化レイヤーに依存させます。こうすることで、フィーチャーフラグシステムに対する依存やフィーチャーフラグに関するコードを抽象化レイヤーで一元管理できますし、ロジックのテスタビリティーも向上します。さまざまな実装方法が考えられますが、一例として以下のように実装することができます。
function createFeatureDecisions(featureFlagSdk) { return { isNewFeature1Enabled() { return featureFlagSdk.getBooleanValue("new-feature-1-enabled"); }, // 他のフィーチャーに関する関数 }; } function someFunction(featureDecisions) { if (featureDecisions.isNewFeature1Enabled()) { runNewFeature1(); } else { runOldFeature(); } }
運用や管理に関するプラクティス
チームの開発フローにフィーチャーフラグを組み込んだ時に発生する、運用や管理にまつわる複雑性を抑えるためのプラクティスを3つ説明します。
役割を終えたフィーチャーフラグを削除する仕組みを作る
フィーチャーフラグをある程度の規模でしばらく運用していると、フラグ数の増加によってコードの条件分岐が増え、複雑性が増大するという問題があります。
この問題の対処法としては、役割を終えたフィーチャーフラグを削除する仕組みを作ることです。フィーチャーフラグシステムで対象のフィーチャーフラグを無効化するとともに、関連するコードを削除します。フィーチャーフラグの削除を忘れずに行うためには、フィーチャーフラグを取り除くタスクをチームのバックログに追加する方法や、フィーチャーフラグに期限を付けて通知を受け取る方法など、何らかの仕組みを作ることが有効です。また、アクティブなフィーチャーフラグの数の上限を作るといった方法を取り入れているチームもあります。
フィーチャーフラグを分類して開発フローに組み込む
martinfowler.comの記事[注2]によると、フィーチャーフラグを以下の4つに分類でき、それぞれ特徴や使用されるタイミングが異なります。
- Release Toggles:開発中でテストされていないコードを、絶対にオンにならない機能としてプロダクション環境にリリースするために使用します。
- Experiment Toggles:ABテストを行う際に使用します。
- Ops Toggles:システムのオペレーションに関する側面を制御するために使用します。
- Permissioning Toggles:特定のユーザーにだけ提供する機能やユーザー体験を変更するために使用します。
例えば、ある機能の一連の開発フローにおいて、リリース前はRelease Toggleを使って機能をオフにしておき、開発が完了したら、Experiment Toggleに差し替えてABテストをするといった運用を行うことができます。このようにフィーチャーフラグを分類して開発フローに組み込むことで、フィーチャーフラグの管理がしやすくなります。
フィーチャーフラグの組み合わせではなく、フィーチャーごとにテストする
フィーチャーフラグを導入している場合、フィーチャーフラグの取りうる値の全ての組み合わせをQAフェーズでテストしようとすると、テストパターンが増えすぎてしまい、すぐに破綻してしまいます。
この問題の対処法としては、フィーチャーフラグの組み合わせではなく、フィーチャーごとにテストすることです。これを実現するためには、フィーチャー同士が他のフィーチャーに影響を与えないように実装することが重要です。ただし、どうしてもフィーチャー同士が依存してしまうことはあり得るので、その場合のみ依存関係にあるフィーチャーフラグの値の組み合わせをテストすることで、テストパターンの増大を抑えることができます。
まとめ
以上、本記事ではフィーチャーフラグの導入方法やその比較、フィーチャーフラグの運用に関するベストプラクティスを説明しました。
フィーチャーフラグの導入において、全てのチームにとって最適な単一の方法はないため、現在求める要件や将来的に変化しそうな要件に照らし合わせて、フィーチャーフラグの導入戦略を決定する必要があります。その際に、本記事で説明したメリットや注意点、比較内容が参考になれば幸いです。また、フィーチャーフラグの運用時には、フィーチャーフラグのメリットを享受しつつデメリットを最小限に抑えるために、本記事で説明したプラクティスを参考にしてみてください。
次回は、市場に存在する各種サードパーティー製サービスやOSSの詳細な比較を行い、具体的なシステムの導入を検討している方の参考になるような情報を提供します。