論述基礎與限制

本卡的論述基於 1 個 casedart Stream 事故的 review 中讀者指出「事件 payload 第二段」依賴 code 看過)抽出來的觀察。具體限制:

  • Scope 限縮在概念說明 / 架構決策 / 設計檢討類文章:教學類、tutorial 類、code walkthrough 類文章的讀者本來就會逐行對映 code、本卡不適用——這幾類文章的論述跟 code 緊密交織是合理 narrative
  • 「翻譯成業務角色」也有讀者熟悉度的邊界:用「鏡像訂閱者」「狀態變更 service」這類角色名詞替代「那個 controller」、對熟悉 POS 跟 pub-sub 的讀者通順、對不熟的讀者仍是空名詞。修法是「換 reference 類型」、不是「徹底解決 self-contained」
  • 跟規則二(商業邏輯先於 CASE)的關係要精準:規則二講「層次順序」(先商業邏輯後 CASE)、本卡講「論述自包含」(不依賴 code reference)。兩者有重疊但不完全相同——本卡是規則二在「字句層 reference 處理」的子場景、不是規則二的全面延伸

讀者使用本卡時、先判斷文章類型——概念說明 / 架構決策 / 設計檢討 → 套用;教學 / tutorial → 評估「跟 code 交織」是否合理 narrative。


核心原則

概念說明 / 架構決策 / 設計檢討類文章的論述段(不放 code 的段落)要 self-contained——用名詞 / 角色 / 條件描述業務邏輯、不依賴讀者去翻附近的 code block。讀者跳過所有 code block 仍能理解論述、是「商業邏輯先於 CASE」(compositional-writing 規則二)在「字句層 reference 處理」的子場景應用。

維度依賴 code 的論述Self-contained 論述
引用方式「事件 payload 第二段帶了那個欄位」「事件 payload 包含『當前完整列表』+『最後變動品項』兩段」
主詞「那個 controller」「剛才的 service」「副螢幕鏡像 controller」「狀態變更 service」
讀者前提已經看過 code、記得結構不需要看 code、只看論述
失敗模式讀者翻不到 reference 對應位置 → 卡住或誤讀讀者直接 parse 論述、不被 code 結構綁住
修補擴散code 改了、論述自動 outdatedcode 改了、論述仍然有效

self-contained 論述的價值在於論述本身就是完整的、code 是 case 補充而非依賴。讀者用論述就能 reproduce 思考過程、code 提供具體驗證。


為什麼依賴 code 的論述會出現

來源 1:寫的人有 code 在腦中、預設讀者也有

寫作者通常已經看過或寫過 code、所以在論述段用「那個 payload 第二段」這類 reference 對自己沒負擔。但讀者可能:

  • 跳過 code 直接讀論述(多數讀者習慣)
  • 看了 code 但沒記住具體結構
  • 不熟 Dart / 該專案、看 code 也 parse 不出 payload 結構

依賴 code 的論述把這些讀者擋在門外、強迫他們翻 code 對映、認知負擔顯著上升。

來源 2:把對話風格搬進文章

「現在只要有人訂閱、把它記錄下來、UI 就能用」——這是對話風格、預設聽者跟說話者共享 context。寫成文章時要把 context 補進去:「需要新增一個訂閱者讀這段資訊、再把它對應到 UI 上的視覺標記」。

來源 3:論述跟 code 段過於緊密交織

文章在寫「先給 code、然後論述、然後再給 code」的交織結構時、論述容易自然 reference 上面 code 的具體行。讀者跳過 code 就斷掉。

來源 4:誤把「業務邏輯」當成「code 行為」

「業務邏輯」是「為什麼這件事存在 / 服務什麼需求」、「code 行為」是「具體怎麼跑」。依賴 code 的論述把兩者混在一起、讀者難以分離兩個層次。


識別訊號:什麼時候你寫了依賴 code 的論述

訊號 1:論述用「那個」「這個」「剛才的」「上面的」當主詞

「那個 service」「這個 payload」「剛才的 controller」——這類代詞依賴讀者剛才看過 code。

修法:把代詞換成具體名詞 + 角色描述(「狀態變更 service」「事件 payload」「副螢幕鏡像 controller」)。

訊號 2:用 code 結構描述(「第二段」「那個欄位」)

「payload 第二段」「那個 nullable 欄位」「上面 method 的 return value」——這類描述依賴讀者看過 code 的具體結構。

修法:把 code 結構描述翻譯成業務角色描述。「payload 第二段」→「最後變動品項欄位(給需要追蹤單筆變動的訂閱者用)」。

訊號 3:時序連接詞依賴 code 順序

「先…然後…接著…」如果這個時序對應上面 code 的執行順序、論述跟 code 綁太緊。

修法:把時序敘述為「在 X 條件下、Y 動作觸發 Z 結果」、不依賴 code 的具體順序。

訊號 4:論述只有「就好」「就能」「就行」

「現在只要有人訂閱、UI 就能用」「修改一行就好」——這類「就」字句在依賴 code 補足背景條件時有問題。但「修一行就好」如果背景條件已在前文說明、用「就」表達精簡是合理的——區分標準是「沒有 code 也讀得通嗎」、不是字面有「就」就違規。

修法:背景條件不在前文時、把省略的條件補回去;背景條件已在前文時、保留「就」沒問題。「在訂閱端讀取這段資訊、加一個視覺標記的綁定、即可在 UI 上呈現」。

訊號 5:跳過 code block 後段落讀不通

最直接的測試方法:把所有 code block 拿掉、再讀一次論述、看是否仍能理解。

