Skip to content

Add: オンボーディング画面のABテスト機構とBバリアント - #456

Open
ujiro99 wants to merge 3 commits into
mainfrom
feat/onboarding-ab-test
Open

Add: オンボーディング画面のABテスト機構とBバリアント#456
ujiro99 wants to merge 3 commits into
mainfrom
feat/onboarding-ab-test

Conversation

@ujiro99

@ujiro99 ujiro99 commented Sep 13, 2026

Copy link
Copy Markdown
Owner

概要

オンボーディングのIntro画面での離脱率(約60%)改善のため、ABテスト機構と Bバリアント画面を実装します。

Closes #455

1. ABテスト機構

配分比率のリモート設定(packages/hub/public/data/experiments.json

{ "onboarding_v2": { "enabled": true, "allocation": 0.5 } }

hub のデプロイのみで配分変更・実験停止(enabled: false)が可能で、Chrome Web Store への再申請は不要です。

variant の割当(src/services/experiments/

  • Math.random() < allocation ? "B" : "A" で割当し、chrome.storage.localexperiments キーへ不揮発保存
  • 既存の割当があれば再抽選せず、同一ユーザーの全イベントが同じ variant になります
  • 取得タイミングは background script の onInstalled(INSTALL時)→ chrome.tabs.create の直前。失敗してもオンボーディングは必ず開きます(その場合はページ側が自前で割当)
  • 設定の取得は都度実行(キャッシュなし・3秒タイムアウト)。失敗時はビルド時の既定値(allocation: 0.5)で割当自体は継続し、config_source: "fallback" を記録します。hub 障害時に片方のアームだけがネットワーク不良ユーザーに偏るのを避けるためです

GAイベント

  • onboarding_* イベントに variant パラメータを付与(sendOnboardingEvent() に集約)
  • onboarding_startexperiment_id: "onboarding_v2"config_source を追加

2. Bバリアント画面

  • Intro ステップを廃止し、検索コマンドのステップから開始
  • 冒頭にロゴ + 「ようこそ!」のオーバーレイを表示。2秒で自動遷移、クリックで即時遷移(読む内容が無いため待たせない)
  • 入場・退場は Linear 風のブラー+フェード。OnboardingFadeIneffect オプションとして追加したので、Aバリアントの既存アニメーションは変更ありません
  • 文言は全14ロケールへ追加

3. 開発・レビュー用

  • dev/e2e ビルド限定で ?variant=A|B により任意のアームを表示可能
  • e2e/onboarding-shots.spec.ts に Bバリアントのウェルカム画面のショットを追加

動作確認

  • yarn test … 70ファイル / 1082テスト パス(新規テスト19件: 設定フェッチ・割当ロジック・ウェルカム画面・初期ステップ)
  • yarn lint … エラー0
  • tsc -b / yarn build:extension … 成功

マージ後に必要な作業

  • GA4管理画面でカスタムディメンション variant / experiment_id / config_source の登録(未登録だとイベントは送信されてもレポートに出ません)
  • experiments.jsonallocation: 0.5 で入れてあります。拡張のリリースまで配信を止めておきたい場合は enabled: false または allocation: 0 に変更してください

🤖 Generated with Claude Code

https://claude.ai/code/session_01VeTTSrCxM9ifQatx6XjCxo

ujiro99 and others added 2 commits September 13, 2026 14:31
Adds the experiment plumbing for the onboarding A/B test (#455):

- packages/hub/public/data/experiments.json serves the allocation ratio,
  so the split can be changed - or the test stopped - by deploying the
  hub, with no Chrome Web Store release.
- services/experiments assigns a variant once per install and persists it
  in chrome.storage.local. On a fetch failure it still assigns, using the
  build-time allocation, and records config_source so a hub outage can be
  separated out during analysis.
- The assignment runs in onInstalled right before the onboarding tab is
  created, so the page renders its first frame from storage.
- Every onboarding_* event now carries a `variant` param.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VeTTSrCxM9ifQatx6XjCxo
The B arm of the onboarding A/B test (#455), aimed at the 60% drop-off on
the intro screen:

- Drops the INTRO step and opens directly on the search command step.
- Shows the logo and a welcome message first, in a new WELCOME phase.
  It advances after 2s, or immediately on click - there is nothing to
  read there, so the timer should not be a second thing to sit through.
- Enter and exit use a blur + fade transition (Linear-style), added as
  an `effect` option on OnboardingFadeIn so variant A keeps its current
  rise animation untouched.
- `?variant=A|B` lands on either arm in dev/e2e builds, and the
  screenshot spec captures the welcome overlay.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VeTTSrCxM9ifQatx6XjCxo
@codecov

codecov Bot commented Sep 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 71.00977% with 89 lines in your changes missing coverage. Please review.
✅ Project coverage is 45.00%. Comparing base (7e352f7) to head (d7b20eb).

Files with missing lines Patch % Lines
...nsion/src/components/onboarding/OnboardingPage.tsx 0.00% 35 Missing ⚠️
.../src/components/onboarding/useOnboardingVariant.ts 0.00% 29 Missing ⚠️
packages/extension/tailwind.config.js 0.00% 18 Missing ⚠️
...ion/src/components/onboarding/OnboardingLayout.tsx 0.00% 3 Missing ⚠️
...n/src/services/experiments/onboardingExperiment.ts 94.82% 3 Missing ⚠️
...nsion/src/services/experiments/experimentConfig.ts 98.11% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #456      +/-   ##
==========================================
+ Coverage   44.38%   45.00%   +0.62%     
==========================================
  Files         267      272       +5     
  Lines       27488    27763     +275     
  Branches     2145     2211      +66     
==========================================
+ Hits        12200    12495     +295     
+ Misses      15288    15268      -20     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@claude

claude Bot commented Sep 13, 2026

Copy link
Copy Markdown

レビュー結果

オンボーディングABテスト機構とBバリアント実装、全体的に設計意図(フォールバック戦略・variant一貫性の担保・GA4連携)が明確でテストも手厚く書かれています。以下、気になった点をいくつか挙げます。

1. (中〜高)Welcome画面の自動遷移タイマーが親の再レンダリングでリセットされる懸念

  • packages/extension/src/components/onboarding/OnboardingPage.tsx:72
  • packages/extension/src/components/onboarding/OnboardingWelcome.tsx:30-43

OnboardingFlow から OnboardingWelcome に渡している onDone は、

<OnboardingWelcome onDone={() => onboarding.setPhase(StepPhase.EXPLAIN)} />

という毎レンダリングで新規生成されるインライン関数です。OnboardingWelcome 側では finishuseCallback(..., [onDone])、自動遷移用の useEffect[finish] に依存しているため、OnboardingFlow が再レンダリングされるたびに保留中の2秒タイマーがクリアされ、新しいタイマーが再設定されます。

現状のコードでは OnboardingFlow 内の positionElmSelectAnchor の ref コールバック、OnboardingPage.tsx:36,76)がマウント直後に一度だけ更新されるため実害はごく軽微ですが、将来的に OnboardingFlow 配下で他の state 更新(useOnboardingState への機能追加や SelectContextProvider 経由の再レンダリング波及など)が入ると、「2秒で自動遷移」がいつまでも発火しなくなる可能性があります(クリックによる遷移は finish が最新のクロージャを使うため引き続き機能しますが、PRの仕様である自動遷移は壊れます)。

onboarding.setPhase 自体は useCallback で安定しているので、OnboardingPage.tsx 側で onDoneuseCallback 化する、もしくは OnboardingWelcome 内で最新の onDone を ref に保持してエフェクトの依存配列から外す、といった修正を推奨します。

なお OnboardingWelcome.test.tsxonDone に固定の vi.fn() を渡しているため、この種の不具合は現状のテストでは検出できません。

2. (中)Welcome表示中、背後のSkipボタンがキーボードから到達可能

  • packages/extension/src/components/onboarding/OnboardingPage.tsx:45-68OnboardingLayoutisWelcome に関わらず常にレンダリングされる)
  • packages/extension/src/components/onboarding/OnboardingLayout.tsx:63-70showsSkip(step)step === SEARCH でも true を返す)
  • packages/extension/src/components/onboarding/OnboardingWelcome.tsx

OnboardingWelcomefixed inset-0 z-30 bg-white の不透明オーバーレイとして視覚的には背後のヘッダー/Skipボタン(z-10/z-20)を覆っていますが、DOM上は OnboardingLayout の中身(Skipボタンなど)がそのまま存在し、aria-hidden/inert 等での無効化がされていません。そのため、キーボード操作(Tab / Shift+Tab)でオーバーレイの外にフォーカスを移動すると、視覚的に隠れているSkipボタンにフォーカスが移り操作できてしまいます(マウス操作ではオーバーレイがクリックを受けるため問題になりません)。モーダル的に振る舞わせるなら、Welcome表示中は背後のコンテンツに inert を付与する、もしくは OnboardingLayout 側で isWelcome 時はSkipボタン自体を描画しない、といった対応が必要です。

3. (軽微)Storage.update は get→set の非アトミック実装で、コメントの想定ほど競合を防げない

  • packages/extension/src/services/experiments/onboardingExperiment.ts:82-96
  • packages/extension/src/services/storage/index.ts:177-181

ensureOnboardingAssignment() のコメントには「Re-read inside the update so a concurrent assignment ... wins consistently」とありますが、BaseStorage.update は単純に getupdaterset を行うだけで、呼び出し間にロックはありません。そのため、同一ユーザーが2つのオンボーディングタブを本当に同時に開いたような稀なケースでは、両方の get が空の状態を読んでしまい、後勝ちの set が先勝ちの結果を上書きしてしまう可能性があります(その場合、上書きされた側の cachedAssignment=GA4に送るvariantと、実際にstorageへ永続化された値がその実行コンテキスト内で食い違います)。

background_script.tsawait ensureOnboardingAssignment() の完了を待ってから chrome.tabs.create するため、通常のインストールフローではこの競合は発生しません。実害は小さいですが、コメントの説明が実装の保証内容より強めなので、コメントを実態に合わせて修正するか、本当に排他制御が必要ならミューテックス的な仕組みを検討してください(既存の pageAction 系コードでも同じ Storage.update パターンが使われているため、今回のPR固有の問題ではありません)。

4. (軽微)フォールバック用デフォルト設定とhub側JSONの二重管理

  • packages/extension/src/services/experiments/experimentConfig.ts:24-27DEFAULT_CONFIGS.onboarding_v2 = { enabled: true, allocation: 0.5 }
  • packages/hub/public/data/experiments.json

hub障害時に使われるビルド時デフォルト値が、hub側の experiments.json の値と別々に管理されています。将来hub側で配分比率を変更した際にビルド時デフォルトを更新し忘れると、hub障害時のフォールバック挙動だけが古い比率のままになります。意図的な設計(ビルド時に固定したいから)であれば問題ありませんが、運用上の注意点としてコメントかAGENTS.mdに残しておくと親切です。

その他確認した点(問題なし)

  • 全14ロケールへの onboarding_welcomeMessage / onboarding_welcomeContinue 追加は網羅されており、既存のメッセージファイルの慣習(英語のみ description 付与)にも沿っています。
  • experimentConfig.tsnormalizeConfig / fetchExperimentConfig はタイムアウト・HTTPエラー・不正payloadへのフォールバックが単体テストでしっかりカバーされています。
  • OnboardingFadeIneffect 拡張はAバリアントの既存アニメーションに影響しない後方互換な実装になっています。
  • z-index(z-30 のオーバーレイ vs z-10/z-20 のレイアウト要素)は視覚的な重なり順として正しく設定されています。

全体として設計・テストの質は高く、上記1・2は本番影響がありうるため対応を推奨、3・4は軽微な指摘です。

- Hold onDone in a ref so the 2s auto-advance timer survives parent
  re-renders. Callers pass an inline callback, so depending on it
  directly restarted the pending timer on every render of the flow.
- Withhold the progress indicator and the Skip button during the
  WELCOME phase: the overlay only hid the button visually, leaving it
  reachable by keyboard.
- Correct the concurrency comment in ensureOnboardingAssignment -
  Storage.update is a plain get/set, so the re-read is best-effort, not
  atomic.
- Note that the build-time fallback allocation deliberately duplicates
  the hub's experiments.json, and has to be updated alongside it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VeTTSrCxM9ifQatx6XjCxo
@claude

claude Bot commented Sep 13, 2026

Copy link
Copy Markdown

レビュー結果

ABテスト機構とBバリアントのウェルカム画面、実装・テストともに丁寧に作り込まれています(ensureOnboardingAssignment の再読込によるレース対策、onDoneRef によるタイマー安定化など、review findings コミットでの追い込みも的確でした)。以下、気づいた点です。

1. E2Eスクリーンショットのタイミングが新規カット(variant B ウェルカム)に対して短すぎる可能性
packages/extension/e2e/onboarding-shots.spec.ts:108(00b-welcome-variant-b カット、35行目)では、他のショットと同じ page.waitForTimeout(700) でキャプチャしています。しかし OnboardingWelcome.tsx は OnboardingFadeIn effect="blur" を使っており、tailwind.config.js の onboarding-blur-in は duration 0.8s に加えロゴが delay 100ms、メッセージが delay 300ms(OnboardingWelcome.tsx:69,77)です。つまりロゴのフェードインは900ms、メッセージは1100msで完了するため、700ms時点のスクリーンショットはどちらもアニメーション途中(ぼかしが残った状態)で撮影されてしまいます。entrance animations を settle into a representative frame させてから撮る、というコメントの意図に反するため、このカットだけ待機時間を伸ばす(例: 1200ms以上)か、variantショット専用の待機時間を設けるとよさそうです。

2. OnboardingWelcome のアクセシブルネームに挨拶文が含まれない
OnboardingWelcome.tsx:58-68 で全画面を button aria-label=onboarding_welcomeContinue として実装しており、中に「ようこそ!」(onboarding_welcomeMessage) を子要素として描画しています。aria-label はアクセシブルネームの計算で子要素のテキストを完全に上書きするため、スクリーンリーダーのユーザーには「クリックして続ける」しか読み上げられず、ウェルカムメッセージ自体は伝わりません(意図的な割り切りかもしれませんが、Aバリアントの onboarding_step0Title/onboarding_step0Body が両方読み上げられるのと非対称です)。挨拶文もアクセシブルネームに含める(例: aria-label をメッセージ+続行ラベルの組み合わせにする、または aria-describedby でメッセージ要素を紐付ける)ことを検討してもよさそうです。

3. ビルド時フォールバック設定とhub側設定の重複管理
packages/extension/src/services/experiments/experimentConfig.ts:20-24 のコメントで明示されている通り、DEFAULT_CONFIGS(ビルド時の既定値)は packages/hub/public/data/experiments.json と意図的に重複した値を持ちます。両者の同期漏れを検出する自動テストは見当たりませんでした。将来的に hub 側の配分比率だけを変更し、拡張側の DEFAULT_CONFIGS を更新し忘れるリスクがあるため、hub の JSON を読み込んで DEFAULT_CONFIGS と突き合わせる簡単な整合性テスト(あるいはビルドスクリプトでの警告)があると安全です。現状はコメントでの注意喚起のみで運用に依存しています。

4. インストール時のフェッチタイムアウトがオンボーディング表示を最大3秒遅らせる
background_script.ts:479-484 は chrome.tabs.create の前に ensureOnboardingAssignment() を await しており、内部の fetchExperimentConfig(experimentConfig.ts:12 の FETCH_TIMEOUT_MS = 3000)がタイムアウトするまで最大3秒ブロックします。動作としては意図通り(失敗時もビルド時既定値で割当継続)ですが、低速回線やhub障害時にはインストール直後の体感が「拡張機能をインストールしたのにしばらく何も起きない」状態になり得ます。許容できるトレードオフだとは思いますが、必要であれば tab 作成を先に行い、ページ側の useOnboardingVariant のフェッチ結果を待つ設計(現状のフォールバック経路と同じ)に寄せることで体感を改善できる余地はあります。将来的な改善検討事項として共有します。

その他:
型定義・命名・エラーハンドリングは一貫しており、ExperimentConfigSource(types.ts:20)の remote/fallback 区別や config_source のGA4送信も分析上妥当です。
テストカバレッジは新規ロジック(experimentConfig.test.ts、onboardingExperiment.test.ts、OnboardingWelcome.test.tsx、useOnboardingState.test.tsx)に対して網羅的で、レース・タイマー・アンマウント等のエッジケースまでカバーされています。
全14ロケールに onboarding_welcomeMessage / onboarding_welcomeContinue が追加されており、en/messages.json にのみ description が付与されているのは .claude/rules/i18n.md のルールに沿っています。

全体として実装の完成度は高く、上記はいずれも軽微〜中程度の指摘です。マージ前に必須というより、フォローアップとしての検討を推奨します。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

オンボーディング画面のABテストの実行

1 participant