iPhone アプリ minulo を作っている北林です。個人で作っているアプリの計測に PostHog を使っています。
10/25 に PostHog の中身を一度ぜんぶ棚卸ししました。保存済みのグラフ 75本、ダッシュボード 5つ、コホート・アクション・アラート・実験・注釈です。結果として、毎週見ていた数字のいくつかがずれていました。グラフはどれもエラーなく動いていたので、見た目では気づけませんでした。
この記事では、そのとき使った点検の手順をチェックリストの形でまとめます。PostHog を1人か少人数で回していて、グラフが増えてきた人向けです。
チェックリスト
- 保存済みのグラフを全部
/query/で流す - テスト端末の除外が本当に効いているか確かめる
-
{filters}とfilterTestAccountsが両方そろっているか見る - アラートのしきい値の向きを確かめる
- 版の絞りを文字列で比べていないか見る
- 国を何から出しているか確かめる
- 届かなくなったイベントを見ているグラフとアラートを探す
- 注釈と実験の登録を、外の記録と突き合わせる
- 変える前に JSON を保存し、PATCH は変える欄だけ送る
以下、1つずつ書きます。
1. 保存済みのグラフを全部 /query/ で流す
まず、グラフが動くかどうかを機械的に確かめました。保存済みの insight の query を取り出し、ブラウザでログインした状態のまま、同じオリジンの REST API に1本ずつ投げます。読むだけなので何も変わりません。
結果は 75本すべて HTTP 200、エラーは0でした。値が全部0だったのは1本だけです。
ここで大事なのは、エラーが0でも数字が正しいとは限らないことです。この後の問題は、どれもエラーを出さずに静かに数字をずらしていました。流した結果は「何行返ったか」も一緒に残しておくと、あとで直した前後を比べられます。
2. テスト端末の除外が効いているか確かめる
いちばん影響が大きかったのがこれです。
17本の HogQL で、テスト端末の除外を次の1行だけでしていました。
and person_id not in cohort 568663
568663 は PostHog が用意する「Internal / Test users」のコホートです。ところが、このプロジェクトは人のプロフィールを作らない設定にしていました(全イベントが $process_person_profile=false)。人がいないので、このコホートは常に0人です。0人を外しても何も外れません。
直近30日で、テスト印($internal_or_test_user=true)の付いた端末は 25台・6,061件ありました。それがそのまま入っていました。
特にずれが大きかったのは、件数の少ないお金まわりです。
-
trial_started(7日間の無料体験の開始)32件のうち 23件がテスト端末 -
trial_ended(体験の終わり)は 3件すべてがテスト端末
直したあと、お金の流れの表では、7日間の体験を始めた新しい人(6週の合計)が 24 → 8 になりました。「7日間 → 払ったか」の表は、体験が終わった人 3人 → 0人 で、表が空になりました。つまり、本物のユーザーで体験が終わった人はまだ1人もいなかったのに、テスト端末の3件を見ていたことになります。
確かめ方は簡単です。除外が効いているはずのグラフについて、テスト印の付いた件数を数えます。
select countIf(coalesce(toString(properties.$internal_or_test_user), '') = 'true')
from events
where event = 'trial_started'
and timestamp > now() - interval 30 day
ここで0にならない数が出て、グラフの側にもそれが入っていれば、除外は効いていません。コホートで外している場合は、そのコホートの人数も一緒に見ておくと早いです。
3. {filters} と filterTestAccounts が両方そろっているか見る
直し方は、コホートの1行を {filters} に置き換えて、insight の filters に filterTestAccounts: true を付ける形にしました。
and {filters}
"filters": { "filterTestAccounts": true }
これでプロジェクトの設定にあるテスト除外($internal_or_test_user の付いた端末や、指定した IP)が効きます。
ここにもう1つ落とし穴がありました。この2つは片方だけだと効きません。
- query に
{filters}があるのに、filterTestAccountsの指定が無いグラフが1本 -
filterTestAccounts: trueは付いているのに、query に{filters}が無いグラフが2本
どちらもエラーは出ず、画面上は「除外あり」に見えます。前者を直すと、人数の合計が 1606 → 1553 になりました。
なお、$internal_or_test_user だけでなく IP の除外も効くので、Pro を始めた人が 51 → 49 になりました。この2人はテスト印ではなく IP の除外で外れた人です。
4. アラートのしきい値の向きを確かめる
「北極星(週に SNS を開いた人)が前の週より20%減った」というアラートが、10/18 から Firing のままでした。
週の人数を見ると、9/27 の週 101 → 10/4 158 → 10/11 280 → 10/18 612 です。減っていません。毎週増えています。
原因はしきい値の向きでした。このアラートの値は「前より減った割合」で、10/15 の値は −0.564 でした。
(158 − 101) / 101 = 0.564 増えた → 値は −0.564
マイナスは「増えた」という意味です。それなのにしきい値が「−0.2 を下回ったら鳴らす」になっていたので、20%以上増えると鳴る設定になっていました。上限 0.2(20%以上減ったら鳴る)に直して、状態は Not firing になりました。
点検するときは、アラートが見ている値を実際の数字で1回手計算して、符号の向きを確かめるのが確実です。
ついでに、しきい値の大きさも実物の数字と比べました。「新しく入れる人(7日平均)が1日40人を下回ったら」というアラートは、今の値が1日105人ほどなので、半分以下に落ちないと鳴りません。今の約半分の 50人に上げました。
5. 版の絞りを文字列で比べていないか見る
新機能を見る表に、こういう絞りが入っていました。
and toString(properties.app_version)='1.0.12'
1.0.12 だけを見ているので、次の 1.0.13 の人が入りません。直近30日で 1.0.12 は8人、1.0.13 は18人でした。
「1.0.12 以降」に広げるとき、文字列のまま >= で比べると困ったことになります。文字列は1文字ずつ比べるので、'1.0.9' > '1.0.12' が真になります。1.0.9 が入って、1.0.12 より新しいと判定されてしまいます。
そこで、点で区切って数字の配列にしてから比べました。
arrayMap(x -> toIntOrZero(x), splitByChar('.', toString(properties.app_version))) >= [1, 0, 12]
1.0.8・1.0.9・1.0.11 が外れ、1.0.12・1.0.13・1.1.0 が入ることを確かめてから保存しました。直したあと、1.0.12 以降で開いた人(日ごとの合計)は 8 → 17、はじめ方の流れの1段目は 6 → 14人になりました。
別のファネルでは、名前が「1.0.8〜1.0.11」なのに、各段が app_version=1.0.8 に固定されていました。直すと1段目が 125 → 1321 人になりました。名前と中身が合っているかも、版の絞りでは見ておくところです。
6. 国を何から出しているか確かめる
国別のグラフは、もともと GeoIP の国コードを使い、無ければアプリの言語から国を当てる式になっていました。
multiIf(properties.lang='ja','JP',properties.lang='ko','KR',properties.lang='fr','FR',properties.lang='pt','BR',properties.lang='en','EN','XX')
ところが GeoIP を切っていたので、直近7日の app_open 4,010件のうち、$geoip_country_code が入っていたのは0件でした。つまり国のグラフは全部、言語から当てた値でした。英語の画面を使っている人は、どこの国の人でも「英語圏」に入ります。札には「国は回線から推定」と書いたままでした。
端末の地域設定(device_region)を使い、無ければ言語設定の地域(device_locale の -XX の部分)を使う形に直しました。英語圏は US・GB・CA・AU だけにしました。
- 21日の合計:英語圏 416 → 195、その他 169 → 512(合計 1349 は同じ)
- 直近4週:アメリカ 6 → 109、カナダ 12 → 40、イギリス 9 → 31
アメリカが6人だと思っていたのが、実際は109人でした。広告や掲載文の言語をどこに向けるかを決める数字なので、これがいちばん怖いずれでした。
点検では、国に使っている属性について「入っている件数」を数えてみるのが早いです。0件なら、そのグラフは別の何かで国を作っています。
7. 届かなくなったイベントを見ているグラフとアラートを探す
イベントの最後に届いた日と、グラフが参照しているイベントを突き合わせました。
-
layout_broken:一度も届いていない。これを見るグラフはいつも0、アラート(1日5回超)は鳴りようがない -
trial_converted:一度も届いていない。アクション「払った」の片方の条件と、実験の主な指標になっていた -
sleep_toggled:9/23 が最後。アプリ側でbed_setに替わっていた
アラートは消さずに無効にして、名前の頭に「停止中:未着」と付けました。グラフは名前に「(未着)」や「〜で止まっている」と付け、sleep_toggled のグラフは bed_set に替えました。
値が0のグラフは「何も起きていない」のか「イベントが来ていない」のか、見た目では区別できません。イベント一覧の「最後に届いた日」と並べて見るのが確実です。
8. 注釈と実験の登録を、外の記録と突き合わせる
注釈は 10/14 の「1.0.11 公開」で止まっていました。1.0.12 と 1.0.13 の公開を足そうとして App Store Connect を確かめたところ、1.0.12 も 1.0.13 も公開されていませんでした。公開中は 1.0.11 のままで、1.0.13 は審査中です。グラフに出ていた 1.0.12・1.0.13 の人は、テスト端末か審査の端末だと思われます。手元の記録だけを見て「公開」の注釈を入れていたら、間違った線をグラフに引くところでした。注釈には提出や却下の事実だけを書き、10本足しました。
実験も同じです。棚卸しの段階では「実験の指標が登録されていない」と書いていましたが、直す担当が確かめると、10/20 から主の指標2本と副の指標2本が登録済みでした。こちらは棚卸しのほうが誤りでした。点検の結果も、直す前にもう一度実物で確かめたほうがいいです。
9. 変える前に JSON を保存し、PATCH は変える欄だけ送る
直すときの決まりは2つにしました。
- 変える前に、対象の JSON を丸ごと保存する。 insight は query 全文、コホート、アクション、アラート、ダッシュボードの札の本文、名前と説明。保存したものと元を照らして、1文字ずつ一致することを確かめた
- PATCH では変える欄だけを送る。 名前を変えるなら name だけ、除外を直すなら query と filters だけ。取ってきた JSON を全部送り返すと、関係ない欄まで上書きしかねない
消したものも、論理削除(deleted: true)だけにしました。どの盤にも載っておらず、9/23 から誰も見ていないグラフが1本、同じ数字を別の形で出していた重複が4本です。
直したあとは、変えたグラフを全部もう一度 /query/ で流し、エラーが0であることと、前後の数字を記録しました。
まとめ
今回ずれていたものを並べると、こうなります。
| 点検 | 見つかったこと | 前 → 後 |
|---|---|---|
| テスト端末の除外 | 0人のコホートで外していた | 体験の開始 24 → 8、体験の終わり 3 → 0 |
{filters} |
片方だけで効いていなかった | 人数の合計 1606 → 1553 |
| アラートの向き | 増えると鳴る設定 | −0.564 は増加だった |
| 版の絞り | 1.0.12 だけを見ていた | 開いた人 8 → 17 |
| 国 | 言語から推定していた | 英語圏 416 → 195、US 6 → 109 |
どれもエラーは出ず、グラフは毎日それらしい数字を出していました。グラフが数十本を超えたら、ときどき全部を流し直し、除外・しきい値・版・国の4つだけでも確かめると、ずれに早く気づけると思います。