設計檢討用當下三軸論證、不依賴 hindsight
論述基礎與限制
本卡的論述基於 1 個 case(dart Stream 事故 的 review 過程)抽出來的假說、不是經過多個獨立 case 驗證的工程原則。具體限制:
- 「成本對稱性 / 可逆性 / 領域先驗」三軸框架借自 yagni 篇的判斷工具、不是 industry standard
- 「hindsight 偏向個人歸因 / 三軸偏向制度歸因」是觀察性分歧、不是必然對立——實際事故報告(aviation / medical incident)也常用 hindsight 同時歸因到制度
- 5 個多面向 case(code review / post-mortem / ADR / retrospective / 寫作)是假設性對比、未驗證真實案例中兩種論述的差異
讀者使用本卡時、把它當「這個 blog 提倡的判定方式」、不當「驗證過的工程原則」。
核心原則
本卡提倡的「設計缺陷」判定方式是「在當下成本對稱條件下選了限制更高的選項」、用以替代依賴結局的 hindsight 判定。
| 維度 | 事後諸葛論述(hindsight) | 當下三軸論證(current axes) |
|---|---|---|
| 視角 | 已知結局、回頭看 | 不知結局、看當下選擇 |
| 判斷依據 | 後來真的需要 X、所以當初該做 X | 當下三軸(成本對稱性 / 可逆性 / 領域先驗)都指向 X |
| 歸因方向 | 設計者個人沒預見 | 工具預設 + 領域知識制度 |
| 教讀者什麼 | 後見之明的具體規則 | 當下能用的判斷工具 |
| 泛化能力 | 弱(只在類似 case 適用) | 強(可套到新情境) |
兩種論述讀起來都像在分析「設計缺陷」、但結論不同、可教性也不同。事後諸葛論述需要結局發生、當下三軸論證在當下就能判斷——後者才是 portable 工具。
為什麼 hindsight 不適合做設計缺陷判定
Hindsight 預設了結局、不能在當下使用
事後諸葛論述「StreamController() 被當廣播用了一段時間沒事、加第二個 listener 立刻炸 → 設計缺陷」隱含一個前提:讀者知道後來加了第二個 listener。
但設計者在當下不知道結局。如果讀者用這個論述當判斷工具、他要等到結局發生才知道「自己做錯了」——這不是判斷工具、是事後檢討。
當下三軸論述「在零成本差異 + 強領域先驗下選了限制更高的選項 → 設計缺陷」不需要等結局——讀者拿到的是「現在面對這類選擇、跑三軸、就能判斷」的工具。
把判斷依據放在結局上、會誤判類似情境
「結局是『後來需要多訂閱』 → 當初該選 broadcast」這個推論、套到不同議題會推出反向結論:
| 議題 | 結局 | Hindsight 推論 | 當下三軸推論 |
|---|---|---|---|
| 事先沒做 plugin、後來真的需要 | 後來需要 plugin | 當初該建 → 過度設計 | 高成本差、屬 YAGNI 適用 → 當初不做是對的 |
| 用 single 預設、後來只有一個訂閱者 | 沒事 | 當初選 single 是對的 | 成本對稱、領域先驗強 → 當初仍該選 broadcast、留彈性 |
| 用 sync API、後來都是 async | 沒事或重構成本高 | 視結局而定 | 看當下三軸:簽章差異對稱、async 領域先驗強 |
Hindsight 把「結局」當判斷依據 → 跟「後來剛好不需要」的 case 推出衝突結論。三軸論述用「當下選項屬性」當判斷依據 → 不依賴結局、可一致應用。
個人歸因 vs 制度歸因(觀察性分歧、不是必然對立)
Hindsight 論述偏向個人歸因——「設計者沒預見」「沒多想」「沒考慮未來」——隱含「個人錯了」。但這不是 hindsight 的必然結果:成熟的事故報告(aviation post-mortem、medical incident report)即使用 hindsight 視角、也常透過 root cause analysis 歸因到系統 / 制度層級。
當下三軸論述把判斷依據放在外部變數(工具預設、領域常識),歸因自然偏向制度層——「工具預設選錯」「領域知識沒內化進團隊規範」。但寫作者仍可選擇用三軸論證後再歸因到個人(「三軸都指向 broadcast、但設計者選了 single」)。
兩種論述在歸因方向上有偏差、但不是排他關係。本卡的主張不是「hindsight = 個人歸因、三軸 = 制度歸因」、而是「三軸論證的歸因預設偏向制度、寫作者要明確選擇歸因層次」。
兩種歸因引導的修補方向不同:
- 個人歸因 → 「下次更仔細」(無法行動的結論)
- 制度歸因 → 「review checklist 加一條」「lint rule 警告」「架構規範禁掉」(可執行)
多面向:跨情境的同個結構(假設性對比)
下面 5 個面向是假設性對比——本卡用相同 framework 推到 5 個情境、但只有面向 5(技術文章寫作)有對應的真實 case(dart Stream review)。其他 4 個面向(code review / post-mortem / ADR / 個人 retro)是 framework 在不同情境的推導、未驗證真實案例中兩種論述風格的差異。讀者套用時、優先在「跟本卡 case 結構接近」的情境使用、其他面向先當「待驗證的擴張」。
面向 1:Code review / PR 審查
事後諸葛 review:「你這段 code 後來在 X 條件下會炸、所以當初該寫成 Y」
當下三軸 review:「在當下、Y 比 X 多打 N 個字元、領域先驗強烈指向 Y、可逆性中等偏高 → Y 是當下三軸對齊的選擇」
差別:reviewer 不需要等到 bug 發生、就能在 PR 階段給出判斷。Hindsight review 需要等 bug、其實是「事後追究」、不是「事前防護」。
面向 2:Post-mortem / 事故檢討
事後諸葛 PM:「Stream 的單訂閱限制在第二個訂閱者出現時暴露、所以是設計缺陷」
當下三軸 PM:「Stream 的單訂閱選擇在當下成本對稱條件下不必要地縮小未來空間、所以是設計缺陷」
差別:第二種論述讓「設計缺陷」這個判定不依賴「結局已發生」、讀者學到的是 portable 判斷工具。團隊裡其他成員看到這份 PM、能套用到沒有相同結局的新議題上。
面向 3:架構決策反思(ADR retrospective)
事後諸葛 retro:「我們選 SOAP、後來生態系全跑去 REST、所以 SOAP 是錯的」
當下三軸 retro:「選 SOAP 在當時的成本對稱性是 ⋯⋯、領域先驗是 ⋯⋯ → 在那個時間點屬於合理選擇 / 屬於設計缺陷」
差別:能區分「真的當時就該避開」vs「環境後來變了」。前者是設計缺陷、後者是健康的演化、兩者修補方向完全不同。
面向 4:個人 retrospective
事後諸葛自我檢討:「上次我沒考慮 X、結果出事 → 下次要考慮 X」
當下三軸自我檢討:「上次的選擇在當下三軸論述下對齊嗎?對齊 → 不算判斷力問題、是環境變化;不對齊 → 找出當時該抓到的訊號是什麼」
差別:避免不必要的自我責備、改善方向更精準。「下次要考慮 X」是 case-bound、套到下一個議題(不是 X)反而沒幫助;「下次跑三軸」是 portable。
面向 5:技術文章寫作
事後諸葛寫作:「[工具] 預設為 [限制版本]、被當 [通用版本] 用了一段時間沒事、後來出問題 → 設計缺陷」
當下三軸寫作:「[工具] 在當下零成本差異條件下選了限制更高的選項、領域先驗指向通用版本 → 設計缺陷」
差別:讀者學到 portable 判斷工具、不只是「這個 case 後來怎樣」的故事。文章的長期價值(被新讀者拿去用在新議題)大幅提升。
識別訊號:什麼時候你正在用 hindsight 論述
訊號 1:論述需要「後來」「最終」這類時序詞
「後來加了第二個訂閱者才炸」「最終發現 plugin 系統不需要」「事故爆發後才知道」——這些時序詞標記論述依賴結局。
修法:把時序詞拿掉、論述還站得住嗎?站不住 → 補當下軸的論證。
訊號 2:歸因落在個人能力 / 預見性
「沒預見到」「沒考慮到」「沒多想」「疏忽」——把判斷錯誤歸給個人能力。當下三軸論述會把歸因放到「工具 / 領域常識」這類外部變數。
修法:把「沒預見到」改成「在當下、工具預設指向 X、領域先驗指向 Y、選擇者選了 Z」——歸因從個人轉到外部結構。
訊號 3:結論無法 portable
「下次要考慮 X」——但「X」是這次的具體議題(多訂閱)、套到不同議題(plugin 系統)反而會推出反向結論。
修法:結論寫成「下次跑三軸」「下次跑 checklist」——是工具、不是 case-specific 規則。
訊號 4:讀者讀完只記得 case、記不住原則
讀者讀完事後諸葛論述、能複述 case、但問他「下次遇到不同議題怎麼判斷」答不出來——因為原則沒被抽出來、case-bound。
修法:在 case 之後補一段「抽象原則」、原則用語不綁定本 case 的具體名詞。
何時 hindsight 論述仍然合理
「設計檢討用當下三軸、不依賴 hindsight」這條原則在大多數工程檢討情境成立、但有合理例外:
| 情境 | 為什麼 hindsight 仍合理 |
|---|---|
| 真實事故 forensic 重現 | 目的是還原時序與因果鏈、結局視角不可避免(但 root cause 仍要用三軸) |
| 學術 case study | 教學重點是「結局如何揭露結構」、hindsight 是 narrative 風格選擇 |
| 個人日記 / 隨筆 | 不是檢討文件、是記錄當下感受、不需要嚴格判斷工具 |
| 純運氣 case(環境完全不可預測) | 例如外部 API 突然 deprecated、三軸無法事先抓 → 用 hindsight 描述合理 |
| 教學「為什麼這個結局重要」 | 重點是讓讀者理解結局的影響、不是教 portable 判斷工具 |
判讀:寫之前自問「這份檢討是要產出 portable 判斷工具嗎?」——是 → 嚴格用三軸;否 → hindsight narrative 可接受。
跟其他抽象層原則的關係
| 原則 | 跟本卡的關係 |
|---|---|
| #67 寫作便利度跟意圖對齊反相關 | hindsight 論述比三軸論述好寫(事後資料完整、結局明確)、但離 portable 意圖更遠 |
| YAGNI 三軸框架(成本對稱性 / 可逆性 / 領域先驗) | 本卡是三軸框架在「設計檢討寫作」面向的應用——把判斷工具寫進文章、而不是只寫結論 |
| Writing-articles 規則四:事後檢視看判讀品質 | 本卡是規則四的進階——不只看判讀品質、還要避免結局視角偏差汙染判讀記錄 |
| Hindsight bias(認知科學) | 本卡把認知偏誤具體化到工程文章寫作層面、給可執行修法 |
判讀徵兆
| 訊號 | 該做的行動 |
|---|---|
| 論述用「後來」「最終」「結果」當主語 | 改用「在當下、X 比 Y ⋯⋯」 |
| 歸因落在「沒預見」「沒考慮」「沒多想」 | 改成「工具預設選什麼 / 領域常識指向什麼 / 三軸怎麼跑」 |
| 結論是「下次要記得 X」 | 改成「下次跑三軸 / 跑 checklist」 |
| 讀者複述只能複述 case | 補一段「抽象原則 + 套用到不同 case」 |
| 文章在教「設計缺陷」但讀者要等到自己出事才懂 | 補當下三軸論證、讓讀者在事前就能用 |
核心原則:「設計缺陷」是當下成本對稱條件下選了限制更高的選項、不是事後發現需求變了。寫設計檢討時用三軸論證、避免讓判斷依賴於結局已發生——這樣讀者拿到的是 portable 判斷工具、不只是 case 故事。
Self-case:本卡的觸發來源
本卡的觸發是修 Dart StreamController:single-subscription vs broadcast 的事故實錄 時的反思。
文章 v1 寫「StreamController() 預設為單訂閱、被當廣播用了一段時間沒事、加第二個 listener 立刻炸」——典型 hindsight 論述。
讀者反問:「如果只有一個人用沒問題、後來才需要多訂閱、這算設計缺陷嗎?」——這個反問正確 catch 了 hindsight 論述的薄弱處(依賴結局、不依賴當下選擇)。
修補後改寫:「在當下零成本差異條件下選了限制更高的選項」+ 加四格表釐清「需求演化 vs 設計缺陷」的分界——不依賴結局、用三軸論述。讀者拿到 portable 判斷工具、不只是 case 故事。
對應本卡:寫設計檢討時、第一版常常用 hindsight(因為事故已發生、視角自然帶結局)——要刻意 review 改成當下三軸、才會產出 portable 工具。這個 review 不會自動發生、需要 multi-pass review 流程在「規則三 / 規則四對齊」輪刻意檢查。