GoとLaunchDarklyでフィーチャーフラグを実装する

## はじめに
業務でフィーチャーフラグを実装をする機会があり、技術調査の一環でフィチーチャーフラグのプラットフォームである[LaunchDarkly](https://launchdarkly.com/)を使って実装してみました。

この記事では、Go言語とLaunchDarklyを使った具体的なフィーチャーフラグの実装方法について解説します。フィーチャーフラグ自体の概要やメリットについては、多くの記事で詳しく説明されていますので、ここでは割愛します。

フィーチャーフラグについては、よくわからないという方は以下の記事がおすすめです。
[フィーチャーフラグ（Feature Flag）はなぜ必要なのか？](https://codezine.jp/article/detail/14114)
[Feature Flag](https://martinfowler.com/bliki/FeatureFlag.html)

## やること
この記事では以下の内容で進めます。
・LaunchDarklyでフィーチャーフラグを設定する
・設定したフィーチャーフラグをSDKを使って取得する

## 動作環境
Goバージョン：1.22.5

## 実装

### 1.LaunchDarklyでフラグを設定
LaunchDarklyでアカウントを作成後、`Create flag`よりフラグを追加します。

![create_flag.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365177/1d5178cb-5922-524e-1bd3-5f52e89bffc0.png)


### 2.フラグの取得

#### 2-1.SDKのインストール
`go get github.com/launchdarkly/go-server-sdk/v7`

#### 2-2.バックエンドでの実装
SDKを使ってLaunchDarklyに設定したフラグを取得します。
以下のコードを追加（LaunchDarklyから引用）。
`featureFlagKey`には、作成したfeature flagのキーを指定してください。

```main.go
package main

import (
	"fmt"
	"os"
	"time"

	"github.com/launchdarkly/go-sdk-common/v3/ldcontext"
	"github.com/launchdarkly/go-sdk-common/v3/ldvalue"
	ld "github.com/launchdarkly/go-server-sdk/v7"
)

  func showBanner() {
    fmt.Print("\n        ██       \n" +
      "          ██     \n" +
      "      ████████   \n" +
      "         ███████ \n" +
      "██ LAUNCHDARKLY █\n" +
      "         ███████ \n" +
      "      ████████   \n" +
      "          ██     \n" +
      "        ██       \n")
  }

  func showMessage(s string) { fmt.Printf("*** %s\n\n", s) }

  func main() {
    var sdkKey = os.Getenv("LAUNCHDARKLY_SDK_KEY")

    if sdkKey == "" {
      showMessage("LaunchDarkly SDK key is required: set the LAUNCHDARKLY_SDK_KEY environment variable and try again.")
      os.Exit(1)
    }

    ldClient, _ := ld.MakeClient(sdkKey, 5*time.Second)
    if ldClient.Initialized() {
      showMessage("SDK successfully initialized!")
    } else {
      showMessage("SDK failed to initialize")
      os.Exit(1)
    }

    // Set up the evaluation context. This context should appear on your LaunchDarkly contexts dashboard
    // soon after you run the demo.
    context := ldcontext.NewBuilder("example-user-key").
      Name("Sandy").
      Build()

    // Set featureFlagKey to the feature flag key you want to evaluate.
    var featureFlagKey = "sample-feature"

    if os.Getenv("LAUNCHDARKLY_FLAG_KEY") != "" {
      featureFlagKey = os.Getenv("LAUNCHDARKLY_FLAG_KEY")
    }

    flagValue, err := ldClient.BoolVariation(featureFlagKey, context, false)
    if err != nil {
      showMessage("error: " + err.Error())
    }

    showMessage(fmt.Sprintf("The '%s' feature flag evaluates to %t.", featureFlagKey, flagValue))

    if flagValue {
      showBanner()
    }

    if os.Getenv("CI") != "" {
      os.Exit(0)
    }

    updateCh := ldClient.GetFlagTracker().AddFlagValueChangeListener(featureFlagKey, context, ldvalue.Null())

    for event := range updateCh {
      showMessage(fmt.Sprintf("The '%s' feature flag evaluates to %t.", featureFlagKey, event.NewValue.BoolValue()))
      if event.NewValue.BoolValue() {
        showBanner()
      }
    }
  }

```

#### 2-3.SDK キーの取得
:::note info
LaunchDarklyから設定データを取得するには SDKのキーが必要になります。
:::

キーはLaunchDarklyの以下の画面からコピーします。

1.アカウント設定(歯車アイコン)をクリック
2.サイドバーのProjectsを選択
3.プロジェクト名を選択
4.環境ごとのキーが表示されるので使いたい環境のキーをコピー（以下の画像)

![SDKキー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365177/22b308bc-7d60-3715-1cf1-083871aad135.png)


先ほどコピーしたキーを`.env`に追加。

```.env
LAUNCHDARKLY_SDK_KEY=XXXXXXXX
```

環境変数の登録。
```terminal
$ source .env
$ env | grep SDK
LAUNCHDARKLY_SDK_KEY=XXXXXXXX
```

起動するとフラグを取得し、フラグの状態を表示がされます。

```terminal
$ go run main.go
[LaunchDarkly] 2024/08/02 00:28:25 INFO: Starting LaunchDarkly client 7.6.0
[LaunchDarkly] 2024/08/02 00:28:25 INFO: Starting LaunchDarkly streaming connection
[LaunchDarkly] 2024/08/02 00:28:25 INFO: Waiting up to 5000 milliseconds for LaunchDarkly client to start...
[LaunchDarkly] 2024/08/02 00:28:25 INFO: Connecting to LaunchDarkly stream
[LaunchDarkly] 2024/08/02 00:28:26 INFO: LaunchDarkly streaming is active
[LaunchDarkly] 2024/08/02 00:28:26 INFO: Initialized LaunchDarkly client
*** SDK successfully initialized!

*** The 'sample-feature' feature flag evaluates to true.


        ██
          ██
      ████████
         ███████
██ LAUNCHDARKLY █
         ███████
      ████████
          ██
        ██
```

LaunchDarklyのフラグ管理画面で取得するフラグをOFFで保存します。そうすると、取得した設定データもOFFになることがわかります。

```terminal
*** The 'sample-feature' feature flag evaluates to false.
```

指定したフラグがない場合はエラーが返されます。
`*** error: unknown feature key: sample. Verify that this feature key exists. Returning default value`

## コンテキストについて
コンテキストを使用すると、ユーザー、組織、デバイスなどに関連する属性を基に、フィーチャーフラグのターゲットルールを柔軟に作成できます。これにより、特定のユーザーやグループに対して、異なるフィーチャーフラグの設定を適用することが可能です。

以下のコード例では、「Sandy」という名前のユーザーコンテキストを作成し、そのユーザーに基づいたフィーチャーフラグの評価を行えるようにしています。

```main.go
context := ldcontext.NewBuilder("example-user-key").
      Name("Sandy").
      Build()
```

このコードでは、example-user-key というキーを持つコンテキストが作成され、Name("Sandy") により、名前が "Sandy" であるユーザーとして識別されます。このコンテキストを使うことで、LaunchDarklyは "Sandy" に対して適切なフラグのバリエーションを提供します。

作成されたコンテキストはLaunchDarklyの以下の画面から確認できます。
![Contexts.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365177/ca611bdd-c0ab-eab1-ca9c-9f4b5b20dfca.png)

### フラグに個別のコンテキストを割り当てる
フラグページにある「Add rule」から「Target individuals」を選択。
コンテキストの対象として先ほど作成した「Sandy」を選択することができるようになっています。

![target rule.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365177/5f2a8d20-6af0-e3d1-ed08-8657f98f1961.png)


少数のコンテキストなどを個別に対象とするときに利用できそうです。

### その他
[クイックスタート](https://app.launchdarkly.com/projects/default/get-started?env=test&env=test&selected-env=test)で始めると様々な言語のSDKを使って手軽に設定データの取得、動作確認をすることができます。

![SDK.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365177/e92be843-7802-7eef-07d2-9a5b64423915.png)

さらに、LaunchDarklyでは監視やデータ分析などのサービスとも連携できる点も魅力的です。
https://launchdarkly.com/integrations/

LaunchDarkly以外にもフィーチャーフラグを管理できるサービスは沢山あるようです。
以下の記事でサービスごとの比較がされておりわかりやすかったです。
[世の中のフィーチャーフラグのベンダー比較 （2024年前半）](https://zenn.dev/gunta/articles/79f77bdc285874#%E4%B8%96%E3%81%AE%E4%B8%AD%E3%81%AE%E3%83%95%E3%82%A3%E3%83%BC%E3%83%81%E3%83%A3%E3%83%BC%E3%83%95%E3%83%A9%E3%82%B0%E3%81%AE%E3%83%99%E3%83%B3%E3%83%80%E3%83%BC%E6%AF%94%E8%BC%83-%EF%BC%882024%E5%B9%B4%E5%89%8D%E5%8D%8A%EF%BC%89)

## まとめ
今回はGoのSDKが提供されていてすぐに利用できる点とドキュメントが豊富で初めて利用する場合でもとっつきやすいなどの理由からLaunchDarklyを選択しました。
LaunchDarklyを触ってみたところ、UIも直感的で使いやすく設計されており、非エンジニアの方でも直感的に操作できると感じました。
また、ドキュメントも豊富で、SDKの使い方などについても非常にわかりやすくまとめられていました。

:::note warn
LaunchDarklyの試用期間はサインアップから2週間になっています。トライアル期間が終わるとフラグの作成や編集などはできなくなるようでした。
継続して使用する場合は、プランの変更が必要ですので、試用期間中にその機能やサービスが自分のニーズに合っているかをしっかりと確認することをお勧めします。
:::

### 参考
https://pkg.go.dev/github.com/launchdarkly/go-server-sdk/v6
https://docs.launchdarkly.com/home