關於那些不屬於你的資料
股票工具完全跑在我無法掌控的資料上。這是一篇關於「建立在借來的事實之上」——那種安靜、帶著防禦性質手藝的筆記。
股票工具徹底而永久地依賴著一批我既不擁有、也無法掌控的資料。每一個價格、每一筆法人籌碼數字、每一份財報,都來自別的地方——台灣證券交易所、櫃買中心、集保結算所,還有幾個第三方 API。我的軟體,是疊在別人事實之上的一層翻譯。這也意味著,任何一個早晨,某個東西壞掉最有可能的原因,都是某個我控制不了的來源,沒知會我一聲就改了什麼。
我想把「建立在借來的資料之上」到底是什麼感覺寫下來,因為這是獨立開發裡沒人會提醒你的一個類別,而它安安靜靜地,是最難的部分之一。
來源會壞,而且是無聲無息地壞。
我學會最怕的失敗模式,不是那種吵鬧的——來源掛掉、拋出錯誤、頁面跳出一個紅色框框。那反而是*簡單*的情況。錯誤是誠實的;你看得到,你就修得掉。危險的是那種無聲的:一個來源回傳了一份格式完全正確、內容看起來也完全正常的回應,只是它剛好是空的、或是過期的、或是有點微妙地錯了。任務跑完了。沒有拋出任何錯誤。儀表板一片綠。而資料其實從四天前就悄悄停止更新了,沒有人發現,直到有個使用者寄信來問,為什麼某個數字看起來怪怪的。
早期有一次,我的一個上游來源,開始在某條網路路徑上回傳轉址、換一條又不會——只有當請求是從某種特定類型的伺服器發出時才看得到。擷取任務沒有崩潰。它拿到一份空清單,寫進零筆資料,記下一筆漂漂亮亮的「成功」,然後繼續往下走。資料在那個綠色勾勾後面過期了好幾天。這個教訓刻進去了:當你依賴一批你不擁有的資料,「任務成功了」和「資料是對的」是兩個完全不同的主張,而你必須另外去驗證後者——因為前者會眉開眼笑地騙你。
於是你蓋了第二套系統,它唯一的工作就是懷疑。
解法不是把擷取這件事做得更可靠。我沒辦法;它不是我的。解法是蓋一個小小的、獨立的看門狗,它整個存在的目的,就是不信任第一套系統。它不問「任務跑了嗎?」它問的是「以今天是哪一天來看,資料庫裡最新的那一列,真的有它該有的那麼新嗎?」如果資料庫裡最新的交易日比它應該要有的還舊,或者一條本該寫進一千列的管線只寫了一百列,它就會說出來——大聲地、用 email,趕在使用者之前。它把「成功」預設當成謊言,除非資料自己證明不是。
我想,這對任何建立在借來資料之上的人來說,是一條可以通用的原則:把「做事的東西」和「檢查那件事的東西」分開,並且讓那個檢查者可以疑神疑鬼。一套靠問自己「我做完了嗎?」來監控自己的系統,永遠只會答「做完了」。你需要第二套系統來問「這結果到底好不好?」——而且它必須被允許,對這件事講話很不客氣。
你會繼承別人的行事曆、單位和慣例。
借來的資料,附贈的是借來的假設,而它們會咬人。有一個來源用「股」報成交量;另一個,同一個市場,用「張」(一千股)來報——如果你沒注意就把它們混在一起,一檔股票的成交量會突然錯了三個數量級,而且錯得幾乎像真的一樣。假日又是另一回事。市場休市,所以沒有新資料,所以一個天真的新鮮度檢查會在每個週末、每個國定假日尖叫著說一切都壞了,直到你把交易日曆教給它。每個來源都有自己的節奏、自己安靜的慣例、對那些你以為是普世通用的字詞的自己一套定義。這些你不會從文件裡學到。你是靠在正式環境裡做錯,才學會的。
責任只往一個方向流。
接下來這部分我覺得最重要,而且它跟技術無關。當你建立在借來的資料之上,使用者信任的是*你*,不是你的來源。如果交易所公布了一個錯的數字、而我把它顯示出來,讀者不會怪交易所——他們根本沒聽過那個 API。他們會怪這個工具。信任流向他們接觸到的那個表層,也就是我的;而怪罪也一樣,即使那個失敗發源自上游三層、完全不在我手上。
這也不能說不公平。這就是這筆交易的條件。如果我要站在一個人和一堆公開資料之間,宣稱要讓它變得看得懂,那我就接受了「讓它看得懂這件事必須是*對的*」的責任——包括在我的供應商放我鴿子的那些日子。這就是為什麼這個產品上這麼多不起眼的工作,本質都是防禦性的:每個數字上的合理範圍界限、備援來源、那個疑神疑鬼的看門狗、還有在真的沒東西可以顯示的日子裡那些友善的空狀態畫面。這些東西運作正常時,一個都看不見。但它們全部,就是這份工作本身。
建立在你不擁有的資料之上,有點像用一個你打不了電話的供應商送來的食材煮菜。大多數日子,箱子送到、一切都好。這門手藝,完全在於箱子出問題那天你怎麼做——以及,趕在你的客人之前就注意到,今天正是那種日子。