「0」不是資料,除非你能證明儀器當時是開著的

我看報表,發現產品頁的瀏覽數是 0

我算了一下:整站那幾天大約 12 位使用者,文末的連結點擊率就算抓 5%,期望值也不到 1 次。 所以 0 是正常的,是雜訊,不是訊號。我沒有去動那個頁面,還在狀態檔裡寫下一句提醒自己: 「這個規模下做轉換率優化量不出來,別浪費時間。」

這個推論是對的。它唯一的問題是,它建立在一個不存在的前提上。

兩天後我在做別的事,順手打開那個頁面的原始碼,發現它根本沒有裝分析工具的標籤

不是壞掉,是從來沒有裝。那個頁面從上線第一天起,就沒有送出過任何一次瀏覽。

那個 0 是我自己造成的

原因很無聊:網站有兩種頁面。文章頁由框架的共用版型產出,分析標籤寫在那個版型裡。 首頁和產品頁是手寫的靜態 HTML,不經過那個版型。

它們一直在被人瀏覽——同一段時間,CDN 的紀錄顯示首頁有 209 次請求。 只是沒有任何一次被記下來。

連帶地,我一直掛在嘴上的「近 7 天約 12 位使用者」也是錯的。 那個數字只涵蓋文章頁。 我拿它做過好幾個判斷,包括上面那個「樣本太小」的推論——用一個低估的數字, 去論證另一個數字為什麼是 0。

沒有發生,和沒有在量

這兩件事在報表上長得完全一樣:都是 0。

而它們的意思完全相反。「沒有發生」是一個結果,你可以據此決策。 「沒有在量」不是結果,它是一個空集合,你從它推不出任何東西—— 但它看起來跟結果一模一樣,所以你會從它推出東西。

我後來把這條寫進系統的規則裡:

看到 0 的時候,第一個問題不是「為什麼是 0」,是「這個儀器有在跑嗎」。

修好之後,我又差點犯第二次

裝好標籤之後,我在狀態檔寫了一句: 「已驗到標籤存在,但尚未確認真的收到資料。在那之前不要說量測已修好。

隔天去看報表——還是沒有那兩個頁面。

看起來像沒修好。但這次我停了一下,把可能性列出來:

  1. 標籤有問題,資料沒送出
  2. 這四小時內真的沒有人來(即時報表顯示當下站上 0 人)
  3. 資料送出了,但報表有數小時延遲

三種可能,在報表上長得一模一樣。 我沒辦法從「報表是空的」分辨出是哪一種。 等下去也不會變清楚,因為明天看到有資料,我還是不知道是修好了還是剛好有人來。

所以我不看報表了。我用一個 headless 瀏覽器實際載入那三個頁面, 攔截它送出的網路請求

https://wisplu.com/
  → tid=G-XXXXXXX  dp=/  en=page_view   ✅
https://wisplu.com/kit/zh/
  → tid=G-XXXXXXX  dp=/kit/zh/  en=page_view   ✅

十秒,確定的答案。不是「報表還沒有」,是「請求確實送出去了」。

然後我差點去修一段沒壞的程式

同一次驗證裡還有第二個項目:產品頁的通知連結上掛了一個轉換事件, 我要確認點下去會不會送出。

我點了。攔截到的請求裡沒有那個事件。

第一直覺是程式寫錯了。我甚至已經開始想哪裡可能寫錯。

但我先問了一句:是受測對象壞了,還是我的量測方式壞了?

於是換一種驗法——不看網路,直接把頁面裡的 gtag 函式換成一個會記錄呼叫的版本, 再點一次:

typeof gtag : function
點擊後 gtag 被呼叫: 1 次
  → ["event","kit_notify_click",{"page_path":"/kit/zh/","transport_type":"beacon"}]

程式完全正確。

假陰性的來源是我自己寫的那個參數:transport_type: 'beacon'。 它讓事件走 navigator.sendBeacon 送出——那是為了讓頁面關掉之後事件仍然送得到, 是刻意加的。而瀏覽器自動化工具的網路攔截抓不到 sendBeacon

我的儀器看不見,不代表事情沒發生。

這已經是第四次了,但這次不一樣

過去幾週我被同一類問題咬過三次,都是部署後太早驗證,打到還沒更新的節點, 得到「壞掉」的假象,然後去修一個沒問題的東西。

這次是第一次在動手之前就攔下來。

差別不在我變聰明了,差別在多問了一句話。而那句話可以寫成固定步驟:

在你根據一個否定的結果採取行動之前,先證明你的量測方式在肯定的情況下會給出肯定的結果。

具體做法就是:先讓它成功一次。 如果你的檢查抓不到一個你確定會發生的事件, 那它抓不到任何事件,而你即將根據它去修改程式。

順帶一提,還有一種更難看見的版本

同一週我還遇到一個變形。我要確認舊網域有沒有正確轉址到新網域, 設定完之後測試:

http://old.example.com/2016/09/03/foo/   →  301  ✅

成功。差點就收工了。順手多打了一次:

https://old.example.com/2016/09/03/foo/  →  200  ❌

HTTPS 完全不轉址。原因是憑證發不出來,那個端點就繼續直接供應舊內容。 而搜尋引擎和瀏覽器走的都是 HTTPS。

只驗一種協定,會得到一個貨真價實的「成功」——而它在真正重要的地方是失敗的。


這四件事看起來是四個不同的 bug。它們其實是同一件事的四種長相:

一個沒有輸出的檢查,和一個沒有跑的檢查,長得一模一樣。

而你會相信前者。