概要
OKR: Outcome KR で「活動」でなく「成果」を測る
「売上を上げる」は目標ではなく意図。OKR では KR に数値・ベースライン・期限を必ず含める。「GMV ¥80M → ¥120M(Q3末)」が正しい形。KR が測定不可能だと OKR は飾りになる。
ICE スコアリング: 施策の定量優先順位付け
Impact × Confidence × Ease で 1〜1000 のスコアに変換。「声の大きさ」ではなく数値で施策をランク付けし、ICE ≥ 300 を今スプリントで着手するルールにすることで意思決定会議を 60min → 15min に短縮できる。
A/B テスト事前登録: HARKing(後知恵仮説)を防止
結果を見てから仮説を立てる「HARKing (Hypothesizing After Results are Known)」は統計的偽陽性を生む。サンプルサイズ・停止規則・成功基準を実施前に commit することで再現性のある施策検証が実現する。
KPI ツリー: GMV を分解してボトルネックを特定
GMV 単一指標では「なぜ下がったか」が分からない。送信数 × 開封率 → クリック数 → CVR × AOV の段階に分解すると、どのプロセスがボトルネックかが週次で一目で分かり、改善レバーを絞り込める。
問題
ECサイト MOps チームで「メールキャンペーン施策の意思決定プロセス」を抜本的に改善したい。現状フローには 7つの設計上の問題 が潜んでいる。問題点を全て洗い出し、OKR 設計・ICE スコアリング・A/B テスト事前登録・KPI ツリー・dbt mart を活用した Bad→Good リファクタリングを行え。
制約・前提条件
- チーム規模: PdM 1名、エンジニア 3名、マーケター 2名
- 扱うデータ: メール送信数/開封率/CTR/CVR/AOV/GMV(BigQuery + dbt)
- 意思決定の頻度: 週次スプリント + 月次戦略レビュー
- 既存ツール: BigQuery, dbt Core 1.8+, Looker Studio, Argo Workflows, DataDog
現状フロー (Before)
# ① PdM が月末に売上データを Excel で手動集計(3時間)
# 問題①: データ集計が属人化・遅延。意思決定が最新データで行われない
# ② 施策の優先順位は「声が大きい人」の直感で決定
# 問題②: バイアスが入る。定量的根拠なし
# ③ 目標は「売上を上げる」(抽象的、測定不可能)
# 問題③: KR が測定不可能 → OKR が飾りになる
# ④ 施策リリース後、効果測定なし(次の施策へ)
# 問題④: 何が効いたか蓄積されない。同じ失敗を繰り返す
# ⑤ A/B テストは担当者が Excel で統計計算(p値間違いあり)
# 問題⑤: 手動計算の誤り + HARKing(結果見てから仮説作り)
# ⑥ KPI は GMV のみ追跡(途中プロセスの指標なし)
# 問題⑥: ブラックボックス。「なぜ下がったか」が分からない
# ⑦ 四半期 OKR は「お飾り」で実行に紐づかない
# 問題⑦: OKR と週次スプリントが接続していない
ヒント(段階的開示)
ヒント1 — 方向性
ヒント2 — アプローチ
- 問題①: 手動集計 → dbt
mart_campaign_kpi_weekly+ Looker Studio 自動ダッシュボード - 問題②: 直感 → ICE スコア(Impact × Confidence × Ease)で定量評価
- 問題③: 抽象目標 → OKR 設計(KR = 数値・ベースライン・期限の三点セット)
- 問題④: 効果測定なし → A/B テスト事前登録(仮説・サンプルサイズ・停止規則を先に書く)
- 問題⑤: 手動 p値 → BigQuery SQL で Z検定自動化
- 問題⑥: GMV のみ → KPI ツリーで開封率・CTR・CVR・AOV を週次追跡
- 問題⑦: OKR 乖離 → 週次スプリントに KR 進捗レビュー 15min を組み込む
ヒント3 — 骨格
-- ICE スコア計算式
ICE = Impact(1-10) × Confidence(1-10) × Ease(1-10)
-- 例:
-- 件名 A/B テスト(絵文字有無): Impact=7, Confidence=8, Ease=9 → ICE=504 ✓
-- 新規セグメント開拓: Impact=9, Confidence=3, Ease=2 → ICE=54 ✗
-- KPI ツリー構造
-- GMV = 送信数 × 開封率 × CTR × CVR × AOV
-- OKR の正しい KR 形式
-- NG: 「メールの品質を向上させる」
-- OK: 「開封率を 2026-Q3末までに 18% → 25% に引き上げる」
問題点分析(7点)
KPI ツリー図(GMV 分解)
模範解答
① 自動データ基盤 — dbt mart_campaign_kpi_weekly
# 問題①: PdM が毎週 Excel で手動集計(3時間)
# - VLOOKUP でキャンペーン名を結合
# - 開封率を手動で割り算
# - 前週比を手動で計算
#
# 結果: データが1週間遅れ・属人化・PdM の施策企画時間が0になる
-- models/mart/mart_campaign_kpi_weekly.sql
-- KPI ツリーの全段指標 + 前週比(WoW)を自動計算
{{
config(
materialized = 'incremental',
partition_by = {'field': 'week_start', 'data_type': 'date'},
cluster_by = ['campaign_id'],
on_schema_change = 'fail' -- スキーマ変更時は明示的にエラー
)
}}
WITH base AS (
SELECT
DATE_TRUNC(sent_at, WEEK(MONDAY)) AS week_start,
campaign_id,
COUNT(*) AS sends,
COUNTIF(opened_at IS NOT NULL) AS opens,
COUNTIF(clicked_at IS NOT NULL) AS clicks,
COUNTIF(converted_at IS NOT NULL) AS conversions,
SUM(IF(converted_at IS NOT NULL, order_amount, 0)) AS gmv
FROM {{ source('mops', 'email_events') }}
{% if is_incremental() %}
-- 過去2週ルックバック(遅延データを補正)
WHERE sent_at >= DATE_SUB(
DATE_TRUNC(CURRENT_DATE(), WEEK(MONDAY)), INTERVAL 2 WEEK
)
{% endif %}
GROUP BY 1, 2
)
SELECT
week_start,
campaign_id,
sends,
opens,
clicks,
conversions,
gmv,
-- KPI ツリー各段の中間指標(SAFE_DIVIDE で ZeroDivision ガード)
SAFE_DIVIDE(opens, sends) AS open_rate, -- 開封率
SAFE_DIVIDE(clicks, opens) AS ctr, -- クリック率
SAFE_DIVIDE(conversions, clicks) AS cvr, -- コンバージョン率
SAFE_DIVIDE(gmv, conversions) AS aov, -- 平均注文単価
-- 前週比(LAG ウィンドウ関数)
SAFE_DIVIDE(
gmv - LAG(gmv) OVER (PARTITION BY campaign_id ORDER BY week_start),
LAG(gmv) OVER (PARTITION BY campaign_id ORDER BY week_start)
) AS wow_gmv -- GMV 前週比
FROM base
② 施策優先順位 — ICE スコアリングフォーマット
# 問題②: 施策会議での会話例
# マーケター A「件名を変えればいいんじゃないですか」
# マーケター B「いや、やっぱり送信頻度を増やすべき」
# PdM「では両方やりましょう」(根拠なし、工数∞)
#
# 結果: 工数分散・優先順位なし・効果不明
## 施策評価シート(2026-06-18 Sprint #73)
| 施策 | Impact | Confidence | Ease | ICE | 仮説 | 成功 KR |
|------|:------:|:----------:|:----:|:---:|------|---------|
| 件名 A/B テスト(絵文字) | 7 | 8 | 9 | 504 | 絵文字で開封率+3pt | 開封率 22%→25% (2w) |
| 送信時刻パーソナライズ | 6 | 5 | 6 | 180 | 受信直後 CTR+2pt | CTR 8%→10% (4w) |
| 新規セグメント(休眠顧客)| 9 | 3 | 2 | 54 | 3ヶ月離脱顧客の再活性 | CVR 1%→1.5% (8w) |
# ICE ≥ 300: 今スプリントで着手(件名 A/B テスト)
# ICE 100-299: 来スプリントに検討
# ICE < 100: バックログへ移動(根拠が弱い → Confidence を上げてから再評価)
# ICE = Impact(1-10) × Confidence(1-10) × Ease(1-10)
# ポイント: Confidence が低い施策は小規模プロトで確認してから再評価
③ OKR 設計(測定可能な KR)
## Q3 2026 OKR — MOps メールチャネル
Objective: MOps メールチャネルを「顧客に届く」主力チャネルに変える
KR-1: GMV を 2026-Q3末までに ¥80M → ¥120M(+50%)に引き上げる
KR-2: 開封率を 2026-Q3末までに 18% → 25% に引き上げる
KR-3: CVR を 2026-Q3末までに 5.2% → 7.5% に引き上げる
KR-4: A/B テスト実施数を週1本以上に増やす(Q3合計 ≥ 13本)
## OKR アンチパターン(NG の KR)
# ❌ 「メールの品質を向上させる」 → 測定不可能
# ❌ 「施策を積極的に推進する」 → 活動(Output)であり成果(Outcome)でない
# ❌ 「A/B テストを頑張る」 → 数値・期限なし
## 正しい KR の書き方
# ✅ 「[指標] を [ベースライン] から [目標値] に引き上げる(by [期限])」
④ A/B テスト事前登録フォーマット(HARKing 防止)
## A/B テスト設計書(必ず実施前に GitHub commit)
# ファイル: experiments/exp_emoji_subject_2026q3.md
実験ID: exp_emoji_subject_2026q3
登録日: 2026-06-18(← 実施前に commit することが証明になる)
### 仮説
件名に絵文字を追加することで、開封率が +3pt 向上する
(ベースライン: 22%、目標: 25%)
### 介入
- Control: 「今週限定クーポン」
- Treatment: 「🎁 今週限定クーポン」
### 主要指標 / 副次指標
- Primary: 開封率
- Secondary: CTR(クリック率)
- Guardrail: 配信停止率 ≤ 0.5%(悪化しないことを確認)
### 統計設計
- 最小検出差(MDE): 3pt(絶対値)
- 検出力(Power): 80%
- 有意水準(α): 0.05(両側検定)
- 必要サンプルサイズ: 各群 3,500通(BigQuery で power analysis 済)
### 停止規則
- 7日間 OR サンプルサイズ到達(どちらか先に達した方)
- 早期停止禁止(バノーニ補正なしの常時監視は偽陽性を生む)
### 成功基準
- p < 0.05 かつ open_rate_diff > 0.02(統計的 + 実用的 両方の有意性)
⑤ BigQuery A/B テスト自動集計(Z検定 SQL)
# 問題⑤: Excel TTEST 関数を手動で入力
# → 引数の順序間違い・片側/両側の混同・繰り返し回数の誤設定
# → 「有意差あり」と誤判断してロールアウト → 本番で再現しない
-- mart/ab_test_results.sql
-- 大標本(n ≥ 30)の2標本 Z検定(中心極限定理)
WITH stats AS (
SELECT
variant,
COUNT(*) AS n,
AVG(opened) AS mean_open_rate,
STDDEV_SAMP(opened) AS std_open_rate
FROM mart.ab_test_email_opens
WHERE experiment_id = 'exp_emoji_subject_2026q3'
GROUP BY variant
),
-- Control と Treatment を横並びに
control AS (SELECT * FROM stats WHERE variant = 'control'),
treatment AS (SELECT * FROM stats WHERE variant = 'treatment'),
-- Z検定計算
z_test AS (
SELECT
t.mean_open_rate - c.mean_open_rate AS lift,
SAFE_DIVIDE(
t.mean_open_rate - c.mean_open_rate,
SQRT(
SAFE_DIVIDE(c.std_open_rate * c.std_open_rate, c.n) +
SAFE_DIVIDE(t.std_open_rate * t.std_open_rate, t.n)
)
) AS z_score, -- |z| > 1.96 で p < 0.05(両側)
c.n AS n_control,
t.n AS n_treatment
FROM control c, treatment t
)
SELECT
lift, -- 差分(絶対値)
z_score, -- Z統計量
n_control,
n_treatment,
IF(ABS(z_score) > 1.96 AND ABS(lift) > 0.02,
'✅ 有意差あり(採用)',
'❌ 有意差なし(継続 or 中止)') AS conclusion -- 統計的+実用的有意性
FROM z_test
⑦ OKR ↔ スプリント週次マッピング
## 週次スプリントレビュー議題(必須 15min — 毎週月曜)
### 1. KR 進捗確認(各 KR の現在値/目標値/トレンド)
KR-1: GMV ¥○○M / ¥120M(○%達成)↑↓
KR-2: 開封率 ○% / 25%(トレンド: ↑↓)
KR-3: CVR ○% / 7.5%(トレンド: ↑↓)
KR-4: A/B テスト本数 ○本 / 13本(週1本ペース確認)
※ Looker Studio ダッシュボードに KR ターゲットラインを重ねて表示 → 2クリックで確認
### 2. 今週完了施策の ICE vs 実績乖離
施策名: ○○ | ICE予測: 504 | 実績: open_rate +1.2pt(乖離の原因分析)
### 3. 来週着手施策の選定(ICE ≥ 300 から選ぶ)
### 4. A/B テスト状況
実施中: exp_emoji_subject_2026q3(残 3日 / 現在 n=2,100)
完了: exp_send_time_opt → 有意差なし → バックログへ
## エスカレーションルール
# KR 進捗が2週連続で前進なし → ブロッカー特定 + PdM ↔ EM でスコープ見直し
ポイント解説
ICE スコアリングの限界
ICE は相対比較ツールであり絶対的な正解ではない。「Confidence」が低い施策は小規模プロトタイプで確信度を上げてから再評価すること。ICE が高くても「前提仮説が間違っている」場合は全て水の泡になる。
Output KR vs Outcome KR
「A/B テストを週1本実施する」は Output KR(活動量)。KR-1〜3 は Outcome KR(成果)。Output KR だけでは事業価値が測れないため、必ず Outcome KR とセットにする。KR-4 は Output だが「実験文化の醸成」を測る指標として例外的に有効。
停止規則の事前登録(α エラー膨張)
A/B テストで「良い結果が出たら早めに止める」は第1種過誤率を膨張させる。バノーニ補正やシーケンシャル検定(mSPRT)でない限り、事前登録サンプルサイズまで走り切ること。早期停止は「再現しない施策」の主因。
KPI ツリーの因果と相関の混同
開封率が上がっても CVR が下がれば GMV は改善しない。「どの段でロスが最大か」をボトルネック分析し、レバーを絞る。週次でツリー全段の前週比(WoW)を確認することで真の改善ポイントを特定できる。
SAFE_DIVIDE の実務意義
送信数0のキャンペーンで ZeroDivision になるとダッシュボード全体が壊れる。SAFE_DIVIDE は NULL を返すため Looker Studio でグラフが崩れず、dbt テストで not_null チェックとも相性が良い。本番 SQL での必須イディオム。
実務への応用
MOps/販促チームでは毎週「どの施策を優先するか」の議論に多くの時間が費やされる。ICE スコアと事前登録 A/B テストを導入すると:
- 意思決定会議の時間を 60分 → 15分に短縮(感情論ではなく ICE 数値で決まる)
- 偽陽性の A/B テスト結果によるロールアウト失敗を防止(「効果あったはずなのに本番で再現しない」問題の根絶)
- OKR と Looker Studio ダッシュボードの接続で、週次 KR 進捗確認が 2クリックで完了
- PdM の工数配分を変える: データ整形 3時間 → 施策企画・仮説立案 3時間