Amazon CloudWatch EvidentlyでA/Bテストすればデザインで困らないんじゃね？

# 概要
こんにちは。 KDDIアジャイル開発センターの小板橋です。 この記事は、[KDDI Engineer&Designer Advent Calendar 2021](https://qiita.com/advent-calendar/2021/engineer-designer)の12日目の記事です。

先日(2021/11/30 時点)、re:Invent2021にて[Amazon CloudWatch Evidently](https://aws.amazon.com/jp/about-aws/whats-new/2021/11/amazon-cloudwatch-evidently-feature-experimentation-safer-launches/)という機能が追加されました。
今回は、このAmazon CloudWatch Evidentlyについて、まとめこれにより何ができるようなるのかという点について書いていこうと思います。

# Amazon CloudWatch Evidentlyとは？

* Amazon CloudWatch Evidentlyとは、アプリケーション開発者が既存で動いているアプリに対し新機能を安全に検証するのに役立つサービスとなっています。
* もう少し詳しく説明すると、アプリケーション開発者は本番リリース前に、A/Bテストでページロード時間の平均値やCVR(コンバージョンレート)をテストできるようになる機能となっています。

## シチュエーションとして
* 例えば、既に動いているアプリケーションがあったとします。
* そこに新規機能をつけたいという要望があり、実装してみました。
* ただ、変更に間違いがある可能性(意図しない問題を含んでしまう可能性)はあると思います。
* なので本番リリース前に、検証として変更点をfeatureという形で管理しておき、その変更点に応じてページロード時間の平均値やCVR(コンバージョンレート)を取り、A/Bテストを行います。

* コンバージョンレートというのは、例えば、ある画面に新機能としてボタンを追加したとします。
* その機能追加を行うことでユーザはボタンを付けたら押すのかどうかを測定した値というのが、CVR(コンバージョンレート)です。

### Amazon CloudWatch Evidentlyは2つの機能から構成
Amazon CloudWatch Evidentlyは、下記の2つの機能を含んでいます。

* フィーチャーフラグ（Feature Flag）
* A/Bテスト

#### フィーチャーフラグ（Feature Flag）

* フィーチャーフラグ（Feature Flag）は、コードをデプロイせずに機能を有効または無効にすることで、システムの振る舞いを変更できる開発手法です。
* なので、変更点を登録し、booleanのflagの値に応じて、機能を有効にしたり無効にしたりできます。
* またこのサービスは、新機能を利用するユーザーの割合を決める事ができます。
* なので、一部の顧客に新しい機能を段階的に導入できます。(50%ずつトラフィックを制御するような事ができる。)
* 立ち上げ時には、ページロード時間の平均値などを監視します。問題なさそうであれば、トラフィックを増やして新しい機能を追加のユーザーに公開することができます。
* 何か問題が発生した場合は、ワンクリックでサーバー側のルーティングを変更して、古い機能のみをユーザに使っていただく事ができます。

#### A/Bテスト

* A/Bテストは、通常パターンAに対するCVR（コンバージョン率）とCTR（クリック率)をパターンBに対してテストし、2つのうちどちらがより効果的であるかを判断することにより、単一の機能の複数のバージョンを比較できます。

* A/Bテストの実施に必要な設定は、機能フラグの設定と同様です。アプリに複数のシナリオをデプロイし、トラフィックの一部を1つのパターンまたは別のパターンにルーティングする方法で制御します。
* 次に詳細な傾向、CVR（コンバージョン率）などを比較します。
* これにより、どちらのパターンの方が、ユーザにとってより利便性があるのか、効果的なのかを測定し、そのパターンを採用します。

### 使用できるreagion
現在、下記のリージョンで利用が可能です。

* US East (N. Virginia)
* US East (Ohio)
* US West (Oregon)
* Europe (Ireland)
* Europe (Frankfurt)
* Europe (Stockholm)
* Asia Pacific (Sydney)
* **Asia Pacific (Tokyo)**
* Asia Pacific (Singapore)

# やってみた

## CloudWatch Evidentlyのプロジェクトを作成します

* 左のサイドメニューより、「Evidently」を選択すると下記のような画面が表示されます。
* 以下の画面から「プロジェクトを作成」ボタンを押してプロジェクトを作成します。

![スクリーンショット 2021-12-12 0.42.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/221b9262-77f1-e733-9756-aa2cf566f754.png)

* 作成画面では、まずプロジェクト名を入力していきます。
![スクリーンショット 2021-12-12 0.52.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/36155493-96fa-5671-dbe1-a18b5ae2c33b.png)

* 次に評価結果を保存する設定をすることができます。

重要なのはここで保存する**Evidentlyイベントには2つのタイプ**があることです。

① **評価イベント** : ユーザーに表示される機能のバリエーションに関連しています。
Evidentlyが収集する評価イベントを**S3**もしくは**CloudWatch Logs**に保存するかどうかを選択します。
このオプションを選択すると、サービスの利用料金が発生します。

ただ、評価イベントを保存していない場合でも、Evidentlyはメトリクスなどは生成します。
これらのデータを確認したい場合は、「Evidently」で表示できます。

② **カスタムイベント** : クリックなどのユーザーアクションからメトリクスを生成するために使用されます。

* Evidentlyは、**カスタムイベントではなく評価イベントを保存するオプションのみを提供します**。
* カスタムイベントを保存する場合は、Evidentlyの外部で選択したストレージオプションを使用して保存する必要があります。

今回は、評価イベントのストレージを選択しないで進めていきます。

![スクリーンショット 2021-12-12 1.08.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/97447fd4-fe94-c96f-8813-17364b242e08.png)

プロジェクトの作成が成功しました。
![スクリーンショット 2021-12-12 1.09.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/fd7db0ee-7a3b-3518-e9d1-ad8525c9d9c7.png)


## 作成したプロジェクトに機能を追加していきます。

* 右上にある「機能の追加」を選択します。
![スクリーンショット 2021-12-12 1.10.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/a13eb9ba-d95c-032f-4749-53c45a58c894.png)

* まずは。機能名を入力します。
![スクリーンショット 2021-12-12 10.12.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/5c02b588-3e24-eb70-89ee-8c0d6dd8d624.png)

* 次に機能のバリエーションを定義します。今回は、2つのバリエーションがあり**boole型**を使用しています。

ちなみに、この機能のバリエーションの型で設定できるものは以下のものです。
これにより、アプリケーションで変数をハードコーディングしないようにできます。

* boole値
* 数値 (整数または小数)
* 文字列
* JSON

また、このバリエーションが先に説明した機能フラグを指しておりまして、A/BテストをEvidentlyでコントロールしたい値を設定することになります。

![スクリーンショット 2021-12-12 11.27.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/dcd521e8-a821-eb90-f7d9-834c002204d6.png)

* ここまで設定をすると、Evidentlyのコンソールで下記のように**JavaScriptとJavaのコードスニペット**を吐き出してくれます
* なので、これからはアプリケーションに吐き出されたコードスニペットを追加します。

![スクリーンショット 2021-12-12 10.19.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/b4e68922-1795-e076-f0ee-943f7408227d.png)

## 吐き出されたコードスニペットの解析とアプリ側で設定が必要なもの

* まず初めに吐き出されたコードスニペットの解析をしていきましょう

evidently.evaluateFeatureとあるので、evidentlyを呼び出すためのクライアント設定をアプリ側に書かなければいけません。

```evidently-client.js
// Initialize the Amazon CloudWatch Evidently client
const evidently = new AWS.Evidently({
    endpoint: EVIDENTLY_ENDPOINT,
    region: 'us-east-1',
    credentials: fromCognitoIdentityPool({
        client: new CognitoIdentityClient({ region: 'us-west-2' }),
        identityPoolId: IDENTITY_POOL_ID
    }),
});
```

* 次にEvaluateFeatureAPIを呼び出して、ユーザに表示するバリエーションについての情報を取得しているように見えます、
* この時Requestする際のentityIdは、ユーザをセグメント化するための文字列ベースの属性です。
* これは、セッションID、ユーザID、これらのハッシュを文字列でぶっ込むことができます。
* featureパラメータは、評価する機能の名前を入れます。

```sample.js
// Set up credentials here
  
// API request structure
const evaluateFeatureRequest = {
  // entityId is a JSON key to store the entity ID in custom event.
  // The entity ID used in evaluateFeature API needs to be the same as the one used in putProjectEvents API with custom event type.
  entityId: 'myId',
  // Name of your feature
  feature: "test-feature",
  // Name of your project
  project: "test-evidently",
};

// Evaluate feature
evidently.evaluateFeature(evaluateFeatureRequest)
  .promise()
  .then(response => {
  // Instrument your code based on evaluate feature API response

});

```

この時の、EvaluateFeatureAPIからのresponseとしては、下記のようなものが返却されます。

```
// Example of API response when no rules are being evaluated on the feature 
    // (no experiment, no launch and no overrides)
    {
    "details": {},
    "reason": "DEFAULT",
    "value": "{ 
        "boolValue": "true" 
      }",
    "valueType": "BOOLEAN",
    "variation": "バリエーション 1",
    }
```

## アプリ側の準備
ここからは、実際にアプリを作成し、Evidentlyより吐き出されたコードスニペットを埋め込みたいと思います。
また、今回はamplify authを使ったCognitoでのユーザ認証も可能にします。
なぜ、Cognitoを使用するかというと上記で説明した、entityIdにユーザ情報を格納したかったからです。
なので、今回はCognitoのユーザ名にしました。

そしたら、サクッとnpx create-react-app xxxしちゃいましょう。

* amplify authの設定は、[=>こちら](https://docs.amplify.aws/start/q/integration/react/)を参照してください。

下記に作成したrecatアプリとEvidentlyから吐き出されたコードスニペットを埋め込みます。

* 今回は、機能フラグによってDivのクラスを表示切り返します。「表示の切り替えを行います」の部分ですね。

```App.js
import './App.css';
import Amplify from "aws-amplify";
import awsExports from "./aws-exports";
import {withAuthenticator} from '@aws-amplify/ui-react'
import Evidently from "aws-sdk/clients/evidently";
import {Auth} from "@aws-amplify/auth";
import {useEffect, useState} from "react";

Amplify.configure(awsExports);

async function getEvaluateFeature(){

    const credentials  = await Auth.currentCredentials();
    const userinfo  = await Auth.currentUserInfo();
    console.log(userinfo)

    // Initialize the Amazon CloudWatch Evidently client
    const evidently = new Evidently({
        endpoint: 'https://evidently.us-east-1.amazonaws.com',
        credentials: credentials,
        region: 'us-east-1'
    });

    // API request structure
    const evaluateFeatureRequest = {
        // entityId for calling evaluate feature API
        entityId: userinfo.username,
        // Name of your feature
        feature: 'test-feature',
        // Name of your project
        project: "test-evidently",
    };

    return evidently.evaluateFeature(evaluateFeatureRequest).promise();
}

function App() {

    const [variation, setVariation] = useState();

    useEffect(() => {
        (async() =>{
         const res = await getEvaluateFeature();
         console.log(res)
         console.log(res.value)
         setVariation(res.value.boolValue)
        })()
    },[])


  return (
    <div className="App">
      <header className="App-header">
          { variation &&
          <div className="App-link">
              表示の切り替えを行います
          </div>
          }
      </header>
    </div>
  );
}

export default withAuthenticator(App)
```

* npm startで立ち上げると・・

![スクリーンショット 2021-12-12 11.44.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/110ae038-8047-1330-9641-0448e9d11389.png)

上記の**機能フラグの設定をしただけ**ですと、「表示の切り替えを行います」が表示されないので、Evidently側の設定を変えていきましょう。


次は、機能の起動を行っていきます。
起動に移り「起動の作成」を選択します。

![スクリーンショット 2021-12-12 13.06.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/2588aff4-97e9-c477-cdcd-95ef220ce445.png)


* まずは、「起動名」を入力していきます。
![スクリーンショット 2021-12-12 11.49.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/3dad0a58-e515-8498-4758-3d9e16fc92de.png)


* 次に起動する機能の選択をしていきます。
* ここでは、先程作成した機能を選択していきます。
![スクリーンショット 2021-12-12 11.51.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/b157330a-e4b9-d622-8c98-5a08ab5dc289.png)

**次が重要！！！！、そしてすげえんです**
起動設定にて、「今すぐ起動を開始」で直ちに設定が反映できます。
「起動をスケジュール」を選択すると、日時指定での設定も可能です。

**そして、次なのですが、なんと起動フラグごとにトラフィックの割合を設定できるのです。**
これで、AのversionとBのversionそれぞれをユーザに試してもらえるので、その時の結果を参考にどちらのversionにするかの指標を取ることができます。

まずはversion1にトラフィック割合を100%で全振りします。

![スクリーンショット 2021-12-12 11.56.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/14c71d56-c3a5-d833-66c2-5f2f06bb9ace.png)

最後に、メトリクスの設定もしておきましょう。
versionのパフォーマンスを測定するために、最大3つのメトリックを定義できます。

![スクリーンショット 2021-12-12 12.08.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/f8a9ee0c-4b70-005b-94fc-a28ed700ed45.png)

設定ができたので、トラフィックの分散がversion 1に100%いってますね。
![スクリーンショット 2021-12-12 11.57.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/54137611-58cc-e55f-26ff-9cd160d56394.png)

Reactのアプリを確認してみましょう。
* フラグが有効になったので、「表示の切り替えを行います」が表示されましたね！
![スクリーンショット 2021-12-12 12.20.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/0e344022-5ca0-8ac0-b0b6-d083334c12de.png)


次に、version2に対してトラフィックを100%全振りしてみましょう！
![スクリーンショット 2021-12-12 12.22.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/6c8bad74-543e-90cf-cfa7-10ed6720c1c1.png)

Reactのアプリを確認してみます。
フラグがflaseになったので、「表示の切り替えを行います」が表示されなくなりました。
![スクリーンショット 2021-12-12 12.23.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/1b55c17e-2236-eb4a-0e88-4c172d635ad5.png)

A/Bテスト支援機能もあります！
* A/Bテストの実行も先程の設定と同じです。テストする機能を作成し、実験を作成します。
* トラフィックの一部をvesrion 1にルーティングし、次に他のトラフィックをvesrion 2にルーティングするように実験を構成することもできます。

![スクリーンショット 2021-12-12 13.00.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563635/2838d593-2b85-508f-5f75-79d8680f00b0.png)


# まとめ
* CloudWatch Evidentlyを使用することで、本番リリース前の検証で機能フラグの制御を利用しコードをデプロイせずに機能を有効または無効にすることでのA/Bテストやversionごとの良し悪しを判断できそうですね。！
* タイトル回収できたか不安ですが、これで締めたいと思います！


# 参考文献
https://aws.amazon.com/jp/about-aws/whats-new/2021/11/amazon-cloudwatch-evidently-feature-experimentation-safer-launches/

https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/CloudWatch-Evidently.html

https://aws.amazon.com/jp/blogs/aws/cloudwatch-evidently/

https://dev.classmethod.jp/articles/amazon-cloudwatch-evidently-ga/