Feature Flagが拓く開発の新潮流 3

フラグ管理のオープンスタンダード「OpenFeature」と評価エンジン「flagd」でFeature Flagの導入課題を解決する

第3回の今回は、Feature Flagの本格導入で直面する「選定したツールへの依存」という課題への対処法について解説します。

石飛 真哉(いしとび しんや)

6:30

はじめに

前回は、Feature Flagシステムを構成する4つの内部要素(Toggle Point / Router / Context / Configuration)と、目的に応じた4つの分類(Release Toggle / Experiment Toggle / Ops Toggle / Permission Toggle)について解説しました。フラグの評価ロジック(=Toggle Router)をアプリケーションコードから分離することの重要性や、フラグが単なるif文以上の構造を持っていることが理解できたかと思います。

しかし、いざFeature Flagを本格的に導入しようとすると、選定したツールへの依存という新たな課題に直面します。現在、Feature Flagの管理サービスやオープンソースのライブラリは数多く存在しますが、それらの多くは独自のSDKを提供しており、利用するにはアプリケーションコードへの独自の組み込みが不可欠です。

特定のベンダーが提供するSDKへの直接的な依存はシステムの柔軟性を損ない、将来的な変更コストを増大させます。将来的なツールの刷新や、多言語が混在するマイクロサービス環境への対応を迫られた際、アプリケーションの至る所に記述されたベンダー固有のToggle Pointは、移行を阻む大きな足枷となります。

第3回となる今回は、こうした課題を解決するために策定されたオープンスタンダードな仕様「OpenFeature」と、そのリファレンス実装である評価エンジン「flagd」について紹介します。

Feature Flagの標準仕様「OpenFeature」

OpenFeatureは、Feature Flagの利用インターフェースを標準化することを目的としたオープンソースプロジェクトです。Cloud Native Computing Foundation(CNCF)のIncubatingプロジェクトとしてホストされており(2026年1月時点)、業界の主要なベンダーも参画しています。

OpenFeatureの最大の狙いは、アプリケーションコードとフラグ評価の実装(プロバイダー)を分離することにあります。

前回で解説した用語を用いて整理します。これまでは特定のベンダーのSDKを導入すると、Toggle Point(呼び出し箇所)とToggle Router(評価ロジック)が密結合になっていました。OpenFeatureは、この間に標準化された抽象レイヤーを1枚挟むことで疎結合を実現します。

  • OpenFeature API(SDK): 開発者が利用する統一されたインターフェース(Toggle Point)を提供しています。バックエンドに何を使っているかに関わらず、Toggle Pointの記述方法は統一されます。
  • Provider: 実際のフラグ評価ロジックを実装する部分です。ここをアダプターのように差し替えるだけで、アプリケーションコードを変更することなく利用するバックエンド(LaunchDarkly、flagd、環境変数など)を切り替えることが可能です。
【引用】https://openfeature.dev/docs/reference/intro

Observabilityの世界におけるOpenTelemetryやGoのdatabase/sqlパッケージなどと同じ役割を果たしています。バックエンドのツールやデータベースが何であれ、計測や操作のためのコードは統一されているのと同様の構造がFeature Flagの世界でも実現されているのです。

Go SDKで見る主要なコンセプト

OpenFeatureの仕様は、実際のコード(SDK)においてどのように表現されているのでしょうか。Go SDKを例に、開発者が日常的に触れることになる重要なコンポーネントを2つ紹介します。

ProviderとClientの設定

OpenFeatureを利用する最初のステップは、SDKへのProviderの登録とアプリケーションが利用するClient(Toggle Router)の生成です。

// 1. Provider の登録
// アプリケーションの起動時に一度だけ行います。
// ここで「どのバックエンドを使うか」を決定します。
openfeature.SetProvider(flagd.NewProvider()) 

// 2. Client の生成
// アプリケーション内の各所では、この Client を通じてフラグを評価します。
// Client名("payment-service")をつけることで、ログやメトリクスの追跡が容易になります。
client := openfeature.NewClient("payment-service")

重要なのは、アプリケーションのビジネスロジック内で登場するのはclientだけであるという点です。SetProviderの行を書き換えるだけで、コード全体に影響を与えることなくバックエンド(flagd、LaunchDarklyなど)を切り替えることができます。

統一的なフラグ評価API(Evaluation API)

