論述基礎與限制

來自一個分類的十一篇實作(SQL 語言層教材)加使用者在過程中提出的判斷標準。這是一次個案,機制的推導比量測強。 沒有對照組——手上沒有一批「從複雜需求出發」寫成的同題材內容可以並排比,所以下面關於推導起點那一半是設計論證,不是實驗結果。

深度分配那一半有兩組資料:兩則陷阱的舉例,以及一次篇幅對照。樣本是一批十一篇,不足以推廣成比例。

核心原則

熟悉度決定作者覺得一件事要不要解釋、要不要當成推導的起點。 這裡的熟悉度指的是作者對「這一段正在寫的那件事」有多自動,而不是他的整體資歷。 對基本功能,作者的熟悉度最高,所以它同時失去這兩個待遇——既沒有被展開,也沒有被當成起點。

讀者的處境相反。基本處正是他手上既有結構最少的地方,能自行補完的最少;而越往進階走,他手上可用來掛新知識的東西越多。兩邊的需求剛好反向,所以照自己的熟悉度分配,分出來的深度就與讀者的需求反向。

症狀一:基本的被帶過,而陷阱聚集在那裡

一批十一篇的教材裡,最基礎的那幾個題目量出了全批最危險的缺陷,而它們都不報錯。

第一個是子句的分工。把描述單一列的條件放進篩選分組的子句,一個引擎拒絕、另一個接受並回出一份錯的清單——輸出每組一列、欄位齊全、編號都是實際存在的資料,沒有任何一處會讓人停下來檢查。

第二個是識別字的大小寫。一個引擎把沒加引號的名字摺成小寫,另外兩個完全不區分大小寫——在後者上開發、名字大小寫混著寫,一路都正常,搬到前者之後凡是建表時加了引號而查詢時沒加的地方全部找不到表。錯誤出現的時機離寫下它的時機很遠。

陷阱聚集在基本處有結構上的原因,不是巧合。基本語法用得最頻繁,所以同一個誤解會在每一個查詢裡重複一次;而它們的失效形態多半是靜默的,因為引擎為了相容性接受寬鬆的寫法,而寬鬆的那一版通常還能算出某個看起來合理的值。

進階功能在這一批裡的形態相反:用得少,所以同一個誤解不會重複那麼多次。「難」與「危險」是兩個不同的軸,而教材的深度分配常常照前者配。 這個對比只在這一批成立——進階功能同樣有靜默失敗的形態(視窗函數的預設框架、時區的隱式轉換),本卡沒有量過它們,所以站得住的是頻率那一半,不是「進階比較嚴格」。

深度那一側有一個現成可算的量,而它在改稿過程中會移動——這件事本身是證據的一部分。

初次記錄時,那批十一篇裡最短的三篇分別是「表的一次出現」「連接的左運算元」與「宣告式與三種順序」,而第三篇正是整個模組的推導源頭。最基本的那幾篇拿到最少的篇幅,本卡的判讀徵兆在它自己的來源上成立。

審查之後那個排名變了。審查指出源頭那一篇的核心示範缺一個前提(少了它讀者會看到相反的結果),補完之後它離開了最短的那一群。篇幅與基本度的對應會被審查修掉,而修掉它的前提是有人先量過。

而最短的那幾篇至今仍然是最基本的那幾個——連接的左運算元、識別字、表的一次出現。徵兆在這個來源上只被修掉一處,其餘還在。這是一次個案的行數對照,不是比例關係的證據。

症狀二:進階議題從複雜需求出發

第二種形態出現在模組的規劃階段。一個進階主題要開講的時候,最順手的起點是一個複雜的實際需求——因為那正是讓人想寫這個主題的原因。

從複雜需求出發寫出來的是主題集合:每一篇解一個需求,判斷標準各自成立,而彼此折算不回去。讀者拿到的是一組互不通約的規則,換一個沒被寫過的情境就無從推導。

從基本功能出發則得到推導體系。本次那個分類的源頭是一句關於語言性質的基本陳述——它是宣告式的,所以寫下的只是要什麼結果。

折算對回的是同一個性質,不是同一句斷言。 那個分類的十一篇分成兩支:一支問「結果集由什麼決定」(答案是一個與直覺不同的模型),另一支問「文字之外還有誰在決定」(答案是引擎的名字規則、寬鬆度、權限與最佳化器)。兩支的答案方向相反,而它們問的是同一個性質的兩面。 初稿把兩支寫成同一句斷言的展開,審查指出那讓兩支互為否定——這是選對了源頭而描述錯了它的形狀。

判斷起點合不合格有一個機械的測試:這個起點能不能在不預設本模組任何內容的前提下用一句話說完。 複雜需求說不完,因為它自己就需要先鋪陳。

