<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>文件設計 on Tarragon</title><link>https://tarrragon.github.io/blog/tags/%E6%96%87%E4%BB%B6%E8%A8%AD%E8%A8%88/</link><description>Recent content in 文件設計 on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Wed, 06 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/%E6%96%87%E4%BB%B6%E8%A8%AD%E8%A8%88/index.xml" rel="self" type="application/rss+xml"/><item><title>商業邏輯論述要 self-contained：不依賴 code 才能被理解</title><link>https://tarrragon.github.io/blog/report/prose-self-contained-without-code-reference/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/report/prose-self-contained-without-code-reference/</guid><description>&lt;h2 id="論述基礎與限制">論述基礎與限制&lt;/h2>
&lt;p>本卡的論述基於 &lt;strong>1 個 case&lt;/strong>（&lt;a href="../../work-log/dart_stream_controller_single_vs_broadcast/">dart Stream 事故的 review&lt;/a> 中讀者指出「事件 payload 第二段」依賴 code 看過）抽出來的觀察。具體限制：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Scope 限縮在概念說明 / 架構決策 / 設計檢討類文章&lt;/strong>：教學類、tutorial 類、code walkthrough 類文章的讀者本來就會逐行對映 code、本卡不適用——這幾類文章的論述跟 code 緊密交織是合理 narrative&lt;/li>
&lt;li>&lt;strong>「翻譯成業務角色」也有讀者熟悉度的邊界&lt;/strong>：用「鏡像訂閱者」「狀態變更 service」這類角色名詞替代「那個 controller」、對熟悉 POS 跟 pub-sub 的讀者通順、對不熟的讀者仍是空名詞。修法是「換 reference 類型」、不是「徹底解決 self-contained」&lt;/li>
&lt;li>&lt;strong>跟規則二（商業邏輯先於 CASE）的關係要精準&lt;/strong>：規則二講「層次順序」（先商業邏輯後 CASE）、本卡講「論述自包含」（不依賴 code reference）。兩者有重疊但不完全相同——本卡是規則二在「字句層 reference 處理」的子場景、不是規則二的全面延伸&lt;/li>
&lt;/ul>
&lt;p>讀者使用本卡時、先判斷文章類型——概念說明 / 架構決策 / 設計檢討 → 套用；教學 / tutorial → 評估「跟 code 交織」是否合理 narrative。&lt;/p>
&lt;hr>
&lt;h2 id="核心原則">核心原則&lt;/h2>
&lt;p>概念說明 / 架構決策 / 設計檢討類文章的論述段（不放 code 的段落）要 &lt;strong>self-contained&lt;/strong>——用名詞 / 角色 / 條件描述業務邏輯、不依賴讀者去翻附近的 code block。讀者跳過所有 code block 仍能理解論述、是「商業邏輯先於 CASE」（compositional-writing 規則二）在「字句層 reference 處理」的子場景應用。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>維度&lt;/th>
 &lt;th>依賴 code 的論述&lt;/th>
 &lt;th>Self-contained 論述&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>引用方式&lt;/td>
 &lt;td>「事件 payload 第二段帶了那個欄位」&lt;/td>
 &lt;td>「事件 payload 包含『當前完整列表』+『最後變動品項』兩段」&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>主詞&lt;/td>
 &lt;td>「那個 controller」「剛才的 service」&lt;/td>
 &lt;td>「副螢幕鏡像 controller」「狀態變更 service」&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>讀者前提&lt;/td>
 &lt;td>已經看過 code、記得結構&lt;/td>
 &lt;td>不需要看 code、只看論述&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>失敗模式&lt;/td>
 &lt;td>讀者翻不到 reference 對應位置 → 卡住或誤讀&lt;/td>
 &lt;td>讀者直接 parse 論述、不被 code 結構綁住&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>修補擴散&lt;/td>
 &lt;td>code 改了、論述自動 outdated&lt;/td>
 &lt;td>code 改了、論述仍然有效&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>self-contained 論述的價值在於&lt;strong>論述本身就是完整的、code 是 case 補充而非依賴&lt;/strong>。讀者用論述就能 reproduce 思考過程、code 提供具體驗證。&lt;/p>