OpenFeatureでは、フラグの戻り値の型ごとに明確なメソッドが定義されています。これにより、開発者は型安全にフラグの値を扱うことができます。

  • BooleanValue: 真偽値のフラグ(機能のON/OFFなど)
  • StringValue: 文字列のフラグ(UIの文言変更やAIモデル変更など)
  • FloatValue / IntValue: 数値のフラグ(パラメータ調整など)
  • ObjectValue: 構造化データのフラグ

Go SDKにおけるシグネチャは以下のようになっています。

// 第3引数(defaultValue)が必須であることが重要です
val, err := client.BooleanValue(ctx, "flag-key", false, evalCtx)

ここで特筆すべきは、デフォルト値(Default Value)が必須引数になっている点です。万が一、バックエンド(flagdやSaaS)と通信できなかったり、フラグ定義が見つからなかったりした場合でもアプリケーションが決してクラッシュせず、既知の安全な値で動作し続けられるよう、APIレベルで堅牢性が強制されています。

評価コンテキスト(Evaluation Context)

前回で解説したToggle Context(判定に必要な文脈情報)に対応するのが、OpenFeatureの「Evaluation Context」です。これはOpenFeatureの中で最も重要な概念の1つです。

単にONかOFFかを返すだけの静的なフラグならContextは不要ですが、「有料会員には有効にする」「社内IPからのアクセスならテスト機能を出す」といった高度なターゲティングを行うには、アプリケーション側から「今アクセスしているのは誰か」という情報を評価エンジンに渡す必要があります。

OpenFeatureでは、この情報を標準化された構造(辞書形式のAttributes)として定義しています。

// ユーザーIDや属性情報を含む Context を作成
evalCtx := openfeature.NewEvaluationContext(
    "user-12345", // Targeting Key (ユーザーIDなど)
    map[string]interface{}{
        "email":   "taro@example.com",
        "plan":    "premium",
        "version": "1.5.0",
    },
)

// Context を評価メソッドに渡す
// これにより、バックエンド側で「plan が premium なら true」といった判定が可能になる
showFeature, err := client.BooleanValue(ctx, "new-feature", false, evalCtx)

ベンダー独自のSDKを使う場合、このContextの渡し方はベンダーごとにバラバラでした。OpenFeatureはここを統一しているため、例えばバックエンドをflagdからLaunchDarklyに切り替えたとしても、このEvaluation Contextを構築するアプリケーションコードを書き換える必要はありません。

クラウドネイティブな評価エンジン「flagd」

OpenFeatureはあくまで仕様であり、アプリケーションとフラグ管理システムの間のインターフェースを定めたものに過ぎません。このインターフェースの実装として、実際にフラグの値を判定するための評価エンジン(バックエンド)が不可欠です。

既存のSaaSをバックエンドとして利用することも可能ですが、本節ではOpenFeatureプロジェクトが提供するクラウドネイティブなリファレンス実装であるflagdについて紹介します。

flagdは、Unix哲学に基づいた、シンプルかつ柔軟なFeature Flag評価エンジンです。

it's a small, self-contained binary that evaluates feature flags, uses standard interfaces, and runs just about anywhere.

Feature Flagの評価ロジックと標準インターフェースを内包し、あらゆる環境で実行可能な、軽量かつ自己完結型のバイナリです。

【引用】Quick Start: Cloud Native Feature-Flagging with the OpenFeature Operator

多くのFeature Flag SDKはアプリケーションのプロセス内でライブラリとして動作しますが、flagdはアーキテクチャ的に独立性を高めるアプローチを採用しています。flagdは独立したプロセス(HTTPサーバーやgRPCサーバー)として動作し、アプリケーションはOpenFeature SDKを通じてflagdと通信し、フラグの値を解決します。

例えば、Kubernetes環境においては、flagdをアプリケーションのPodにサイドカーとして配置するパターンが推奨されています。これによりアプリケーションと言語の依存関係を排除し、ネットワークレイテンシも最小限に抑えつつ、フラグ評価の責務をアプリケーションから分離することが可能になります。

また、flagdはGitOpsとの親和性が高いことも特徴です。多くのSaaSはWeb管理画面でフラグを操作しますが、flagdはJSONファイルで定義されたToggle Configuration(設定)を読み込んで動作します。つまり、フラグの設定をGitリポジトリで管理し、Pull Requestベースで変更をレビュー・マージして自動反映させる、という堅牢な開発ワークフローが構築できます。

