Datadog のFeature Flags (機能フラグ) の使い方とユースケース
Datadog が去年買収したEppo というFeature Flag の会社があり、先日Datadog と統合されたFeature Flags がGA され、色々触ってみたので、その仕組みや活用方法についていろいろと考えてみます。
Feature Flag とは
Feature Flag(機能フラグ)は、特定の機能をアプリケーションレイヤで動的に有効化または無効化するための技術です。雑に表現すると以下のような感じです。要するにただのif 文なので、まったく難しい技術ではありません。
if (newFeatureEnabled === true) { // 新しい機能を使う } else { // 古い機能のまま }
ちなみに以下の資料がとても参考になります。
で、なんでこんなことをするかというと、とても手軽に細かい単位でリリースのコントロールができるからです。インフラ側でリリースをコントロールすることももちろんできます。例えばロードバランサーで振り分けを制御し、新しいバージョンのサーバー群にだけトラフィックを流すといったことも可能ですが、アプリケーションレイヤでのFeature Flag の方がより細かい制御が可能であり、またインフラ側の変更を伴わないため、同じバージョンの中で、あるユーザーには新機能を有効化し、別のユーザーには無効化するといったことが可能になります。例えば過去に私は以下のような理由でFeature Flag を活用しました。
コア機能の改修
ユーザーからあるバグの報告があり、バックエンド側でのDBクエリの修正が必要なことが分かったのですが、そのクエリをラップしているクラスが色々なところで使われており、変更影響が大きいと予想されました。そこで、いったんFeature Flagで新しいクエリを有効化し、特定のユーザーグループで試し、問題がなければ徐々に対象を広げていく、という形でリリースを進めました。
UXの改善
改善とあえて表現したのは、開発側はUXの改善と思っていたのですが、実際にユーザーにとってはまったく改善でないことがそれなりにあります(ユーザー側でもよく経験すると思います)。こういった機能に関して、ユーザー側のフィードバックを聞きながら、必要に応じてすぐに切り戻しできるようにするために使いました。
セキュリティの強化
セキュリティを強化することで、しばしばこれまで使えていた機能が使えなくなる場合があります。例えばXSSの対策において、不要なHTML入力欄を廃止して通常のテキストのみ受け付けるようにする場合、やるべきとは分かっているのですが、既存ユーザーからの反発が予想されます。「これまで見やすいようにこの文章を装飾してたのにできなくなるのはけしからん」といった感じです。その場合、Feature Flagを使って新しいセキュリティ設定を有効化し、問題なければそのまま、問題があればいったん戻したり、ユーザー側とコミュニケーションを取りながら、いい方法を模索していくことができます。
Feature Flag の課題
で、実は上記はおそらくあまり上手でない使い方を含むというか、妥協点を探るためのFeature Flag という使い方を私も多くしていたので、色々な問題が後から出てきます。
まずシンプルにFeature Flag が増えすぎてしまいます。Feature Flag につい甘えてしまうのです。つまり、「新機能を作ったけど怖いからいったんFeature Flag で分岐しておくか」を繰り返してしまい、あっという間にその数が増えてしまいます。増えたFeature Flag は管理されず、削除されず、そのまま放置されてしまいます。結果として、コードが煩雑になり、技術的負債となりがちです。コードの削除は面倒ですし、評価されませんし、なんだかんだ怖いものなので先送りします。なので増える一方なのです。
それに関連して、Feature Flag の管理が不十分だと、例えばすでに100%のユーザーが新機能を使っているにもかかわらず古いコードパスが残り続けたり、残り1% のユーザーのために古いコードパスを維持し続けなければならなかったりします。これが地味に辛いです。甘えたツケがあとでまわってくる感じです。
そして、ビジネス的な観点でいうと、当然のことですが、あるアプリケーションがユーザー全員を100% 満足させることは不可能です。その改善がすべてのユーザーのためにならないとしても、ビジネス的にその改修がすべきことなのであれば、どこかで踏ん切りをつけてFeature Flag を使わず全体へリリースしなければなりません。Feature Flag は(たいていの場合は)一時的なものという意識を持っておくことで、技術的負債を最小限に抑えることができます。
で、このような流れから、Feature Flag をSaaS で管理したいというのは自然な流れだと思います。SaaS であれば、本番のコードやデータベースと切り離してFeature Flag の状態を統合的に管理できますし、もちろんUI で簡単に切り替えできます。さらに、より柔軟な段階リリースやA/B テスト、特定ユーザーグループへのターゲティングなども可能になり、品質の高いリリースと、データに基づく機能改善が実現できます。これを手動でやろうとするとなかなかしんどいです。
さらに、生成AI の登場により、Feature Flag は非常に需要が出てきていると考えています。というのも、新しい機能を一から作るのが簡単になったがゆえに、上述したようなデータドリブンアプローチと、製品の品質保証のためのある種のブレーキとしてFeature Flag が働くためです。要するに新しい機能をより気軽にかつ安全に試し、フィードバックループを高速に回すために活用できます。
Datadog の Feature Flag
正式な機能名としてはFeature Flags と複数形ですが、今回は単数形で呼びます。
Datadog のFeature Flag は買収したEppo をベースにして製品に統合しています。クライアントサイド、サーバーサイドいずれも対応しており、モバイルアプリも対応しています。ほかのFeature Flag 製品と違った唯一無二の強みが、Datadog のメトリクスやログ、APM やRUM と統合されていることで、フラグを切り替えた際の影響を相関づけて詳細に分析できることです。例えば新しい機能を有効化したユーザーグループでエラー率が上がっていないか、パフォーマンスが低下していないか、ユーザーエンゲージメントが向上しているかなどを簡単に確認できます。例えばクライアントサイドであれば、UI/UX の変更と、セッションリプレイの組み合わせで、ユーザーが新しい機能による改善を正しく体験できているか視覚的に把握することも可能です。
クライアントサイド
Web アプリにおいては、Feature Flag の実装は大体サーバーサイドで行うと思います。クライアントサイドだとFeature Flag での振り分けロジックがユーザーに丸見えなので、使う際には注意が必要です。ただ、例えば製品のHP を静的サイトとして実装する場合の広告の出し分けをしたい、といったケースなどでは十分に効果を発揮すると思います。モバイルアプリではクライアントサイドでの実装が主になります。
まずはシンプルにクライアントサイドで試します。事前にDatadog 側でdemo-client-flag という名前のフラグを作成しておきます。


以下をindex.html として保存してブラウザで開くとフラグの値が表示されます。esm.sh でCDN 経由でOpenFeature とDatadogProvider をインポートしていますが、実際のプロジェクトではnpm などでインストールして使うことになると思います。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>Datadog Feature Flags</title> </head> <body> <h1>Feature Flag テスト</h1> <div id="result"></div> <script type="module"> import { DatadogProvider } from 'https://esm.sh/@datadog/openfeature-browser' import { OpenFeature } from 'https://esm.sh/@openfeature/web-sdk' const provider = new DatadogProvider({ applicationId: 'APPLICATION_ID', clientToken: 'CLIENT_TOKEN', site: 'datadoghq.com', env: 'prod', }) const evaluationContext = { targetingKey: 'user-123', } try { await OpenFeature.setProviderAndWait(provider, evaluationContext) } catch (e) { console.error('Failed to initialize Feature Flags:', e) } const value = OpenFeature.getClient().getBooleanValue('demo-client-flag', false) document.getElementById('result').textContent = `demo-client-flag: ${value}` </script> </body> </html>
Client Token と Application ID はRUM のApplication Management の画面(左ペインのDigital Experience から移動)から作成します。とはいえ必ずしも例えばセッションリプレイなどの機能は使う必要はないです。
Feature Flag をアプリ側で実装すると、https://preview.ff-cdn.datadoghq.com/precompute-assignments?dd_env=** 宛てにEvaluation Context を送信し、その結果をもとにフラグの値を取得して、機能の有効化/無効化を行います。ということで、Datadog サーバー側の障害時に安全に処理できるよう、きちんとエラーハンドリングはやっておくべきです。

Datadog 側ではフラグの値をかなり柔軟にコントロールできます。まずはシンプルに下記スクリーンショットのようにルールを設定すれば、50% のユーザーに対してフラグがtrue, 残りの50% に対してfalse になるようにできます。

この場合、Evaluation Context を送る際に、サーバー側でおそらくハッシュ的な計算をして振り分けているようです。ですので、同じEvaluation Context を送れば、必ず同じフラグの値が返ってきます。そういうわけで、ユーザーID などで振り分ける際には、同じユーザーに一貫したフラグと体験を提供できます。
ちなみにこのあたり、現時点で設定がやや煩雑というか、boolean の二値の値なのにMultiple Variants が設定できるなど、直感的でないのが気になります。例えば画像では50%をtrue、50% をfalseにしたうえで、さらにロールアウトを50%、デフォルト値をfalse にしているので、最初の振り分けルールでは25% のユーザーがtrue, 75% がfalse (ロールアウトは50% だがデフォルトfalse なのでtrue に選ばれなかったユーザーが全部false になる)という、書いててもややこしいことになります。とはいってもboolean ではない値であれば、multiple variants のユースケースはあると思いますが、Feature Flag を使うユースケースの8割くらいはboolean だと思いますので、one valiant でシンプルに実装したほうが楽です。

サーバーサイド
同様にNodejs で試します。
import tracer from 'dd-trace'; import { OpenFeature } from '@openfeature/server-sdk'; tracer.init({ experimental: { flaggingProvider: { enabled: true, } } }); await OpenFeature.setProviderAndWait(tracer.openfeature); const client = OpenFeature.getClient(); const evaluationContext = { targetingKey: 'user-123', }; const value = await client.getBooleanValue('demo-server-flag', false, evaluationContext); console.log('demo-server-flag:', value);
demo-server-flag という名前でdatadog 側でフラグを作成しておいて、上記コードを実行するとフラグの値が取得できます。 なお、ドキュメントにも記載がありますが、Nodejs の場合setProvider とsetProviderAndWait の挙動に注意します。前者はawait 不要ですぐに次の行以下に進むのに対し、後者はProvider の初期化が完了するまで同期的に待機します。それゆえ、setProvider を使う場合は最初のうちはデフォルト値が返ってきてしまう可能性があるので、フラグ分岐を保証したい場合はsetProviderAndWait を使うのが安全です。
Feature Flag のユースケース
いくつかユースケースを挙げてみます。以下はすべてサーバーサイドのフラグで試しています。
RUM / APM / Error Tracking / Monitor と合わせて新機能を安全にリリース
デモアプリのソースコードは省略しますが(今だとAI で簡単に作れますので)、Nodejs で簡単なデモアプリを作りました。Feature Flag のほかに、RUM やAPM を有効化しています。Datadog 側で50% の確率でtrue になるフラグを作成しておいて、true の場合は新機能であるチャットボットを表示しますが、残念ながら100% の確率で500 エラーを吐くようにしています。なお、テスト目的のために画面更新のたびにevaluationContext が乱数で変化するようにしています。

まず機能フラグ画面側でエラー率が出ます。50% の確率でtrue になりますが、true になれば必ず500 エラーになります。遅延のグラフでは青と赤の2つの線が出ていますが、エラーのグラフでは赤の棒しか出ていないのはこのためです。

次に、APM トレースを見ます。RED メトリクスを確認すると、急にエラーが増えているのが分かります。エラーをトレースから深堀すると、Error: AI Chat service is not available yet. というエラーが出ているのが分かります。


次に、RUM のセッションリプレイを見ます。フラグがtrue になったユーザーのセッションリプレイを見てみると、例えばチャットボットのエラーが発生している様子を視覚的に確認できます。これにより、ユーザーがどのような状況でエラーに遭遇しているのか、どのような操作をしているのかなどをマウス挙動も含めて把握できます。

そして、このようなチャットボットエラーが発生すると、ユーザー体験の悪化につながり、その一例として例えばチャット送信ボタンを連打する、といったようなフラストレーションスコアとしてDatadog 側でカウントされる行動も確認できます。

Error Tracking では、フラグがtrue になったユーザーのエラーを確認できます。APM でも確認できますが、Error Tracking では新規エラーとしてグルーピングされた状態で確認できますので、他のアプリケーションも稼働している状況においては、このリリースが原因であるエラーを早期に発見しやすくなります。

そして、Error Tracking による新規エラーの検知をトリガーにモニターを設定すれば、機能フラグで新規機能をリリースし、アラートが発生したらフラグを切り戻す、といったことが可能になります。そして何より、切り戻しまでのトレースやセッション情報がDatadog に保存されるため、切り戻した後に原因調査→対応の流れもスムーズに行えます。

XSS 対策
あるメモアプリでは、XSS 対策のためにscript タグを無効にしていますが、それではXSS 対策として不十分のため、HTML タグ自体をすべて無効にしようかと考えました。しかし、既存ユーザーの中には、重要な文字を赤字にするなど、すでにHTML 装飾を行っており、いきなり無効にするのはリスクがあります。

そこで、DOMPurify を使って安全なHTML タグだけを許可するようにして、Feature Flag で切り替えられるようにします。ただし、通常はサニタイズ済のテキストがエラーもなく表示されますので、エラートラッキングで検知、というよりは、Feature Flag の段階リリースをより丁寧に行い(例えば10% だけ最初は有効にしてしばらく待つなど)、ユーザーからのフィードバックを聞きながら、必要に応じて切り戻すなどの対応を行うことになると思います。

まとめ
本記事では、Feature Flag の概要と、Datadog のFeature Flag の特徴や実装方法、ユースケースについて紹介しました。AI で新機能開発のスピードが加速すると、それを安全にリリースするためのブレーキとしてFeature Flag の重要性がさらに高まると考えています。そして、Datadog のFeature Flag は、APM などの既存のObservability 機能と統合されているので、フラグの切り替えによる影響を詳細に分析し、切り戻しの判断を迅速かつ定量的に行うことができます。

