GitLab Feature Flags

# GitLab Feature Flags
[GitLab Feature Flags](https://docs.gitlab.com/ee/operations/feature_flags.html) はアプリケーションの機能の ON / OFF を GitLab からできる機能です。実装は [Unleash](https://docs.getunleash.io/) なので、そちらを知っている人はすぐにわかると思います。

次のスクリーンショットは Feature Flags の設定画面ですが、スイッチをオフにするとアプリケーションのその機能は無効に、オンにすると有効になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103982/51987178-b32a-9794-730c-4aa28c641d99.png)

スクリーンショットでは二つの機能が有効になっています。単純に有効無効を切り替えるだけでなく、以下のこともできます。

* `testing` 環境は有効、`prouction` では無効にして、テスターに新機能をテストしてもらう
* 特定のアカウントのみ機能を有効にして、`production` 環境で一般の利用者に影響がないようにテストをする

ほかにも複雑な設定ができるようですが、ここでは解説しません。[Feature flag strategies](https://docs.gitlab.com/ee/operations/feature_flags.html#feature-flag-strategies) を読んでください。


## 利用する
### GitLab に設定する
GitLab Project の `Deployments` -> `Feature Flags` をクリックして、`New flags` ボタンから作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103982/8436f599-7a1a-d66d-2f88-1d03dc596295.png)

ここで設定した名前で、アプリケーション側から有効か無効かを問い合わせます。ちなみに、大文字やほとんどの記号は使えません。

`Type` や `Environments` は無視して `Create feature flag` で保存します。


### アプリケーションに組み込む
最初に説明した通り、実装は [Unleash](https://docs.getunleash.io/) ですので、Unleash クライアントをアプリケーションに組み込みます。

ここでは .NET の例ですが、[Integrate feature flags with your application
](https://docs.gitlab.com/ee/operations/feature_flags.html#integrate-feature-flags-with-your-application) や [Unleash SDKs](https://docs.getunleash.io/sdks) を参考にしてください。

.NET コンソールプロジェクトを作成して、Unleash クライアントを組み込みます。

```
$ mkdir project1
$ cd project1
$ dotnet new console
$ dotnet add package unleash.client
```

GitLab Feature Flags のページの `Configure` ボタンをクリックして、`API URL` と `Instance ID` を入手します。

アプリケーションコードは以下の通りです。

```Program.cs
// Unleash client の初期化。
var settings = new UnleashSettings()
{
    AppName = "development",
    UnleashApi = new Uri("<API URL>"),
    InstanceTag = "<Instance ID>"
};
IUnleash unleash = new DefaultUnleash(settings);

// Unleash が GitLab にアクセスする時間を待機する。
Thread.Sleep(2000);

// Feature Flags が有効ならようこそを表示する。
if (unleash.IsEnabled("show_boot_message"))
{
    Console.WriteLine("ようこそ!");
}
```

これで、GitLab Feature Flags で `show_boot_message` の有効無効を切り替えるだけで、簡単に機能を切り替えることができます。


## 気になるところを調べた
### GitLab が落ちてたら?
GitLab が落ちてたらどうするの? って気になります。

~~落ちてたら `IsEnabled()` は `false` を返すだけです。例外でアプリケーションが落ちるなんてことはありません。~~

~~GitLab が落ちてたら正しい値が取得できなくなるのは困りものですが、Feature Flags はテストのために一時的に機能を有効化するのが目的なので、問題になることはなさそうです。気にしないことにしました。~~

~~ちなみに、`IsEnabled("show_boot_message", true)` ってすると GitLab につながらないときは `true` になります。~~

~~対象の機能のテストが終わって本番でも常に有効にするならば、その Feature Flag をデフォルト有効にするか、Feature Flag をアプリケーションから取り除いた方がよいでしょう。でないと、GitLab が落ちた時に、意図せず本番でその機能が無効化されます。~~

(追記) 詳細は最後の追記に書きましたが、GitLab が落ちていた場合は `false` を返すのではなく、キャッシュされている GitLab Feature Flags の値を使うが正解でした。ですので、GitLab が落ちたからといっても、今まで `true` だったのが `false` になることはありません。


### アプリケーションの起動中に Feature Flags を変更したら?
Feature Flags を変更した後にアプリケーションを再起動しなければいけないなら不便ですよね。

~~一度取得した Feature Flags はキャッシュされます。規定ではキャッシュの寿命は 30 秒です。~~つまり、GitLab Feature Flags で変更した後に少し待てばアプリケーション側に伝わることになります。

(追記) キャッシュの寿命が 30 秒なのではなく、30 秒ごとに GitLab にアクセスしてキャッシュにため込んでいます。「キャッシュの寿命が 30 秒」は正確ではないので訂正します。

そんなに頻繁に切り替えることではないので、これで問題なさそうです。

ちなみに、Unleash の設定でキャッシュの寿命は変更できます。


## 高度な機能
### testing は有効だけど、production は無効
GitLab には GitLab Environment という機能があって、デプロイしたアプリケーションの環境を `production` / `staging` / `testing` などのように区別することができます。

この GitLab Environment の環境ごとに有効無効を切り替えることができます。

`testing` で Feature Flags を有効にしテストして、問題がなければ `production` でも有効にするといったことができます。

アプリケーションの環境名は Unleash クライアントの初期化時に `AppName` で指定します。

> The name of the environment the application runs in (not the name of the application itself).

```Program.cs
// Unleash client の初期化。
var settings = new UnleashSettings()
{
    AppName = "testing", // ここ!
    UnleashApi = new Uri("<API URL>"),
    InstanceTag = "<Instance ID>"
};
IUnleash unleash = new DefaultUnleash(settings);
```

GitLab Feature Flags の設定は、対処の Feature Flags の `Environments` に追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103982/deb2e446-4848-9a77-50ee-51a5d7050921.png)

`Save changes` で保存すると、以後 `testing` 環境のみ機能が有効化されます。


### 特定の人のみ有効にする
新機能をいくらテスト環境でテストしたとはいえ、本番で本当にうまく動作するかは不安でしょう。

開発部のメンバーのアカウントのみ新機能を有効にして、みんなでしばらく使ってみて問題が出ないか様子見するという方法がよく使われています。

アプリケーションコード側では次のように、ユーザー ID を付けて `IsEnabled()` を呼び出します。

```c#
if (unleash.IsEnabled("show_boot_message", new UnleashContext
    {
        UserId = "manager@example.jp"
    }))
{
    // ...
}
```

GitLab Feature Flags の方では `Type` を `User IDs` に変更して、`User IDs` に対象となるユーザー ID をカンマ区切りで指定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103982/e6106617-2565-10f1-7569-7ae73ffc9b12.png)

