TypeScript
機械学習
最近Misreading ChatのA/Bテストの回を聞いて興味を持ったので、このブログにA/Bテストを実装してみた。 Misreading Chatで取り上げられたGoogleの論文では、 大規模な組織でA/Bテストを実施する方法について論じていたが、個人のブログ程度であればシンプルな方法で十分だろう。
ということで、TypeScript + React + Google AnalyticsでA/Bテストを実施した。 実装の概要は以下の通り。
要素要素で上で紹介したGoogleの論文のアイディアを利用している。 実験オブジェクトはクッキーに基づいて一意に決まるので、ページを変える毎に設定が変わったりする心配は特に無い。 実装に難しいところはないし、結果の集計はGoogle Analytics上で適当なレポートを作成すればいいので管理も簡単。
実験オブジェクトの具体的な中身は以下のようなイメージ。 各種フラグを格納するオブジェクト。
interface Experiment {
useBigButton: boolean;
}
型定義など込みで70ページ程度。 環境変数からGoogle Analyticsのタグを取得しているが、自分で利用する際には適宜書き換えてほしい。
"use client";
import Cookies from "js-cookie";
import { useSyncExternalStore } from "react";
interface Experiment {
useBigButton: boolean;
}
const defaultExperiment: Experiment = {
useBigButton: false,
};
export function initGoogleAnalytics() {
const tag = process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID;
const script1 = document.createElement("script");
// biome-ignore lint: google analytics script
script1.src = "https://www.googletagmanager.com/gtag/js?id=" + tag;
script1.async = true;
document.body.appendChild(script1);
window.dataLayer = window.dataLayer || [];
function gtag() {
// @ts-ignore
dataLayer.push(arguments);
}
const id = getABTestID();
const experiments =
id !== undefined ? generateExperiment(id) : defaultExperiment;
// @ts-ignore
gtag("js", new Date());
// @ts-ignore
gtag("config", tag, {
"user_properties": experiments,
});
}
export function useExperiment(): Experiment {
const id = useSyncExternalStore(
() => () => {},
() => getABTestID(),
() => undefined
);
if (id === undefined) {
return defaultExperiment;
}
return generateExperiment(id);
}
function getABTestID(): number | undefined {
const key = "blog-ab-test-id";
const cookie = Cookies.get(key);
if (cookie !== undefined) {
return parseInt(cookie, 10);
}
const id = Math.floor(Math.random() * 1000) % 1000;
Cookies.set(key, id.toString(), { expires: 30 });
return id;
}
function generateExperiment(id: number): Experiment {
return {
useBigButton: id < 500,
};
}
今回はuseBigButtonという仮想のフラグを使っている。 IDが500以下かどうかでtrueかfalseが決まるので、後はuseExperimentで自由に表示を切り替えればいい。
今回初めてuseSyncExternalStoreを使ってみた。 クッキーから情報を取得する際にuseEffectなどで頑張ることもできるが、ちらつきなどを考えるとuseSyncExternalStoreを使ったほうが好ましい。 以下のリンクで詳しく紹介してくれている。
https://qiita.com/ssssota/items/51278dc5d51801dfb3fc
Google Analyticsが使いづらいという問題がある。 user_properties周りの記事があまり見つからないし、Real Time Viewの挙動がガバガバでuser_propertiesが見えないことがあった。 別にリアルタイムで見えなくても分析時はそこまで困らないが、動作確認中とかは困る。 カスタムディメンジョンを作って、実際に分析で使えるようになるまでもラグがあって体験が微妙に悪い。
あとは集計がやりにくいという問題もある。 基本的なメトリクスは取れるが検定などの数値的な処理をしたい場合には使いにくい。 使ったことが無いがLooker Studioなどと連携すれば、この問題は解決されるかもしれない。
TypeScriptで簡単なA/Bテストを実装した。 小規模のWebサイトであれば十分実用に耐えるものだと思う。
掲載しているコードに誤りがあったので修正