上一篇我們透過 Goals-Signals-Metrics(GSM),從「希望使用者成功」一路往下找到可以觀察的信號與指標。
以商品頁面的案例來說,我們希望使用者能判斷商品是否符合自己的需求,而可能出現的信號包含:
但知道要觀察什麼之後,還有一個很工程師的問題:
這些東西到底要怎麼寫進程式?
前一篇最後,我們很快地用了這段程式碼:
posthog.capture('update_cart', {
action: 'add_item',
item_id
})
PostHog 的 capture() 可以讓我們主動記錄一個事件(Event),第一個參數是事件名稱,第二個參數則可以帶入這次事件的屬性(Property)。
實際開發時,真正麻煩的通常不是怎麼呼叫 capture(),而是:到底什麼事情值得被記錄?
在前面安裝 PostHog 時,其實我們還沒有寫多少事件追蹤的程式碼,PostHog 就已經可以取得不少資料。
其中一部分來自 Autocapture。PostHog 可以自動收集網頁上的部分互動,因此即使沒有為每個按鈕手動加入 capture(),我們仍然可以看到使用者在頁面上的一些操作。
這對剛開始使用產品分析來說非常方便。
例如今天想知道:
有沒有人使用新加入的按鈕?
如果相關操作已經被 Autocapture 收集,我們可能不需要為了回答這個問題立刻修改程式碼。
不過,Autocapture 看到的主要是「使用者操作了什麼介面」,不一定知道這個操作在產品中的意義。
假設商品頁面上有一個「加入購物車」按鈕,我們可能可以知道某個按鈕被點擊了,但真正想觀察的其實是:
使用者成功把商品加入購物車。
這兩件事情並不完全相同。
使用者可能點了按鈕,但後端請求失敗;也可能同一個操作之後改成選單、鍵盤快捷鍵,甚至由其他流程觸發。如果我們的指標綁在特定 DOM 元素上,介面一改,原本想觀察的行為也可能跟著失去意義。
這時候,自訂事件就比較適合。
例如:
posthog.capture('add_item_to_cart', {
item_id,
})
比起記錄:
posthog.capture('click_add_to_cart_button')
前者描述的是產品中實際發生的行為,而不是這個行為目前剛好由哪個按鈕觸發。
並非是說記錄 UI 事件是不好的方法,如果我們現在想回答的問題就是:
使用者有沒有注意到這顆按鈕?
那麼追蹤這個事件就是合理的。
重點仍然是回到前一篇的問題:
我們想觀察的是什麼?
事件並不是因為「這裡可以埋點」就需要建立,而是因為我們需要它來回答某個問題。
除了決定事件要不要存在之外,另一個問題是事件應該怎麼設計。
例如購物車可能有很多操作:
posthog.capture('add_item_to_cart', {
item_id,
})
posthog.capture('remove_item_from_cart', {
item_id,
})
也可以像前一篇一樣寫成:
posthog.capture('update_cart', {
action: 'add_item',
item_id,
})
這兩種方式並沒有一個放諸四海皆準的答案。
如果我們經常想分別觀察「加入購物車」與「移除購物車」,拆成不同 Event 可能比較直接。
但如果它們本來就是同一類操作,而且分析時經常需要一起觀察,那麼使用同一個 Event,再利用 action Property 區分也可能比較方便。
Property 的用途就是補充事件發生時的資訊。
例如:
posthog.capture('update_cart', {
action: 'add_item',
item_id,
product_category,
quantity,
})
之後我們除了能知道 update_cart 發生幾次,也可以進一步詢問:
哪一類商品最常被加入購物車?
一次通常加入幾個?
加入與移除的比例有什麼不同?
這些資訊在下一篇介紹 Product Analytics 時就可以拿來進行 Filter 或 Breakdown。
不過,這也不代表 Property 越多越好。
很容易出現一種情況:既然都已經寫到這裡了,就順便把商品名稱、價格、庫存、賣家、建立時間、折扣、配送方式全部塞進去。
雖然這不會帶來額外成本,但如果現在完全想不到用途,不一定需要因為「以後可能會用到」就先收集。
後續我們也會介紹關聯查詢,來補足原先沒有紀錄,但實際上已經儲存的資料。
即使事件名稱一樣,觸發的時間點不同,也可能得到完全不同的資料。
例如:
posthog.capture('purchase_completed')
什麼叫做 purchase_completed?
是使用者按下「付款」按鈕?
是前端收到付款 API 成功?
還是後端真的確認訂單已完成?
如果我們想用這個事件衡量「完成購買」,那麼在使用者按下按鈕時就 Capture 顯然太早了。
同樣的情況也會出現在檔案上傳:
開始上傳
→ 上傳中
→ Server 接收完成
→ 檔案處理完成
如果我們真正關心的是「使用者成功上傳檔案」,事件就應該盡量接近最後真正代表成功的時間點,而不是只因為前端最容易在按下按鈕時埋點,就選擇那個位置。
換句話說,事件的觸發條件也是 Metric 定義的一部分。
如果兩名工程師對 purchase_completed 的理解不同,即使事件名稱完全一樣,最後算出來的指標也可能完全不同。
對規模很小的產品而言,不需要一開始就建立非常複雜的 Tracking 規範。
但至少可以在加入事件以前,簡單整理:
| 想觀察的 Signal | Event | 什麼時候觸發 | Property |
|---|---|---|---|
| 使用者把商品加入候選 | update_cart |
商品成功加入購物車後 | action, item_id |
| 使用者移除候選商品 | update_cart |
商品成功移除後 | action, item_id |
| 使用者完成購買 | purchase_completed |
訂單確認完成後 | order_id, total |
這張表真正重要的不是格式,而是讓我們在寫:
posthog.capture(...)
之前先確認:
這個 Event 對應哪個 Signal?
以及:
什麼情況下,這個 Signal 才算真的發生?
如此一來,事件就不只是散落在程式碼中的 Tracking Code,而是前一篇 GSM 中定義的觀察方式真正落到產品裡。
當然,實際系統還會遇到更多問題。
例如同一筆訂單會不會被前端與後端各送一次?使用者登入前後的事件是不是同一個人?瀏覽器擋掉 Tracking Request 時又該怎麼辦?
這些問題會直接影響資料是否可信,我們會在後面的文章再回來處理。
現在,我們已經先完成最基本的一步:把原本抽象的 Signal 轉換成 PostHog 可以收集的 Event 與 Property。
但資料收集進來之後,如果只是放在 PostHog 裡,也還沒有太大意義。
下一篇,我們就開始使用 Product Analytics,把這些 Event 轉換成真正可以觀察的指標與圖表。
如果你願意花 30 秒留下回饋,我會用這些意見來調整後續文章:分享你的意見