`Save changes` で保存すると特定のユーザーのみ機能を有効にすることができます。

ユーザーの指定は単純に ID を並べるだけではなくて、あらかじめ設定しておいたユーザーリストを使う方法などいくつかあります。[GitLab Feature Flags の User List](https://docs.gitlab.com/ee/operations/feature_flags.html#user-list) のあたりを読んでください。


### 複数の Strategy
本番環境では `foo@example.jp` ユーザーのみ有効に、テスト環境ではユーザーによらず有効にするということはよく行われると思います。

GitLab Feature Flags は Strategy を複数設定することでこれを実現できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103982/74f5c3b9-3e45-e753-3af2-3d5d333eeeba.png)


### そのほか
[Percent Rollout](https://docs.gitlab.com/ee/operations/feature_flags.html#percent-rollout) とか [Percent of Users](https://docs.gitlab.com/ee/operations/feature_flags.html#percent-rollout) とかもあるようです。


## 終わりに
GitLab Feature Flags 便利そうですね!

あと、実装が Unleash なので、最悪 GitLab Feature Flags 機能が削除されても、Unleash サーバーを別途立てて、アプリケーションを少し修正するだけで済むのもいいですね。

今は、自作のできの悪い Feature Flags ライブラリ (コード書き換え必須・要再起動) を使っていますが、次から GitLab Feature FLags に置き換えようと思います。


## 追記 (2021/12/17)
Feature Flags を使う機会があり、このブログを見ながら構築したのですが、動かないのですよ! Unleash のソースコードを追っかけてみると... このブログに間違いが見つかりました。ごめんなさい。

自分は `IsEnabled()` を呼び出されたときに GitLab にアクセスすると思っていたのですが、違いました。実際は Unleash クラインとの起動と同時に、バックグラウンドで 30 秒ごとに GitLab にアクセスします。また、アクセスした結果はキャッシュしてました。

今回の例のようなコンソールアプリケーションで、Unleash クライアントを起動してすぐに使うと、まだ GitLab から Feature Flags の情報が取得できていないため、`false` が返ってきます。また、情報を取得する前にアプリケーションが終了するので、キャッシュも作られず、常に `false` になります。

Unleash クライアントは常にこのキャッシュを利用するので、GitLab にアクセスできない場合に `false` を返すとは限りません。最後に GitLab にアクセスしたときの Feature Flags の設定に従います。便利ですね!