Goとflagdによる実装例

それでは、実際に手を動かしてflagdを体験してみましょう。ここではアプリケーションとflagdを連携させ、フラグの値を評価するまでの流れを解説します。

1. Toggle Configurationの作成

まず、フラグの定義ファイルを作成します。これが前回で解説したToggle Configurationに当たります。以下の内容でmy-flag-config.jsonというファイルを作成します。

{
  "flags": {
    "new-welcome-message": {
      "state": "ENABLED",
      "defaultVariant": "on",
      "variants": {
        "on": true,
        "off": false
      }
    }
  }
}

2. flagdの起動

次に、Dockerを使ってflagdを起動します。flagdは先ほど作成した設定ファイルを読み込み、ポート8013で待受を開始します。これがToggle Routerの実体となります。

# 作成した設定ファイルをマウントして起動
docker run \
    --rm -it \
    --name flagd \
    -p 8013:8013 \
    -v $(pwd):/etc/flagd \
    ghcr.io/open-feature/flagd:latest start \
    --uri file:/etc/flagd/my-flag-config.json

3. アプリケーション(Go)の実装

最後に、Goのアプリケーションからflagdを利用します。OpenFeature Go SDKとflagd専用のProviderをインストールします。

go get github.com/open-feature/go-sdk-contrib/providers/flagd
go get github.com/open-feature/go-sdk

実装コードは以下のようになります。

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/open-feature/go-sdk-contrib/providers/flagd"
	"github.com/open-feature/go-sdk/openfeature"
)

func main() {
	// 1. Providerの設定
	// flagd Provider を生成し、OpenFeatureのグローバルプロバイダーとして設定します。
	// これにより、以降のAPI呼び出しは全て flagd (localhost:8013) に向かうようになります。
	provider, err := flagd.NewProvider()
	openfeature.SetProvider(provider)

	// 2. Clientの生成
	client := openfeature.NewClient("my-app")

	// 3. フラグの評価 (Toggle Point)
	// 前回で解説した Toggle Point です。
	// ここでは標準化されたAPI (BooleanValue) を使用しているため、
	// バックエンドが flagd であることを意識する必要はありません。
	ctx := context.Background()
	showWelcome, err := client.BooleanValue(
		ctx,
		"new-welcome-message", // フラグのキー
		false, // デフォルト値 (評価失敗時や接続エラー時用)
		openfeature.EvaluationContext{}, // Toggle Context (今回は空)
	)

	if err != nil {
		log.Printf("Evaluation error: %v", err)
	}

	if showWelcome {
		fmt.Println("新しいウェルカムメッセージを表示します (on)")
	} else {
		fmt.Println("従来のメッセージを表示します (off)")
	}
}

このコードを実行すると、flagdの設定通り「新しいウェルカムメッセージを表示します(on)」が出力されるはずです。my-flag-config.jsonのdefaultVariantを“off”に書き換えるとflagdは動的に変更を検知し、アプリケーション側の挙動も再起動なしで切り替わります。

このように、OpenFeatureとflagdを組み合わせることで「コードを変更することなく、外部の設定だけでシステムの振る舞いを制御する」というFeature Flagの本質的な価値を、特定のベンダーに依存しない形で実現できるのです。

おわりに

今回は、Feature Flagの標準仕様であるOpenFeatureと、クラウドネイティブな評価エンジンであるflagdについて紹介しました。

OpenFeatureを採用することで、ベンダーロックインのリスクを回避しながら、統一された作法でFeature Flagを実装する基盤が整います。また、flagdを組み合わせることで、高価なSaaSを契約する前のスモールスタートや、GitOpsを中心としたモダンな開発・運用フローを構築することが可能です。

今回までの3回を通してFeature Flagを導入し、実装する環境は整いました。しかし、ツールが利便性を増す一方で、開発者は「Feature Flagを作りすぎる」というシンプルな課題に直面します。無計画にフラグを増やし続けるとコードの複雑性は増大し、テストは困難になり、誰も管理していないゾンビフラグが蓄積されていきます。

次回は、Feature Flag運用の最大の敵である「Feature Flagがもたらす技術的負債」に焦点を当て、健全なフラグ管理を維持するためのライフサイクルマネジメントについて解説します。

この記事をシェアしてください

人気記事トップ10

人気記事ランキングをもっと見る

企画広告も役立つ情報バッチリ! Sponsored