讀不通 → 論述依賴 code、要修補。


修法:把 reference 翻成 self-contained 描述

修法 1:用名詞替代代詞

修補前:

「那個 service 對外發送事件、payload 第二段帶了這次變動是哪筆。」

修補後:

「狀態變更 service 對外發送的事件 payload 包含兩段:當前完整商品列表、最後變動的具體品項。第二段是『最後變動品項』。」

修法 2:用角色替代「位置」

修補前:

「上面 code 第三行那個 listener 拿到的是 nullable。」

修補後:

「副螢幕的訂閱者收到的事件 payload 中、第二段(最後變動品項)可能為 null(移除或清空操作的情境)。」

修法 3:用條件替代時序

修補前:

「先建立 controller、然後 listen、接著 add 事件、再 cancel。」

修補後:

「在 controller 建立後、訂閱者可呼叫 .listen() 註冊;註冊完成後、controller 才能 .add() 事件給訂閱者;訂閱者呼叫 .cancel() 解除註冊後、後續 .listen() 對 single-subscription 仍然違反契約。」

修法 4:把「就好」展開成具體條件

修補前:

「現在只要有人訂閱、把它記錄下來、UI 就能用。」

修補後:

「新需求只要新增一個訂閱者讀這段資訊、再把它對應到 UI 上的視覺標記即可——介面不需要變動、payload 結構不需要調整、實作範圍只限於新增訂閱端。」


何時論述可以依賴 code

「論述要 self-contained」這條原則在概念說明 / 架構決策 / 設計檢討類文章成立、但有合理例外:

情境為什麼可以依賴 code
Code walkthrough / line-by-line文章本身就是 code 解說、讀者預設會逐行對映
簡短 inline 引用 specific 行為stream.listen() 第一個參數是 callback」這類引用本身就是 code 字面
Tutorial 教學步驟「跑這個指令、你會看到 X 輸出、接著做 Y」是 hands-on 教學風格
Code review 評論評論本身就是針對某行 code、上下文是 inline 共享的

判讀:寫之前自問「我的文章是『教讀者怎麼讀這份 code』還是『教讀者一個概念 / 框架』?」——前者可依賴 code、後者要 self-contained。


跟其他抽象層原則的關係

原則跟本卡的關係
Compositional-writing 規則二:商業邏輯先於 CASE本卡是規則二在「字句層 reference 處理」的子場景——規則二講層次順序、本卡講論述不依賴 code reference。兩者有重疊但不完全相同
#67 寫作便利度跟意圖對齊反相關用「那個 payload」是寫作便利、self-contained 論述需要刻意翻譯——同骨展現
#111 口語化修辭會稀釋技術精度「就好」「就能」這類字句既是口語也是依賴 code、本卡跟 #111 在這層重疊
#110 設計檢討用當下三軸論證、不依賴 hindsight三卡都是「論述要能讓讀者拿來用」的不同層次——當下視角 / 精度 / self-contained

判讀徵兆

訊號該做的行動
論述用「那個 / 這個 / 剛才」當主詞換成具體名詞 + 角色描述
論述提到「第 X 段 / 第 Y 行 / 那個欄位」翻譯成業務角色描述
段落用「就好 / 就能 / 就行」結尾把省略的條件補回去
把 code block 拿掉後論述讀不通整段重寫成 self-contained
寫的時候有 code 在記憶中、覺得「不用解釋」預設讀者跳過 code、強迫自己用文字描述

核心原則:技術文章的論述要 self-contained——讀者跳過所有 code block 仍能理解論述、是「商業邏輯先於 CASE」的延伸實踐。寫完後跑一次「拿掉 code block 還讀得通嗎」自測、讀不通 → 翻譯成 self-contained 描述。


Self-case:本卡的觸發來源

本卡的觸發是修 Dart StreamController:single-subscription vs broadcast 的事故實錄 時、讀者指出某段論述「重點混淆 + 過於口語 + 預設讀者看過原始碼」。

問題段(修補前):

「技術上這需求很乾淨:service 早就在事件 payload 第二段帶了『這次變動是哪筆』,現在只要有人訂閱、把它記錄下來,UI 就能用。於是收銀主畫面的 controller 加了第二個訂閱。」

問題分析:

  • 「事件 payload 第二段」依賴讀者看過 code 結構
  • 「就能用」「就好」省略了具體條件
  • 「於是 X 加了 Y」對話風格、不是論述風格

修補後:

「這個需求剛好對應 service 已經備妥但尚未被消費的資訊——service 對外的事件 payload 從原始設計就分兩段:一段是『當前完整的商品列表』、另一段是『這次變動的具體品項』。第二段是當初為『需要追蹤單筆變動的訂閱者』預留的擴充欄位、過去幾個月一直沒被消費。新需求只要新增一個訂閱者讀這段資訊、再把它對應到 UI 上的視覺標記即可——介面不需要變動、payload 結構不需要調整、實作範圍只限於新增訂閱端。」

修補後的論述用「角色描述(『需要追蹤單筆變動的訂閱者』)」替代了「位置描述(『第二段』)」、補回了省略的條件(「介面不需要變動、payload 結構不需要調整、實作範圍只限於新增訂閱端」)。讀者即使跳過所有 code block 也能理解這段論述。

對應本卡:論述依賴 code 是「寫的人有 code 在腦中」的便利選擇——multi-pass review 要在輪 5「反例 / 邊界」加掃 self-contained frame、用「拿掉 code 還讀得通嗎」自測