&lt;hr>
&lt;h2 id="為什麼依賴-code-的論述會出現">為什麼依賴 code 的論述會出現&lt;/h2>
&lt;h3 id="來源-1寫的人有-code-在腦中預設讀者也有">來源 1：寫的人有 code 在腦中、預設讀者也有&lt;/h3>
&lt;p>寫作者通常已經看過或寫過 code、所以在論述段用「那個 payload 第二段」這類 reference 對自己沒負擔。但讀者可能：&lt;/p>
&lt;ul>
&lt;li>跳過 code 直接讀論述（多數讀者習慣）&lt;/li>
&lt;li>看了 code 但沒記住具體結構&lt;/li>
&lt;li>不熟 Dart / 該專案、看 code 也 parse 不出 payload 結構&lt;/li>
&lt;/ul>
&lt;p>依賴 code 的論述把這些讀者擋在門外、強迫他們翻 code 對映、認知負擔顯著上升。&lt;/p>
&lt;h3 id="來源-2把對話風格搬進文章">來源 2：把對話風格搬進文章&lt;/h3>
&lt;p>「現在只要有人訂閱、把它記錄下來、UI 就能用」——這是對話風格、預設聽者跟說話者共享 context。寫成文章時要把 context 補進去：「需要新增一個訂閱者讀這段資訊、再把它對應到 UI 上的視覺標記」。&lt;/p>
&lt;h3 id="來源-3論述跟-code-段過於緊密交織">來源 3：論述跟 code 段過於緊密交織&lt;/h3>
&lt;p>文章在寫「先給 code、然後論述、然後再給 code」的交織結構時、論述容易自然 reference 上面 code 的具體行。讀者跳過 code 就斷掉。&lt;/p>
&lt;h3 id="來源-4誤把業務邏輯當成code-行為">來源 4：誤把「業務邏輯」當成「code 行為」&lt;/h3>
&lt;p>「業務邏輯」是「為什麼這件事存在 / 服務什麼需求」、「code 行為」是「具體怎麼跑」。依賴 code 的論述把兩者混在一起、讀者難以分離兩個層次。&lt;/p>
&lt;hr>
&lt;h2 id="識別訊號什麼時候你寫了依賴-code-的論述">識別訊號：什麼時候你寫了依賴 code 的論述&lt;/h2>
&lt;h3 id="訊號-1論述用那個這個剛才的上面的當主詞">訊號 1：論述用「那個」「這個」「剛才的」「上面的」當主詞&lt;/h3>
&lt;p>「那個 service」「這個 payload」「剛才的 controller」——這類代詞依賴讀者剛才看過 code。&lt;/p></description><content:encoded><![CDATA[<h2 id="論述基礎與限制">論述基礎與限制</h2>
<p>本卡的論述基於 <strong>1 個 case</strong>（<a href="../../work-log/dart_stream_controller_single_vs_broadcast/">dart Stream 事故的 review</a> 中讀者指出「事件 payload 第二段」依賴 code 看過）抽出來的觀察。具體限制：</p>
<ul>
<li><strong>Scope 限縮在概念說明 / 架構決策 / 設計檢討類文章</strong>：教學類、tutorial 類、code walkthrough 類文章的讀者本來就會逐行對映 code、本卡不適用——這幾類文章的論述跟 code 緊密交織是合理 narrative</li>
<li><strong>「翻譯成業務角色」也有讀者熟悉度的邊界</strong>：用「鏡像訂閱者」「狀態變更 service」這類角色名詞替代「那個 controller」、對熟悉 POS 跟 pub-sub 的讀者通順、對不熟的讀者仍是空名詞。修法是「換 reference 類型」、不是「徹底解決 self-contained」</li>
<li><strong>跟規則二（商業邏輯先於 CASE）的關係要精準</strong>：規則二講「層次順序」（先商業邏輯後 CASE）、本卡講「論述自包含」（不依賴 code reference）。兩者有重疊但不完全相同——本卡是規則二在「字句層 reference 處理」的子場景、不是規則二的全面延伸</li>
</ul>
<p>讀者使用本卡時、先判斷文章類型——概念說明 / 架構決策 / 設計檢討 → 套用；教學 / tutorial → 評估「跟 code 交織」是否合理 narrative。</p>
<hr>
<h2 id="核心原則">核心原則</h2>
<p>概念說明 / 架構決策 / 設計檢討類文章的論述段（不放 code 的段落）要 <strong>self-contained</strong>——用名詞 / 角色 / 條件描述業務邏輯、不依賴讀者去翻附近的 code block。讀者跳過所有 code block 仍能理解論述、是「商業邏輯先於 CASE」（compositional-writing 規則二）在「字句層 reference 處理」的子場景應用。</p>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>依賴 code 的論述</th>
          <th>Self-contained 論述</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>引用方式</td>
          <td>「事件 payload 第二段帶了那個欄位」</td>
          <td>「事件 payload 包含『當前完整列表』+『最後變動品項』兩段」</td>
      </tr>
      <tr>
          <td>主詞</td>
          <td>「那個 controller」「剛才的 service」</td>
          <td>「副螢幕鏡像 controller」「狀態變更 service」</td>
      </tr>
      <tr>
          <td>讀者前提</td>
          <td>已經看過 code、記得結構</td>
          <td>不需要看 code、只看論述</td>
      </tr>
      <tr>
          <td>失敗模式</td>
          <td>讀者翻不到 reference 對應位置 → 卡住或誤讀</td>
          <td>讀者直接 parse 論述、不被 code 結構綁住</td>
      </tr>
      <tr>
          <td>修補擴散</td>
          <td>code 改了、論述自動 outdated</td>
          <td>code 改了、論述仍然有效</td>
      </tr>
  </tbody>
</table>
<p>self-contained 論述的價值在於<strong>論述本身就是完整的、code 是 case 補充而非依賴</strong>。讀者用論述就能 reproduce 思考過程、code 提供具體驗證。</p>
<hr>
<h2 id="為什麼依賴-code-的論述會出現">為什麼依賴 code 的論述會出現</h2>
<h3 id="來源-1寫的人有-code-在腦中預設讀者也有">來源 1：寫的人有 code 在腦中、預設讀者也有</h3>
<p>寫作者通常已經看過或寫過 code、所以在論述段用「那個 payload 第二段」這類 reference 對自己沒負擔。但讀者可能：</p>
<ul>
<li>跳過 code 直接讀論述（多數讀者習慣）</li>
<li>看了 code 但沒記住具體結構</li>
<li>不熟 Dart / 該專案、看 code 也 parse 不出 payload 結構</li>
</ul>
<p>依賴 code 的論述把這些讀者擋在門外、強迫他們翻 code 對映、認知負擔顯著上升。</p>
<h3 id="來源-2把對話風格搬進文章">來源 2：把對話風格搬進文章</h3>
<p>「現在只要有人訂閱、把它記錄下來、UI 就能用」——這是對話風格、預設聽者跟說話者共享 context。寫成文章時要把 context 補進去：「需要新增一個訂閱者讀這段資訊、再把它對應到 UI 上的視覺標記」。</p>
<h3 id="來源-3論述跟-code-段過於緊密交織">來源 3：論述跟 code 段過於緊密交織</h3>
<p>文章在寫「先給 code、然後論述、然後再給 code」的交織結構時、論述容易自然 reference 上面 code 的具體行。讀者跳過 code 就斷掉。</p>
<h3 id="來源-4誤把業務邏輯當成code-行為">來源 4：誤把「業務邏輯」當成「code 行為」</h3>
<p>「業務邏輯」是「為什麼這件事存在 / 服務什麼需求」、「code 行為」是「具體怎麼跑」。依賴 code 的論述把兩者混在一起、讀者難以分離兩個層次。</p>
<hr>
<h2 id="識別訊號什麼時候你寫了依賴-code-的論述">識別訊號：什麼時候你寫了依賴 code 的論述</h2>
<h3 id="訊號-1論述用那個這個剛才的上面的當主詞">訊號 1：論述用「那個」「這個」「剛才的」「上面的」當主詞</h3>
<p>「那個 service」「這個 payload」「剛才的 controller」——這類代詞依賴讀者剛才看過 code。</p>
<p>修法：把代詞換成具體名詞 + 角色描述（「狀態變更 service」「事件 payload」「副螢幕鏡像 controller」）。</p>
<h3 id="訊號-2用-code-結構描述第二段那個欄位">訊號 2：用 code 結構描述（「第二段」「那個欄位」）</h3>
<p>「payload 第二段」「那個 nullable 欄位」「上面 method 的 return value」——這類描述依賴讀者看過 code 的具體結構。</p>
<p>修法:把 code 結構描述翻譯成業務角色描述。「payload 第二段」→「最後變動品項欄位（給需要追蹤單筆變動的訂閱者用）」。</p>
<h3 id="訊號-3時序連接詞依賴-code-順序">訊號 3：時序連接詞依賴 code 順序</h3>
<p>「先&hellip;然後&hellip;接著&hellip;」如果這個時序對應上面 code 的執行順序、論述跟 code 綁太緊。</p>
<p>修法：把時序敘述為「在 X 條件下、Y 動作觸發 Z 結果」、不依賴 code 的具體順序。</p>
<h3 id="訊號-4論述只有就好就能就行">訊號 4：論述只有「就好」「就能」「就行」</h3>
<p>「現在只要有人訂閱、UI 就能用」「修改一行就好」——這類「就」字句<strong>在依賴 code 補足背景條件時</strong>有問題。但「修一行就好」如果背景條件已在前文說明、用「就」表達精簡是合理的——區分標準是「沒有 code 也讀得通嗎」、不是字面有「就」就違規。</p>
<p>修法：背景條件不在前文時、把省略的條件補回去；背景條件已在前文時、保留「就」沒問題。「在訂閱端讀取這段資訊、加一個視覺標記的綁定、即可在 UI 上呈現」。</p>
<h3 id="訊號-5跳過-code-block-後段落讀不通">訊號 5：跳過 code block 後段落讀不通</h3>
<p>最直接的測試方法：把所有 code block 拿掉、再讀一次論述、看是否仍能理解。</p>
<p>讀不通 → 論述依賴 code、要修補。</p>
<hr>
<h2 id="修法把-reference-翻成-self-contained-描述">修法：把 reference 翻成 self-contained 描述</h2>
<h3 id="修法-1用名詞替代代詞">修法 1：用名詞替代代詞</h3>
<p>修補前：</p>
<blockquote>
<p>「那個 service 對外發送事件、payload 第二段帶了這次變動是哪筆。」</p></blockquote>
<p>修補後：</p>
<blockquote>
<p>「狀態變更 service 對外發送的事件 payload 包含兩段：當前完整商品列表、最後變動的具體品項。第二段是『最後變動品項』。」</p></blockquote>
<h3 id="修法-2用角色替代位置">修法 2：用角色替代「位置」</h3>
<p>修補前：</p>
<blockquote>
<p>「上面 code 第三行那個 listener 拿到的是 nullable。」</p></blockquote>
<p>修補後：</p>
<blockquote>
<p>「副螢幕的訂閱者收到的事件 payload 中、第二段（最後變動品項）可能為 null（移除或清空操作的情境）。」</p></blockquote>
<h3 id="修法-3用條件替代時序">修法 3：用條件替代時序</h3>
<p>修補前：</p>
<blockquote>
<p>「先建立 controller、然後 listen、接著 add 事件、再 cancel。」</p></blockquote>
<p>修補後:</p>
<blockquote>
<p>「在 controller 建立後、訂閱者可呼叫 <code>.listen()</code> 註冊；註冊完成後、controller 才能 <code>.add()</code> 事件給訂閱者；訂閱者呼叫 <code>.cancel()</code> 解除註冊後、後續 <code>.listen()</code> 對 single-subscription 仍然違反契約。」</p></blockquote>
<h3 id="修法-4把就好展開成具體條件">修法 4：把「就好」展開成具體條件</h3>
<p>修補前：</p>
<blockquote>
<p>「現在只要有人訂閱、把它記錄下來、UI 就能用。」</p></blockquote>
<p>修補後：</p>
<blockquote>
<p>「新需求只要新增一個訂閱者讀這段資訊、再把它對應到 UI 上的視覺標記即可——介面不需要變動、payload 結構不需要調整、實作範圍只限於新增訂閱端。」</p></blockquote>
<hr>
<h2 id="何時論述可以依賴-code">何時論述可以依賴 code</h2>
<p>「論述要 self-contained」這條原則在概念說明 / 架構決策 / 設計檢討類文章成立、但有合理例外：</p>
<table>
  <thead>
      <tr>
          <th>情境</th>
          <th>為什麼可以依賴 code</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Code walkthrough / line-by-line</td>
          <td>文章本身就是 code 解說、讀者預設會逐行對映</td>
      </tr>
      <tr>
          <td>簡短 inline 引用 specific 行為</td>
          <td>「<code>stream.listen()</code> 第一個參數是 callback」這類引用本身就是 code 字面</td>
      </tr>
      <tr>
          <td>Tutorial 教學步驟</td>
          <td>「跑這個指令、你會看到 X 輸出、接著做 Y」是 hands-on 教學風格</td>
      </tr>
      <tr>
          <td>Code review 評論</td>
          <td>評論本身就是針對某行 code、上下文是 inline 共享的</td>
      </tr>
  </tbody>
</table>
<p>判讀：寫之前自問「我的文章是『教讀者怎麼讀這份 code』還是『教讀者一個概念 / 框架』？」——前者可依賴 code、後者要 self-contained。</p>
<hr>
<h2 id="跟其他抽象層原則的關係">跟其他抽象層原則的關係</h2>
<table>
  <thead>
      <tr>
          <th>原則</th>
          <th>跟本卡的關係</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Compositional-writing 規則二：商業邏輯先於 CASE</td>
          <td>本卡是規則二在「字句層 reference 處理」的子場景——規則二講層次順序、本卡講論述不依賴 code reference。兩者有重疊但不完全相同</td>
      </tr>
      <tr>
          <td><a href="../ease-of-writing-vs-intent-alignment/">#67 寫作便利度跟意圖對齊反相關</a></td>
          <td>用「那個 payload」是寫作便利、self-contained 論述需要刻意翻譯——同骨展現</td>
      </tr>
      <tr>
          <td><a href="../colloquial-rhetoric-erodes-technical-precision/">#111 口語化修辭會稀釋技術精度</a></td>
          <td>「就好」「就能」這類字句既是口語也是依賴 code、本卡跟 #111 在這層重疊</td>
      </tr>
      <tr>
          <td><a href="../design-flaw-by-current-axes-not-hindsight/">#110 設計檢討用當下三軸論證、不依賴 hindsight</a></td>
          <td>三卡都是「論述要能讓讀者拿來用」的不同層次——當下視角 / 精度 / self-contained</td>
      </tr>
  </tbody>
</table>
<hr>
<h2 id="判讀徵兆">判讀徵兆</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>該做的行動</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>論述用「那個 / 這個 / 剛才」當主詞</td>
          <td>換成具體名詞 + 角色描述</td>
      </tr>
      <tr>
          <td>論述提到「第 X 段 / 第 Y 行 / 那個欄位」</td>
          <td>翻譯成業務角色描述</td>
      </tr>
      <tr>
          <td>段落用「就好 / 就能 / 就行」結尾</td>
          <td>把省略的條件補回去</td>
      </tr>
      <tr>
          <td>把 code block 拿掉後論述讀不通</td>
          <td>整段重寫成 self-contained</td>
      </tr>
      <tr>
          <td>寫的時候有 code 在記憶中、覺得「不用解釋」</td>
          <td>預設讀者跳過 code、強迫自己用文字描述</td>
      </tr>
  </tbody>
</table>
<p><strong>核心原則</strong>：技術文章的論述要 self-contained——讀者跳過所有 code block 仍能理解論述、是「商業邏輯先於 CASE」的延伸實踐。寫完後跑一次「拿掉 code block 還讀得通嗎」自測、讀不通 → 翻譯成 self-contained 描述。</p>
<hr>
<h2 id="self-case本卡的觸發來源">Self-case：本卡的觸發來源</h2>
<p>本卡的觸發是修 <a href="../../work-log/dart_stream_controller_single_vs_broadcast/">Dart StreamController：single-subscription vs broadcast 的事故實錄</a> 時、讀者指出某段論述「重點混淆 + 過於口語 + 預設讀者看過原始碼」。</p>
<p>問題段（修補前）：</p>
<blockquote>
<p>「技術上這需求很乾淨：service 早就在事件 payload 第二段帶了『這次變動是哪筆』，現在只要有人訂閱、把它記錄下來，UI 就能用。於是收銀主畫面的 controller 加了第二個訂閱。」</p></blockquote>
<p>問題分析：</p>
<ul>
<li>「事件 payload 第二段」依賴讀者看過 code 結構</li>
<li>「就能用」「就好」省略了具體條件</li>
<li>「於是 X 加了 Y」對話風格、不是論述風格</li>
</ul>
<p>修補後：</p>
<blockquote>
<p>「這個需求剛好對應 service 已經備妥但尚未被消費的資訊——service 對外的事件 payload 從原始設計就分兩段：一段是『當前完整的商品列表』、另一段是『這次變動的具體品項』。第二段是當初為『需要追蹤單筆變動的訂閱者』預留的擴充欄位、過去幾個月一直沒被消費。新需求只要新增一個訂閱者讀這段資訊、再把它對應到 UI 上的視覺標記即可——介面不需要變動、payload 結構不需要調整、實作範圍只限於新增訂閱端。」</p></blockquote>
<p>修補後的論述用「角色描述（『需要追蹤單筆變動的訂閱者』）」替代了「位置描述（『第二段』）」、補回了省略的條件（「介面不需要變動、payload 結構不需要調整、實作範圍只限於新增訂閱端」）。讀者即使跳過所有 code block 也能理解這段論述。</p>
<p>對應本卡：<strong>論述依賴 code 是「寫的人有 code 在腦中」的便利選擇——multi-pass review 要在輪 5「反例 / 邊界」加掃 self-contained frame、用「拿掉 code 還讀得通嗎」自測</strong>。</p>
]]></content:encoded></item></channel></rss>