論述基礎與限制

本卡的觀察來自一次書單分類的收尾。那批內容有三類判定會隨時間失準——時效切分、角色歸屬(哪本書在該主題裡承擔什麼位置)、處境相容性——而判定、判定的記錄、判定的觸發全部出自同一個作者,因此「認真做過」與「完全沒做」在頁面上長得一樣。補救的做法是給每一類一個可以從頁面外核對的指標。處境相容性有現成的一個:以書目小節為計數單位(一本書、或一組合寫的書,佔一個小節),五十四個小節裡二十三個寫了這一項,數字可數。照同一個做法為時效補指標時,兩個獨立的問題才浮出來。

限制:本卡談的是用來證明判定做過的指標,不談判定本身的品質,也不談自動化檢查的規則設計。指標的作用只有一個——讓維護狀態從外部看得出來。

這條原則在什麼條件下會被推翻,比它有幾個案例更值得先講:做得出一個「該偵測的狀態發生時仍然會動」的飽和指標,本卡就錯了。它由「指標要能區分它存在要區分的那兩種狀態」這個機制推出,書單那批是例示不是樣本——下面記的三次重犯出自同一個 batch、同一位作者、同一輪收尾,它們說明的是這個錯誤在知道規則的情況下仍然會發生,不是它多常發生。


核心原則

一個用來證明「判定真的做過」的東西,它的值必須跟它該偵測的狀態連動。 價值不在它現在的數值,在該偵測的事情發生時它會不會動。會下降只是連動的一種形態,而且只在覆蓋率還沒滿的時候管用:已經飽和的覆蓋率(54/54)在既有判定隨時間變質的時候不會動,因為變質不改變「有沒有寫」這件事——它只偵測得到新增項目漏寫。單調遞增的量也可以連動(每一項的上次複核日期久久沒動就是訊號),所以要問的是連動不是方向。而這批裡最需要偵測變質的那一維(時效),正好是最早被寫滿的那一維。

這條推論有一個容易漏掉的分支:覆蓋率一旦飽和就不可能再下降,而那不表示該放棄核對——它表示錨要從聚合層(一個會動的數字)換到單項層(每一項各自可以被判偽的東西)。已經飽和的覆蓋率不會再動,所以在同一個量測對象上找第二個數字沒有用。換量測對象仍然是一條路:隨機抽五項重判、記錄改判比例,或數「距上次複核超過十二個月的項目數」,兩者都是聚合、都是數字、都跟變質連動。單項層是另一條,成本更高但不需要重判。「錨」的完整定義因此比「會下降的量」寬一點:作者以外可以獨立判偽的東西,聚合指標只是它在覆蓋率還沒滿時的一種形態。

這個處境不限於書單。凡是判定、判定的記錄、判定的觸發都出自同一個位置的體系都算——review checklist 的自評欄、風險登記簿、合規控制項的自我聲明、無障礙檢查表。共同特徵是頁面上看不出「認真做過」與「完全沒做」的差別,而補指標的動機就從這裡來。

第二個問題出在量測方式。覆蓋率的量測方式是一個設計選擇,而那個選擇可能跟同一批內容的其他規則衝突。 用關鍵詞出現次數當覆蓋率的代理時,指標實際量的是「有幾個小節用了這個詞開頭」,而不是「有幾個小節做了這件事」。同一批內容如果還有一條「句型要有變化」的規則,執行那條規則會讓指標下降——於是指標在內容變好的時候變差,而指標與規則之中一定有一個會被放棄。


反模式

把覆蓋率寫進頁面而沒有註明計數單位。 該批內容原本寫的是「五十二個書目條目裡二十三個寫了這一項」,而重新計數時,三種數法給出三個不同的數字:數購書連結區塊得 54、數書目小節得 54、數帶有證據來源判定的句子得 52——原文的分母取的是最後一個,跟分子不同單位。分母沒有定義時,這個數字沒有人能重算,於是它不是錨,是另一句自陳。

這個錯誤在寫下本卡的同一輪重犯了兩次。第一次:補上分佈之後,頁面寫的是「二十四個小節寫出處境限制」,而那個 24 取自 處境上出現次數,小節數是 23。第二次發生在改正的時候——差額被寫成「差一,因為維度定義段自己也用了那個詞」,而實際是差二:另一個來源是某個合寫三本書的小節裡出現了兩次,那要逐節數才看得見,而改的人只重掃了全站次數。定義了計數單位不會自動讓計數照那個單位跑,而解釋差額也還不是照單位跑。三件事要各驗一次:寫下單位、用那個單位重數、逐項核對差額的每一個來源。

用關鍵詞計數當覆蓋率的代理。 實例:以 時效上|時效要 掃描得到 51/54,看起來有三個缺口。逐節檢查後發現三處都有時效判定,其中兩處只是換了句子的開頭(「一本 1987 年的書會讓人先問哪些部分還算數」「1983 年的書,書中的製造業類比…」)。這兩處恰好是全批最老的兩本書,也就是時效判定最該存在的兩本。指標把它們判成缺口,而它們是全批寫得最完整的。

用飽和的指標宣告健康。 覆蓋率到 100% 之後,把它繼續列為「這一維有人在維護」的證據。它此後只回答一個問題:新增的項目有沒有漏寫。既有項目的判定是不是已經跟現實脫節,這個指標永遠答不了,而那正是它被設立時想擋的事。

