Go 機能管理ライブラリは、機能フラグに基づいてアプリケーション機能を開発および公開する方法を提供します。 新しい機能が開発されると、多くのアプリケーションでは、その機能を有効にすべきタイミングや、有効にすべきタイミングなど、特別な要件が発生します。 このライブラリは、これらのリレーションシップを定義する方法を提供し、一般的な Go コード パターンに統合して、これらの機能を公開できるようにする方法も提供します。
機能フラグは、Go アプリケーションが機能を動的にオンまたはオフにする方法を提供します。 開発者は、条件付きステートメントのように簡単なユース ケースで機能フラグを使用できます。
Go 機能管理ライブラリを使用する利点の一部を次に示します。
- 機能管理のための一般的な規則
- 参入障壁の低さ
- Azure App Configuration 機能フラグ ソース
- 機能フラグの有効期間管理
- 構成値はリアルタイムで変更される可能性があります
- シンプルなシナリオから複雑なシナリオまで対応
- オン/オフ機能の切り替え
- サーバーへの呼び出しに基づいて機能の状態を動的に評価
Go 機能管理ライブラリはオープン ソースです。 詳細については、GitHub リポジトリを参照してください。
機能フラグ
機能フラグは、有効または無効にできます。 フラグの状態は、機能フィルターを使用して条件付きにすることができます。
機能フィルター
機能フィルターは、機能を有効にする必要があるシナリオを定義します。 機能がオンかオフか評価される場合に、機能フィルターの一覧は、いずれかのフィルターがその機能を有効にする必要があると決定するまで走査されます。 この時点で、その機能は有効であるとみなされ、機能フィルターの走査は停止します。 どの機能フィルターも機能を有効にすることを示していない場合は、無効とみなされます。
例として、Microsoft Edge ブラウザーの機能フィルターを設計できます。 この機能フィルターは、HTTP 要求が Microsoft Edge から派生したものである限り、そのフィルターに接続されているすべての機能をアクティブにします。
機能フラグの構成
Go 機能管理では、組み込みの機能フラグ プロバイダー azappconfigによって Azure App Configuration で定義された機能フラグと、他のプロバイダーによって定義された機能フラグを使用するための FeatureFlagProvider インターフェイスを介した拡張ポイントの使用がサポートされています。
import (
"context"
"log"
"github.com/Azure/AppConfiguration-GoProvider/azureappconfiguration"
"github.com/microsoft/Featuremanagement-Go/featuremanagement"
"github.com/microsoft/Featuremanagement-Go/featuremanagement/providers/azappconfig"
)
// ... ...
// Load Azure App Configuration
appConfig, err := azureappconfiguration.Load(ctx, authOptions, options)
if err != nil {
log.Fatalf("Failed to load configuration: %v", err)
}
// Create feature flag provider
featureFlagProvider, err := azappconfig.NewFeatureFlagProvider(appConfig)
if err != nil {
log.Fatalf("Error creating feature flag provider: %v", err)
}
// Create feature manager
featureManager, err := featuremanagement.NewFeatureManager(featureFlagProvider, nil)
if err != nil {
log.Fatalf("Error creating feature manager: %v", err)
}
機能フラグの宣言
次の例は、JSON ファイルで機能フラグを設定するために使用される形式を示しています。
{
"feature_management": {
"feature_flags": [
{
"id": "FeatureT",
"enabled": true
},
{
"id": "FeatureU",
"enabled": false
},
{
"id": "FeatureV",
"enabled": true,
"conditions": {
"client_filters": [
{
"name": "Microsoft.TimeWindow",
"parameters": {
"Start": "Wed, 01 May 2019 13:59:59 GMT",
"End": "Mon, 01 Jul 2019 00:00:00 GMT"
}
}
]
}
}
]
}
}
JSON ドキュメントの feature_management セクションは、機能フラグの設定を読み込む規則によって使用されます。 機能フラグ オブジェクトは、feature_flags セクションの feature_management 配列に一覧表示されている必要があります。
上記のセクションでは、3 つの異なる機能が見られます。 機能フラグには id および enabled プロパティがあります。
id は、機能フラグを特定および参照するために使用される名前です。
enabled プロパティは、機能フラグの有効な状態を指定します。 有効が偽の場合、機能は OFF です。
enabledが true の場合、機能の状態は条件によって異なります。 条件がない場合、機能は オンになります。 条件があり、それらが満たされている場合、機能は オンになります。 条件があり、それらが満たされていない場合、機能は オフになります。 conditions プロパティは、機能を動的に有効にするために使用される条件を宣言します。 機能はclient_filters配列内でその機能フィルターを定義します。
FeatureV は、Microsoft.TimeWindow という名前の機能フィルターを指定します。 このフィルターは、構成可能な機能フィルターの例です。 フィルターに Parameters プロパティがある例で確認できます。 このプロパティは、フィルターの構成に使用されます。 この場合、機能がアクティブになる開始時間と終了時間が構成されます。
feature_management セクションの詳細なスキーマはこちらで見つけることができます。
詳細設定: 機能フラグ名でのコロン ':' の使用は禁止されています。
オン/オフ宣言
次のスニペットは、単純なオン/オフ機能の機能を定義する別の方法を示しています。
{
"feature_management": {
"feature_flags": [
{
"id": "FeatureT",
"enabled": "true"
},
{
"id": "FeatureX",
"enabled": "false"
}
]
}
}
要件タイプ
機能フラグの requirement_type プロパティは、フィルターが機能の状態を評価する場合に Any または All ロジックを使用する必要があるかどうかを決定するために使用されます。
requirement_type を指定しない場合の既定値は Any です。
-
Anyは、1 つのフィルターだけが true と評価されると、その機能が有効になることを意味します。 -
Allは、機能を有効にするために、すべてのフィルターが true と評価される必要があることを意味します。
requirement_type の All は走査を変更します。 まず、フィルターがない場合、この機能は無効になります。 次に、フィルターのいずれかがその機能を無効にする必要があると判断するまで、機能フィルターが走査されます。 どのフィルターも機能を無効にすることを示していない場合は、有効とみなされます。
{
"feature_management": {
"feature_flags": [
{
"id": "FeatureW",
"enabled": "true",
"conditions": {
"requirement_type": "All",
"client_filters": [
{
"name": "Microsoft.TimeWindow",
"parameters": {
"Start": "Wed, 01 May 2019 13:59:59 GMT",
"End": "Mon, 01 Jul 2019 00:00:00 GMT"
}
},
{
"name": "Percentage",
"parameters": {
"Value": "50"
}
}
]
}
},
]
}
}
上記の例では、FeatureW は requirement_type の All を指定します。つまり、その機能を有効にするには、すべてのフィルターが true と評価される必要があります。 この場合、指定された時間枠に 50% のユーザーに対してこの機能が有効になります。
従量課金
機能管理の基本フォームは、機能フラグが有効かどうかを確認し、その結果に基づいてアクションを実行することです。 機能フラグの状態の確認は、FeatureManager の IsEnabled メソッドを使用して行われます。
// Create feature flag provider
featureFlagProvider, err := azappconfig.NewFeatureFlagProvider(appConfig)
if err != nil {
log.Fatalf("Error creating feature flag provider: %v", err)
}
// Create feature manager
featureManager, err := featuremanagement.NewFeatureManager(featureFlagProvider, nil)
if err != nil {
log.Fatalf("Error creating feature manager: %v", err)
}
// Check if feature is enabled
enabled, err := featureManager.IsEnabled("FeatureX")
if err != nil {
log.Printf("Error checking feature: %v", err)
return
}
if enabled {
// Do something
}
機能フィルターの実装
機能フィルターを作成すると、定義した条件に基づいて機能を有効にする方法を得られます。 機能フィルターを実装するには、FeatureFilter インターフェイスを実装する必要があります。
FeatureFilter には、 Evaluateという名前のメソッドがあります。 機能がフィルターに関連付けられている場合、 Evaluate メソッドは評価中に呼び出されます。
Evaluateがtrueを返した場合、この機能は有効と見なされます。
type FeatureFilter interface {
// Name returns the identifier for this filter
Name() string
// Evaluate determines whether a feature should be enabled based on the provided contexts
Evaluate(evalCtx FeatureFilterEvaluationContext, appCtx any) (bool, error)
}
type FeatureFilterEvaluationContext struct {
// FeatureName is the name of the feature being evaluated
FeatureName string
// Parameters contains the filter-specific configuration parameters
Parameters map[string]any
}
次のスニペットは、カスタマイズされた機能フィルターを実装する方法を示しています。
type MyCustomFilter struct{}
func (f *MyCustomFilter) Evaluate(ctx context.Context, context *FeatureFilterEvaluationContext) bool {
// Custom logic to determine if feature should be enabled
if satisfyCriteria() {
return true
}
return false
}
func (f *MyCustomFilter) Name() string {
return "MyCustomFilter"
}
機能フィルターは、 FeatureManagerの作成時に指定することによって登録されます。 カスタム機能フィルターにコンテキストが必要な場合は、 FeatureFilterEvaluationContext パラメーターを使用して渡すことができます。
// Register custom filters
options := &featuremanagement.Options{
Filters: []featuremanagement.FeatureFilter{
&MyCustomFilter{},
},
}
// Create feature manager with custom filters
featureManager, err := featuremanagement.NewFeatureManager(featureFlagProvider, options)
if err != nil {
log.Fatalf("Error creating feature manager: %v", err)
}
フィルター エイリアス属性
機能フィルターが機能フラグに登録されると、その名前が既定のエイリアスとして使用されます。 この識別子をオーバーライドするには、 Name() メソッドを実装します。このメソッドは、機能フラグ内でフィルターを参照するときに構成で使用する名前を指定します。
不足している機能フィルター
特定の機能フィルターに対して機能が有効に構成されていて、その機能フィルターが登録されていない場合は、機能が評価されるときにエラーが返されます。
組み込み機能フィルター
featuremanagement パッケージには、TimeWindowFilterとTargetingFilterの 2 つの機能フィルターがあります。
組み込み機能フィルターにはそれぞれ独自のパラメーターがあります。 こちらは、機能フィルターの一覧と例です。
Microsoft.TimeWindow
このフィルターは、時間枠に基づいて機能を有効にする機能を提供します。
End のみ指定された場合、その時間までその機能はオンとみなされます。
Start のみ指定された場合、その時点以降のすべての時点で機能はオンとみなされます。
"client_filters": [
{
"name": "Microsoft.TimeWindow",
"parameters": {
"Start": "Wed, 01 May 2019 13:59:59 GMT",
"End": "Mon, 01 Jul 2019 00:00:00 GMT"
}
}
]
Microsoft.Targeting
このフィルターは、対象ユーザーに対して機能を有効にする機能を提供します。 対象設定の詳細については、以下の「対象設定」セクションで説明します。 フィルター パラメーターには、ユーザー、グループ、除外ユーザー/グループ、およびその機能にアクセスできるユーザーベースの既定の割合を記述する Audience オブジェクトが含まれます。
Groups セクションに一覧表示されている各グループ オブジェクトは、グループのメンバーの何パーセントがアクセスできるかも指定する必要があります。 ユーザーがExclusion セクションで指定されている場合、そのユーザーが直接指定されている場合、またはそのユーザーが除外されているグループに属している場合に、機能は無効になります。 それ以外の場合、Users セクションでユーザーが直接指定された場合、またはユーザーがいずれかのグループ ロールアウトの割合に含まれる場合、またはユーザーが既定ロールアウトの割合に含まれる場合、そのユーザーの機能が有効になります。
"client_filters": [
{
"name": "Microsoft.Targeting",
"parameters": {
"Audience": {
"Users": [
"Jeff",
"Alicia"
],
"Groups": [
{
"Name": "Ring0",
"RolloutPercentage": 100
},
{
"Name": "Ring1",
"RolloutPercentage": 50
}
],
"DefaultRolloutPercentage": 20,
"Exclusion": {
"Users": [
"Ross"
],
"Groups": [
"Ring2"
]
}
}
}
}
]
ターゲット設定
対象設定は、開発者が新しい機能をユーザー ベースに段階的にロールアウトできるようにする機能管理戦略です。 この戦略は、対象ユーザーと呼ばれる一連のユーザーを対象設定するという概念に基づいて構築されています。 対象ユーザーは、特定のユーザー、グループ、除外されたユーザー/グループ、およびユーザー ベース全体の指定された割合で構成されます。 対象ユーザーに含まれるグループは、メンバーの合計数に対する割合にさらに分けることができます。
次の手順は、新しい「ベータ」機能の段階的なロールアウトの例を示しています。
- 個人ユーザーのジェフとアリシアには、ベータ版へのアクセスが付与されています。
- 別のユーザーであるマークは、オプトイン要求をして、含まれました。
- 「Ring1」ユーザーと呼ばれるグループの 20% がベータ版に含まれています。
- ベータ版に含まれる "Ring1" ユーザーの数は、最大 100% 増加します。
- ユーザー ベースの 5% がベータ版に含まれています。
- ロールアウト率は最大 100% まで増加し、機能は完全にロールアウトされます。
機能をロールアウトするこの戦略では、これに含まれる Microsoft.Targeting 機能フィルターを通してライブラリに組み込まれています。
ユーザーのターゲット設定
対象設定フィルターは、機能をオンにする必要があるか評価するために、ターゲット設定コンテキストに依存します。 この対象設定コンテキストには、現時点でどのユーザーが評価されているか、どのグループに属しているかなどの情報が含まれています。 ターゲット コンテキストは、IsEnabledWithAppContext が呼び出されたときに直接渡す必要があります。
// ... ...
// Create targeting context
targetingCtx := featuremanagement.TargetingContext{
UserID: "test_user",
Groups: []string{"Ring1"},
}
// Check if feature is enabled for the user
enabled, err := featureManager.IsEnabledWithAppContext("Beta", targetingCtx)
if err != nil {
log.Printf("Error checking feature: %v", err)
return
}
if enabled {
// Feature is enabled for this user
}
対象設定の除外
対象ユーザーを定義する場合、ユーザーやグループを対象ユーザーから除外できます。 除外は、ある機能をユーザー グループにロールアウトする場合に、一部のユーザーまたはグループをロールアウトから除外する必要がある場合に役立ちます。 除外は、対象ユーザー Exclusion プロパティにユーザーとグループの一覧を追加することで定義されます。
"Audience": {
"Users": [
"Jeff",
"Alicia"
],
"Groups": [
{
"Name": "Ring0",
"RolloutPercentage": 100
}
],
"DefaultRolloutPercentage": 0,
"Exclusion": {
"Users": [
"Mark"
]
}
}
上記の例では、Jeff と Alicia という名前のユーザーに対してこの機能が有効になっています。 また、Ring0 という名前のグループのユーザーに対しても有効です。 ただし、ユーザーの名前が Mark である場合、そのユーザーがグループ Ring0 に属しているかに関係なく、機能は無効になります。 除外は対象設定フィルターの残りの部分よりも優先されます。
バリエーション
アプリケーションに新しい機能が追加される場合、その機能に複数の異なる設計オプションがある場合があります。 設計を決定するための一般的なソリューションは、何らかの形式の A/B テストです。 A/B テストでは、ユーザー ベースのさまざまなセグメントに異なるバージョンの機能を提供し、ユーザーの操作に基づいてバージョンを選択します。 このライブラリでは、機能のさまざまな構成をバリアントで表現することで、この機能が有効になります。
バリアントは、機能フラグを単なるオン/オフのフラグ以上のものにします。 バリアントは機能フラグの値を表し、文字列、数値、ブール値、あるいは構成オブジェクトである場合もあります。 バリアントを宣言する機能フラグは、それぞれのバリアントをどのような状況で使用する必要があるかを定義する必要があります。この詳細については、「バリアントの割り当て」セクションで扱っています。
type Variant struct {
// Name uniquely identifies this variant
Name string
// ConfigurationValue holds the value for this variant
ConfigurationValue any
}
バリアントの取得
各機能について、FeatureManager の GetVariant メソッドを使用してバリアントを取得できます。 バリアント割り当ては現在評価されているユーザーに依存し、その情報は渡したターゲット コンテキストから取得されます。
targetingCtx := featuremanagement.TargetingContext{
UserID: "Adam",
}
variant, err := featureManager.GetVariant("TestVariants", targetingCtx)
if err != nil {
log.Printf("Error getting variant: %v", err)
return
}
if variant != nil {
variantConfiguration := variant.Configuration
// Do something with the resulting variant and its configuration
}
バリアント機能フラグの宣言
通常の機能フラグと比較して、バリアント機能フラグには variants と allocation の 2 つの追加プロパティがあります。
variants プロパティは、この機能に対して定義されたバリアントを含む配列です。
allocation プロパティは、これらのバリアントを機能に割り当てる方法を定義します。 通常の機能フラグを宣言するのと同様に、バリアント機能フラグも JSON ファイルで設定できます。 こちらにバリアント機能フラグの例を示します。
{
"feature_management": {
"feature_flags": [
{
"id": "MyVariantFeatureFlag",
"enabled": true,
"allocation": {
"default_when_enabled": "Small",
"group": [
{
"variant": "Big",
"groups": [
"Ring1"
]
}
]
},
"variants": [
{
"name": "Big"
},
{
"name": "Small"
}
]
}
]
}
}
バリアントの定義
各バリアントには名前と構成の 2 つのプロパティがあります。 名前は特定のバリアントを指すのに使われ、構成はそのバリアントの値です。 この構成は configuration_value プロパティを使用して設定できます。
configuration_value は、文字列、数値、論理値、構成オブジェクトのいずれかに設定できるインライン構成です。
configuration_value が指定されていない場合は、返されたバリアントの Configuration プロパティは nil になります。
variants プロパティの下で、各機能に対して利用可能なすべてのバリアントのリストが定義されます。
{
"feature_management": {
"feature_flags": [
{
"id": "MyVariantFeatureFlag",
"variants": [
{
"name": "Big",
"configuration_value": {
"Size": 500
}
},
{
"name": "Small",
"configuration_value": {
"Size": 300
}
}
]
}
]
}
}
バリアントの割り当て
機能のバリアントを割り当てるプロセスは、機能の allocation プロパティによって決定されます。
"allocation": {
"default_when_enabled": "Small",
"default_when_disabled": "Small",
"user": [
{
"variant": "Big",
"users": [
"Marsha"
]
}
],
"group": [
{
"variant": "Big",
"groups": [
"Ring1"
]
}
],
"percentile": [
{
"variant": "Big",
"from": 0,
"to": 10
}
],
"seed": "13973240"
},
"variants": [
{
"name": "Big",
"configuration_value": "500px"
},
{
"name": "Small",
"configuration_value": "300px"
}
]
機能の allocation 設定には次のプロパティがあります。
| プロパティ | Description |
|---|---|
default_when_disabled |
機能が無効とされている間にバリアントが要求された場合に使用するバリアントを指定します。 |
default_when_enabled |
機能が有効であるとみなされ、他のバリアントがユーザーに割り当てられていない場合に、バリアントが要求されたときに使用するバリアントを指定します。 |
user |
バリアントと、そのバリアントを割り当てるユーザーの一覧を指定します。 |
group |
バリアントとグループの一覧を指定します。 バリアントは、ユーザーが少なくとも 1 つのグループに属している場合に割り当てられます。 |
percentile |
バリアントと、ユーザーの計算されたパーセンテージがそのバリアントに割り当てられるために適合しなければならないパーセンテージ範囲を指定します。 |
seed |
percentile に対して計算されたパーセンテージの基になる値。 同じ seed 値が使用されている場合、特定のユーザーに対して計算されたパーセンテージはすべての機能で同じになります。
seed が指定されていない場合は、機能名に基づく既定のシードが作成されます。 |
機能が有効になっていない場合は、機能マネージャーは default_when_disabled とマークされたバリアントを現在のユーザー (この場合は Small) に割り当てます。
この機能が有効になっている場合は、機能マネージャーは user、group、percentile の割り当てを順番に確認してバリアントを割り当てます。 この具体的な例では、評価されるユーザーが Marsha という名前で、Ring1 というグループに属する場合、またはユーザーが偶然 0 パーセンタイルと 10 パーセンタイルの間に存在する場合、指定されたバリアントがユーザーに割り当てられます。 この場合、割り当てられたユーザーはすべて Big バリアントを返します。 これらの割り当てがどれも一致しない場合、default_when_enabled のバリアントが割り当てられ、Small になります。
割り当てのロジックは Microsoft.Targeting 機能フィルターと似ていますが、パラメーターには、ターゲット設定に存在するが割り当てに存在しないものや、その逆があります。 対象設定と割り当ての結果は関係ありません。
バリアントを使用して有効な状態をオーバーライドする
機能フラグの有効な状態をオーバーライドするためにバリアントを使用できます。 オーバーライドにより、バリアントが機能フラグの評価を拡張する機会が付与されます。 バリアントのあるフラグに対して IsEnabledWithAppContext を呼び出すと、機能マネージャーは、現在のユーザーに割り当てられているバリアントが結果をオーバーライドするように構成されているかどうかを調べます。 オーバーライドは、省略可能なバリアント プロパティ status_override を使用して行われます。 既定では、このプロパティは None に設定されており、バリアントはフラグが有効か無効であるかに影響しないことを意味します。
status_override を Enabled に設定すると、バリアントが選択されたときにフラグをオーバーライドして有効にすることができます。
status_override を Disabled に設定すると逆の機能が提供され、バリアントが選択されたときにフラグが無効になります。
enabled 状態が false の機能はオーバーライドできません。
バイナリのバリアントで機能フラグを使用する場合、status_override プロパティが役立ちます。 これにより、アプリケーションで IsEnabledaWithAppContext のような API を使い続けながら、パーセンタイル割り当てやシードなどのバリアントに付属する新しい機能の恩恵を受けることができます。
{
"id": "MyVariantFeatureFlag",
"enabled": true,
"allocation": {
"percentile": [
{
"variant": "On",
"from": 10,
"to": 20
}
],
"default_when_enabled": "Off",
"seed": "Enhanced-Feature-Group"
},
"variants": [
{
"name": "On"
},
{
"name": "Off",
"status_override": "Disabled"
}
]
}
上記の例では、機能が常に有効になっています。 現在のユーザーが計算されたパーセンタイルの 10 から 20 の範囲にある場合、On バリアントが返されます。 それ以外の場合は、Off バリアントが返され、status_override は Disabled と等しいため、その機能は無効とみなされます。
次のステップ
アプリケーションで機能フラグを使用する方法については、次のクイックスタートに進んでください。
機能フィルターの使用方法について学ぶには、次のチュートリアルに進んでください。