我做了一套自己學 Redis 用的 repo,34 支可跑的範例加 47 篇筆記。內容技術上沒有錯,每一支都跑得起來。
然後我在第 17、19、25 支範例前面一路卡住。
卡的是我自己寫的東西。這件事很難堪,但它把一個問題攤得非常乾淨:如果連寫的人都讀不下去,那問題一定不在讀者的努力程度。
表面上卡的點都不一樣,根因只有一個
19 是 consumer group 看不懂,25 是錯誤處理的案例沒有感覺,17 又是另一回事。看起來是三個不同的難點。
但我後來意識到它們共用同一個根因——範例「太資料」了。
我對著 demo:x、job-1、key1 這種東西做操作。技術上這是最乾淨的示範方式,沒有任何無關的雜訊。問題是:沒有用過 Redis 的人,對這種 key 完全沒有著力點。
你看得懂每一行在做什麼,但看不出來「為什麼要這樣做」「實務上這是在幹嘛」。指令的語法你學會了,指令存在的理由你完全沒接觸到。
當時我自己的原話是:「因為給的範例就太資料了完全沒有接近性。」
掛載點
我後來想通的是這件事:學習卡住,多數時候不是機制難,是新知識沒有地方可以掛。
真實情境會自己長出掛載點。你講「訂單付款後要扣庫存」,讀者腦子裡本來就有一個訂單的形狀,新概念可以掛在那個形狀上。你講「對 demo:x 做 DECR」,讀者要先自己憑空想像一個場景,才能開始理解——你把想像力的成本外包給讀者了,而那正是他最缺的東西,因為他還不熟這個領域。
抽象範例對已經懂的人是乾淨,對還不懂的人是空白。
我做的三件事
想通之後我把整套改寫,不是修幾支,是 34 支全部。
第一件:統一情境世界。 所有範例圍繞同一間電商——shop:product、shop:stock、shop:order、shop:cart、shop:session。欄位用真實欄位名(orderId、amount),不用 field1。
統一比「每支各挑一個情境」好得多,而且好在一個我沒預料到的地方:概念之間的連結會自己長出來。分散式鎖保護的就是剛才那筆出貨、cache stampede 打爆的就是剛才那個商品頁、掉單回收撿的就是前面那個 worker 沒做完的事。這些關聯我一個字都不用解釋,讀者自己會接起來。
改完之後的對照很直接:17 改成「訂單未付款自動取消」、19 改成「訂單進來、多台出貨 worker 分食」——同一個人(我)立刻看懂了同一個機制。機制一個字都沒改。
第二件:進階範例加路標。 25 到 33 那批比較硬的,每支開頭放一行〔上手〕點明「這支核心只要帶走哪一句」,比較少見或太深的段落標上 ★「掃過就好」。
這件事的效果是把「一面牆」變成「一條路加上岔路標示」。牆會讓人退,路不會——就算岔路你不走,你知道自己還在主線上。
第三件:輸出要是給人看的形狀。 Redis 回傳的是扁平陣列,我加了一層 tidy() 轉成 {id, orderId, amount} 再印。工作名印名字不印時間戳 id。
這件事很小,小到不值得寫進教材裡。但它決定了讀者是在讀「一筆訂單」還是在讀「一串字元」。
例外,以及例外要怎麼處理
有些範例天生就該是抽象的:示範 race condition、示範 pipeline 的差別、示範編碼方式。這些的重點就是機制本身,硬套電商情境反而多一層雜訊。
這種我保留抽象,但在輸出或註解裡框回真實對應——加一句「這對應到真實世界的什麼」。抽象可以留,但不能讓它懸空。
這條規則我現在拿來檢查所有教學內容
三個問題:
- 這筆資料有沒有名字? 不准
foo、bar、demo:x - 這支的核心一句是什麼,標了沒有?
- 可以跳過的進階段落標了沒有?
第一條是最硬的那條。我現在寫任何範例之前會先問自己「這筆資料在真實世界是什麼東西」——答不出來,代表這個範例我自己也沒想清楚要教什麼。
順帶一提,這條規則後來也回頭改變了我寫技術文章的方式:這個 blog 的快取系列整批都沿用同一個電商情境承載概念,理由跟上面完全一樣。
有一個我沒有量化驗證的部分要老實說:「加了上手路標會降低進階內容的牆感」這件事,目前只有我自己的主觀體感,沒有數據。前面講的統一情境世界那部分有明確的前後對照,這部分沒有。
相關
- 找題目也是同一回事——真實脈絡勝過憑空想 → 挖自己的 code 找內容缺口