論述基礎與限制

本卡的論述基於 1 個 casedart 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 流程在「規則三 / 規則四對齊」輪刻意檢查。