修法

深度的訊號換掉。 不用語法或主題的表面複雜度判斷該寫多詳細,改問「讀者讀到這裡手上有什麼」。基本章節要走一遍資料、把每一步的中間狀態逐項列出來,講完之後換一個角度重組一次——同一個判斷再給一句可以機械執行的問法。

進階議題往回找基本機制當起點。 想寫的那個複雜需求留著當案例,但推導從它底下的基本功能開始。順序是先寫清楚那個基本功能在做什麼,複雜需求成為它的一個推論。

模組的源頭用上面那個一句話測試驗收。 說不完就代表選到的是需求而不是機制。

這些判定的輸入

上面每一條修法的最省力遵循方式都一樣:在腦中判一次,然後宣告通過。而那個判定認真做過與完全沒做,產物完全相同——判定又由知道答案的作者執行,所以「我判過了」不構成證據。每一條判定要留下它的輸入:

判定要留下的輸入
「讀者讀到這裡手上有什麼」列出來:這一段之前,讀者從哪幾篇拿到哪幾個概念。列不出來就是還沒判
源頭的一句話測試那句話交給沒讀過本模組的探針,問它「這句話裡有哪些詞你不知道指什麼」。作者自己判不了,因為他知道答案
「這一章是功能型所以維持原樣」寫出這一章沒有並列兩種以上可替代的寫法。有並列就是選擇型,不論它同時是不是在解釋機制
「問題用讀者的語言寫」問題句要有出處(實際的提問記錄),且句子裡不出現任何章節名裡的名詞。後者可以 grep
「路由表已經補了」每一章至少被一個問題指到。這一條可以數
篇幅與基本度的對照排名回升時要說明補進去的是走過資料的段落,不是任何字數

判定本身不是產物,判定用的材料才是。 沒有這些輸入的時候,最省力的路徑會沿著零後續動作的那個結論走——判成不適用、判成不需要補、判成功能型——因為那個結論不產出任何東西可供檢查。

為什麼審查抓不到

被帶過的那一段沒有錯,它只是短。審查問的是「這一段寫得對不對」,而一段正確而簡略的基本定義在每一個維度上都通過——字句合規、事實正確、結構完整。

而同源的 reviewer 與作者共享同一份熟悉度:讀到那句簡略的定義時,兩邊腦中都自動補上了完整版,於是兩邊都覺得夠了。要偵測它得換一種讀者——手上沒有那份熟悉度、也不會自動補完的那種。

跟其他原則的關係

  • #219 教學模組要有推導源頭、不是主題集合:那張卡要求模組要有一個源頭,本卡限定源頭的性質——它必須是基本機制,而不是任何一個複雜需求。兩張卡合起來才完整:有源頭而源頭選錯,模組仍然折算不起來。
  • #191 讀者是缺經驗的專業人士、不是外行人:這張卡最容易被誤讀成「所以基本的可以帶過」,而本卡明確擋掉這個推論。 那張卡管的是姿態(不要用科普語氣、不要故事線導入),不是份量。讀者缺的正是這個領域的基本結構,所以基本定義要寫足——用中性的敘述寫足,不是用科普的方式寫多。
  • #262 內容超出容器時擴充結構、不壓縮內容:同樣導向擴充而非壓縮,而觸發條件不同。那裡的壓力來自容器(判斷標準裝不進表格格),這裡的壓力來自熟悉度(作者覺得這太基本了不用寫)。前者作者看得見,後者看不見。
  • #303 串連佔掉的是本篇的篇幅:那張卡的「承重術語在自己的主場最容易沒有定義」與本卡是同一個機制的兩個對象——那裡是一個詞,這裡是一整個主題。共同的成因都是越熟悉的東西陌生感消失得越早,所以停下來解釋它的那一刻從來不會到。
  • #306 按功能組織的教材裡「該用哪一個」沒有住址:同一個成因的兩個缺席——本卡缺的是深度(基本的被帶過),那張卡缺的是入口(選擇沒有住址)。共同來源是作者手上有讀者沒有的東西:這裡是熟悉度,那裡是功能與問題的對照表。

判讀徵兆

  • 模組裡最短的那幾篇,主題剛好是最基本的那幾個。
  • 一個進階章節的開場是一段情境鋪陳,而那段鋪陳本身需要三四句才講得完。
  • 模組入口說不出一句「各篇的判斷標準都折算回這裡」的話,或者說得出而那句話裡包含只有本模組定義過的名詞。
  • 教材裡關於基本語法的段落全部是規則的陳述,沒有一段走過一次實際的資料。
  • 讀者的提問集中在基本處,而教材在那裡的篇幅最少。