這一條的後果最不直觀,實際會走成這樣:寫滿的那一刻沒有人覺得有問題,之後一年裡有幾則判定跟現實脫節,而每次有人問「這批還準嗎」,拿到的都是同一個滿分。沒有被發現是因為唯一在看的人就是寫的人,而他看的正是那個不會動的數字。等到有人照著判定去做決定、做完才發現前提早就不在,要收拾的是把全部項目重讀一次逐項重判,成本跟第一次做一樣大。


修法

  1. 先問這個指標在什麼情況下會動。 答案是「只有新增項目漏寫時」就代表它只覆蓋一種失效;那一維的主要失效若是變質而非漏寫,換一個指標——換的方向見第 4 條。
  2. 分母要寫出計數單位、語料範圍與扣除規則,三者缺一就不可重算。 「五十四個書目小節」這個例子本身要補完才成立:語料是各主題篇與技藝篇(不含目錄頁),單位是 H2 段,扣除規則是每篇末尾的「為什麼只收這幾本」與「這個主題接到哪裡」兩節。少了後兩項,第三方跑最直觀的 rg -o '^## ' 會得到九十,而扣掉哪些只在作者腦裡。「五十二個條目」則連單位都沒有,第三方不知道該數什麼。
  3. 關鍵詞不當分子。 關鍵詞量的是措辭,而判定寫在哪一種句型裡是作者的自由——要可數就數結構單位(小節、欄位、條目)。它仍然可以當入口:為那批內容做的一次時效覆蓋掃描(51/54)確實指出了三個待查的小節,逐節看才知道兩個是換了句子開頭、一個是設計上不重複。當入口時標明它量的是措辭,別把命中數直接當覆蓋率。
  4. 飽和之後換量測對象、換到單項層,或明說這一維沒有錨。 三條路成本遞增。換量測對象最便宜:抽樣重判的不一致率、距上次複核超過某個期限的項目數,都還是聚合的數字,只是量的不再是「有沒有寫」。單項層更貴,但不必重判——該批內容的做法是給時效換一個外部查得到的對照物:每一則時效判定要點名一個具體的過時前提(辦公室隔間、以年為單位的瀑布式階段、Java 與 JMock 的範例版本),那個前提現在成不成立,讀者不必信任頁面就查得到。
  5. 補不出錨的維度要明說。 該批的角色歸屬這一維沒有錨,而且補不出來——角色由各主題自己界定,不對應任何頁面外的事實。寫下「這一維的維護狀態目前看不出來」,比擺一個飽和的覆蓋率誠實。

跟其他原則的關係

原則關係
#243 要求執行者做判定的規則要一併規定判定留下什麼痕跡直接的上游——#243 要求判定留痕跡,本卡處理下一步:痕跡的計數要怎麼設計才算是外部可核對的。#243 的痕跡若被計成關鍵詞出現次數,痕跡存在而指標讀不到
#264 規則用數量當門檻時先問那個數量在代理什麼判斷姊妹——#264 管「用數量當門檻」,本卡管「用數量當證據」。兩者的失效同源:數字取代了它本來要代理的那個判斷,而數字自己不知道自己在代理什麼
#221 檢查規則的作用域要顯式列舉同型的假訊號——#221 的零 error 可能是沒被檢查,本卡的滿覆蓋率可能是指標對變質沒有反應。兩者都是「看起來通過」,而通過的原因是量測範圍或量測對象不對
#149 keyword bank 命中是候選不是判決反向的同一件事——#149 說關鍵詞命中不等於違規(偽陽性),本卡說關鍵詞未命中不等於缺席(偽陰性)。兩張合起來的結論是關鍵詞只適合當偵測入口,不適合當任何數字的分子
#267 入口由違規形態寫不寫得成 pattern 決定,佔比只決定能不能原樣進清單同源的另一半——本卡講關鍵詞不能當覆蓋率的分子(漏數判定),#267 講它不能當違規涵蓋的全部入口(漏抓違規)。共同根因是關鍵詞量的是措辭
#122 Cadence 同質化是模板的隱形維度衝突的另一方——關鍵詞覆蓋率獎勵句型統一,而 #122 要求句型有變化。這兩條同時存在時,執行 #122 會讓指標下降,因此指標的量測方式要先讓路

判讀徵兆

訊號該做的事
指標的分子與分母來自不同的計數單位重新定義單位;分母要能被第三方用同一個規則重算
覆蓋率是 100%,而這一維仍被列為「有人在維護」問它在既有項目變質時會不會動;不會就換量測對象、換到單項層,或明說沒有錨
用 grep 得到的覆蓋率跟逐項檢查的結果不一致指標量的是措辭不是判定;改數結構單位
覆蓋率用關鍵詞計數,而同一批內容另有一條要求句型變化的規則兩者互相抵銷(句型一變,關鍵詞就掃不到),先換量測方式再談誰讓路
指標讀出來的缺口,恰好落在該項目最完整的那幾筆上這是量測對象錯了的訊號,不是內容缺了;逐項核對前不要據此補寫
某一維怎麼設計都找不到外部可核對的對象明說這一維沒有錨,並寫出它能做到的上限(例如把界定寫出來讓它可以被反駁)