<?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/%E4%BB%BB%E5%8B%99%E6%8B%86%E5%88%86/</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, 04 Mar 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/%E4%BB%BB%E5%8B%99%E6%8B%86%E5%88%86/index.xml" rel="self" type="application/rss+xml"/><item><title>Atomic Ticket 方法論 - 最小可追蹤任務單位設計</title><link>https://tarrragon.github.io/blog/record/atomic-ticket-%E6%96%B9%E6%B3%95%E8%AB%96-%E6%9C%80%E5%B0%8F%E5%8F%AF%E8%BF%BD%E8%B9%A4%E4%BB%BB%E5%8B%99%E5%96%AE%E4%BD%8D%E8%A8%AD%E8%A8%88/</link><pubDate>Wed, 04 Mar 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/atomic-ticket-%E6%96%B9%E6%B3%95%E8%AB%96-%E6%9C%80%E5%B0%8F%E5%8F%AF%E8%BF%BD%E8%B9%A4%E4%BB%BB%E5%8B%99%E5%96%AE%E4%BD%8D%E8%A8%AD%E8%A8%88/</guid><description>&lt;p>Ticket 越開越大，驗收條件越來越模糊，code review 要重新推理背景——這是我們曾長期面對的問題。根源很簡單：我們沒有定義「一個 Ticket 應該是什麼」。&lt;/p></description><content:encoded><![CDATA[<p>Ticket 越開越大，驗收條件越來越模糊，code review 要重新推理背景——這是我們曾長期面對的問題。根源很簡單：我們沒有定義「一個 Ticket 應該是什麼」。</p>
<h2 id="從一個問題出發">從一個問題出發</h2>
<p>試想這樣一張 Ticket：「實作 ISBNScannerService 的 15 個測試」。</p>
<p>15 個不同的測試目標，掃描啟動、停止、ISBN-10 驗證、ISBN-13 驗證、離線快取、批次模式……每一項失敗都讓整張 Ticket 無法完成。負責人要同時記住 15 個目標才能判斷自己有沒有做完。</p>
<p>這不是一張 Ticket，是一份工作清單。</p>
<h3 id="一張-ticket最少應該是什麼">一張 Ticket，最少應該是什麼？</h3>
<p>答案：<strong>一個動詞 + 一個目標</strong>。</p>
<hr>
<h2 id="什麼是-atomic-ticket">什麼是 Atomic Ticket</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">Atomic Ticket = 動詞 + 單一目標</span></span></code></pre></div><p>核心特徵：</p>
<ul>
<li><strong>單一職責</strong>：只有一個修改原因</li>
<li><strong>獨立驗收</strong>：不需要等待其他任務</li>
<li><strong>不可再拆分</strong>：硬拆會產生循環依賴，表示這些部分本來就是一體的</li>
</ul>
<hr>
<h2 id="四個原子性檢查">四個原子性檢查</h2>
<h3 id="語義檢查">語義檢查</h3>
<p>「這個 Ticket 能用動詞加單一目標描述嗎？」</p>
<p>符合：「實作 startScan() 方法」、「修復 ISBN 驗證邏輯」。</p>
<p>不符合：描述裡有「和」或「並」——「實作掃描功能和離線支援」通常是兩張 Ticket 擠在一起。</p>
<h3 id="修改原因檢查">修改原因檢查</h3>
<p>「只有一個原因會導致這個 Ticket 需要修改嗎？」</p>
<p>「實作 startScan()」只受掃描 API 規格影響，單一原因。「實作掃描功能和離線支援」則是 API 變更或儲存格式變更都會觸發修改——兩個原因，應拆成兩張。</p>
<h3 id="驗收條件一致性">驗收條件一致性</h3>
<p>「所有驗收條件都指向同一個目標嗎？」</p>
<p>如果驗收條件同時涵蓋 startScan()、stopScan() 和離線快取，其實在同時驗收三件事，應該拆開。</p>
<h3 id="依賴獨立性檢查">依賴獨立性檢查</h3>
<p>「如果拆成兩張，它們之間有循環依賴嗎？」</p>
<p>「實作掃描啟動邏輯」和「實作掃描狀態管理」互相依賴，硬拆反而讓兩張都無法獨立完成——這種情況維持為單一 Ticket 才對。</p>
<p>反過來，「實作 startScan()」和「實作 stopScan()」是單向依賴，可以安全拆分。</p>
<hr>
<h2 id="什麼不是判斷原子性的依據">什麼不是判斷原子性的依據</h2>
<p>常見的錯誤標準：時間、程式碼行數、檔案數量、測試數量。</p>
<p>這些都是結果，不是原因。一個單一職責的任務可能只需要 10 行，也可能跨越多個檔案改 200 行。判斷的唯一依據是職責是否單一。</p>
<hr>
<h2 id="行為分離開發測試調整各自追蹤">行為分離：開發、測試、調整各自追蹤</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">開發 (IMP) → 測試 (TST) → 調整 (ADJ)
</span></span><span class="line"><span class="ln">2</span><span class="cl">                  |
</span></span><span class="line"><span class="ln">3</span><span class="cl">               發現問題
</span></span><span class="line"><span class="ln">4</span><span class="cl">                  |
</span></span><span class="line"><span class="ln">5</span><span class="cl">           分析 (ANA) → 調整 (ADJ)</span></span></code></pre></div><p>開發完成不代表測試通過，測試完成可能衍生調整。把這三種行為混在同一張 Ticket，就失去了「問題在哪個環節發生」的追蹤能力。</p>
<p>完整的追蹤鏈讓我們能回答：「這個修復是從哪一次測試失敗衍生的？那次測試又是為了驗證哪個開發任務？」</p>
<h3 id="ticket-類型定義">Ticket 類型定義</h3>
<p>七種類型區分不同性質的任務：</p>
<ul>
<li>IMP（Implementation）：開發新功能</li>
<li>TST（Testing）：執行測試驗證</li>
<li>ADJ（Adjustment）：調整或修復問題</li>
<li>RES（Research）：探索未知領域</li>
<li>ANA（Analysis）：理解現狀和問題</li>
<li>INV（Investigation）：深入追蹤問題根因</li>
<li>DOC（Documentation）：記錄和傳承經驗</li>
</ul>
<hr>
<h2 id="ticket-關聯追蹤">Ticket 關聯追蹤</h2>
<p>每張 Ticket 記錄三個關聯欄位：</p>
<ul>
<li><code>source_ticket</code>：觸發此 Ticket 的來源</li>
<li><code>spawned_tickets</code>：此 Ticket 衍生的後續 Ticket</li>
<li><code>dispatch_reason</code>：派發原因和交接理由</li>
</ul>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">開發 Ticket (IMP) 完成
</span></span><span class="line"><span class="ln">2</span><span class="cl">    |
</span></span><span class="line"><span class="ln">3</span><span class="cl">    | 衍生
</span></span><span class="line"><span class="ln">4</span><span class="cl">    v
</span></span><span class="line"><span class="ln">5</span><span class="cl">測試 Ticket (TST)
</span></span><span class="line"><span class="ln">6</span><span class="cl">    |
</span></span><span class="line"><span class="ln">7</span><span class="cl">    | 測試失敗，衍生
</span></span><span class="line"><span class="ln">8</span><span class="cl">    v
</span></span><span class="line"><span class="ln">9</span><span class="cl">調整 Ticket (ADJ)，dispatch_reason: &#34;UC-01 測試失敗，需修復 ImportService&#34;</span></span></code></pre></div><hr>
<h2 id="ticket-id-命名規範">Ticket ID 命名規範</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">根任務：{Version}-W{Wave}-{Seq}
</span></span><span class="line"><span class="ln">2</span><span class="cl">子任務：{根ID}.{n}[.{n}...]</span></span></code></pre></div><p>Wave 代表執行批次的依賴層級：W1 無依賴可並行，W2 依賴 W1，以此類推。</p>
<p><code>0.15.16-W1-001</code> 是 v0.15.16 的 Wave 1 第一個任務。<code>0.31.0-W3-002.1</code> 是 0.31.0-W3-002 的第一個子任務。單看 ID 就能判斷它在版本工作流中的位置。</p>
<hr>
<h2 id="5w1h-驅動的欄位設計">5W1H 驅動的欄位設計</h2>
<p>每張 Ticket 的 YAML 欄位對應 5W1H：</p>
<ul>
<li>Who：負責人與各 Phase 的歷史負責人</li>
<li>What：任務目標（動詞加單一目標）</li>
<li>When：觸發時機</li>
<li>Where：架構層級與影響的檔案清單</li>
<li>Why：需求依據</li>
<li>How：任務類型與實作策略</li>
</ul>
<p>欄位設計讓 Ticket 本身就能完整描述任務全貌，交接時不需要額外解釋背景。</p>
<hr>
<h2 id="拆分範例">拆分範例</h2>
<h3 id="把功能清單拆成原子任務">把功能清單拆成原子任務</h3>
<p>原始需求：「實作 ISBNScannerService 的 15 個測試」</p>
<p>拆分後：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">0.15.16-W1-001: 實作 startScan() 方法
</span></span><span class="line"><span class="ln">2</span><span class="cl">0.15.16-W1-002: 實作 stopScan() 方法
</span></span><span class="line"><span class="ln">3</span><span class="cl">0.15.16-W1-003: 實作 validateIsbn10() 驗證邏輯
</span></span><span class="line"><span class="ln">4</span><span class="cl">0.15.16-W1-004: 實作 validateIsbn13() 驗證邏輯
</span></span><span class="line"><span class="ln">5</span><span class="cl">0.15.16-W2-005: 實作離線掃描支援（依賴 W1）
</span></span><span class="line"><span class="ln">6</span><span class="cl">0.15.16-W2-006: 實作批次掃描模式（依賴 W1）</span></span></code></pre></div><p>W1 任務可並行，W2 等 W1 完成後進行。</p>
<h3 id="知道什麼時候不該拆">知道什麼時候不該拆</h3>
<p>需求：「實作 BookRepository.save() 方法」</p>
<p>有人可能拆成：Ticket A 實作簽名、Ticket B 實作邏輯、Ticket C 實作驗證。</p>
<p>這是錯的。save() 的簽名、邏輯、驗證三者循環依賴，硬拆後每張都無法獨立完成。正確做法是維持為單一 Ticket。</p>
<hr>
<h2 id="建立-ticket-前的四個問題">建立 Ticket 前的四個問題</h2>
<ol>
<li>能用「動詞 + 單一目標」描述嗎？</li>
<li>只有一個修改原因嗎？</li>
<li>所有驗收條件都指向同一個目標嗎？</li>
<li>如果拆開，不會產生循環依賴嗎？</li>
</ol>
<p>常見的違反模式：「實作 X 和 Y」（兩個目標）、「修復所有 X 測試」（多個目標）、「重構 X 並優化 Y」（兩個行動）、「建立 X 的完整功能」（目標模糊）。</p>]]></content:encoded></item><item><title>認知負擔設計方法論 - 降低程式碼閱讀成本的設計原則</title><link>https://tarrragon.github.io/blog/record/%E8%AA%8D%E7%9F%A5%E8%B2%A0%E6%93%94%E8%A8%AD%E8%A8%88%E6%96%B9%E6%B3%95%E8%AB%96-%E9%99%8D%E4%BD%8E%E7%A8%8B%E5%BC%8F%E7%A2%BC%E9%96%B1%E8%AE%80%E6%88%90%E6%9C%AC%E7%9A%84%E8%A8%AD%E8%A8%88%E5%8E%9F%E5%89%87/</link><pubDate>Wed, 04 Mar 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/%E8%AA%8D%E7%9F%A5%E8%B2%A0%E6%93%94%E8%A8%AD%E8%A8%88%E6%96%B9%E6%B3%95%E8%AB%96-%E9%99%8D%E4%BD%8E%E7%A8%8B%E5%BC%8F%E7%A2%BC%E9%96%B1%E8%AE%80%E6%88%90%E6%9C%AC%E7%9A%84%E8%A8%AD%E8%A8%88%E5%8E%9F%E5%89%87/</guid><description>&lt;p>DRY、SOLID、Clean Code、設計模式……書讀了不少，原則背了很多，但還是常常看別人的程式碼頭痛，自己寫完一個月後也看不懂自己寫的是什麼。&lt;/p>
&lt;p>這些原則背後共同指向的是什麼？&lt;/p></description><content:encoded><![CDATA[<p>DRY、SOLID、Clean Code、設計模式……書讀了不少，原則背了很多，但還是常常看別人的程式碼頭痛，自己寫完一個月後也看不懂自己寫的是什麼。</p>
<p>這些原則背後共同指向的是什麼？</p>
<p>在推進一個書庫管理 App 的開發過程中，我們開始認真思考這個問題。結論很簡單：<strong>所有程式碼設計原則的終極目標，都是降低閱讀者的認知負擔。</strong></p>
<p>一旦確立了這個核心目標，很多過去「規定要這樣做」的事情，開始有了真正的理由。</p>
<h2 id="認知負擔是什麼">認知負擔是什麼</h2>
<p>認知負擔（Cognitive Load）指閱讀者在理解程式碼時，需要同時記住的資訊量。</p>
<p>認知心理學家米勒在 1956 年提出「Miller&rsquo;s Law」：人類工作記憶一次只能處理約 7±2 個項目。超過這個數量，大腦就開始掙扎——反覆閱讀、做筆記、或者乾脆猜。程式碼讓人難懂，通常是寫法讓閱讀者同時記住太多東西。</p>
<p>認知心理學把認知負擔分成三種：</p>
<p><strong>內在認知負擔（Intrinsic Load）</strong>，任務本身的固有複雜度。排序演算法有它的複雜度，業務邏輯有它的業務規則，無法消除。</p>
<p><strong>外在認知負擔（Extraneous Load）</strong>，由不良設計產生的額外複雜度——糟糕的命名、過長的函式、深層巢狀、隱藏的副作用。這些是可以、也應該主動消除的。</p>
<p><strong>相關認知負擔（Germane Load）</strong>，用於建立心智模型的有益負擔——理解一個設計模式、學習架構決策背後的原因。值得保留。</p>
<p>設計的目標因此很清楚：<strong>最小化外在認知負擔，讓閱讀者的認知資源真正用在理解問題本身和建立更深刻的理解。</strong></p>
<h2 id="認知負擔從哪裡來">認知負擔從哪裡來</h2>
<h3 id="變數狀態追蹤">變數狀態追蹤</h3>
<p>每一個區域變數，都是閱讀者需要「記住並追蹤」的一個項目。看看這段程式碼：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dart" data-lang="dart"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kt">void</span> <span class="n">processOrder</span><span class="p">(</span><span class="n">Order</span> <span class="n">order</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  <span class="kd">var</span> <span class="n">items</span> <span class="o">=</span> <span class="n">order</span><span class="p">.</span><span class="n">items</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="kd">var</span> <span class="n">total</span> <span class="o">=</span> <span class="m">0.0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="kd">var</span> <span class="n">discount</span> <span class="o">=</span> <span class="m">0.0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="kd">var</span> <span class="n">tax</span> <span class="o">=</span> <span class="m">0.0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  <span class="kd">var</span> <span class="n">shipping</span> <span class="o">=</span> <span class="m">0.0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="kd">var</span> <span class="n">finalPrice</span> <span class="o">=</span> <span class="m">0.0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="kd">var</span> <span class="n">isValid</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="kd">var</span> <span class="n">errorMessage</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="c1">// 閱讀者需要同時追蹤 8 個變數狀態
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><p>函式還沒開始做什麼，閱讀者已經被迫記住 8 個變數。改成這樣：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dart" data-lang="dart"><span class="line"><span class="ln">1</span><span class="cl"><span class="kt">void</span> <span class="n">processOrder</span><span class="p">(</span><span class="n">Order</span> <span class="n">order</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">  <span class="kd">final</span> <span class="n">pricing</span> <span class="o">=</span> <span class="n">_calculatePricing</span><span class="p">(</span><span class="n">order</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">  <span class="kd">final</span> <span class="n">validation</span> <span class="o">=</span> <span class="n">_validateOrder</span><span class="p">(</span><span class="n">order</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">  <span class="c1">// 閱讀者只需追蹤 2 個概念
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><p>數量少了，每個名稱也更清楚地說明了它代表什麼。</p>
<h3 id="呼叫層級追蹤">呼叫層級追蹤</h3>
<p>追蹤呼叫鏈需要「堆疊」記憶，就像遞迴一樣，每深入一層就要把上一層的狀態記住。</p>
<p>當閱讀者必須追蹤 <code>methodA -&gt; methodB -&gt; methodC -&gt; methodD</code> 四層呼叫才能理解一段邏輯時，工作記憶很快就到了極限。扁平化呼叫結構，讓邏輯盡量在同一個層次展開，能顯著減輕這種負擔。</p>
<h3 id="命名品質">命名品質</h3>
<p>不佳的命名讓閱讀者需要做「翻譯」工作——先猜測 <code>d</code> 是什麼，再猜測 <code>temp</code> 指的是什麼，然後才能開始理解邏輯。</p>
<p><code>d</code> 改成 <code>discountAmount</code>，<code>process()</code> 改成 <code>calculateTotalPrice()</code>，閱讀者可以節省的翻譯工夫，直接換成理解業務邏輯的空間。</p>
<h3 id="條件分支複雜度">條件分支複雜度</h3>
<p>每一個巢狀的 <code>if</code>，都要求閱讀者同時記住所有外層條件。三層巢狀就是同時記住三個條件，並且理解它們的組合關係。</p>
<p>Guard Clause 是最直接的解法：把各種「不符合條件就提前返回」的情況放在函式開頭，讓主要邏輯只有一層縮排。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dart" data-lang="dart"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">// 提前返回，消除巢狀
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">condition1</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">condition2</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">condition3</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="o">//</span> <span class="err">主邏輯，思路清晰，只有一層縮排</span></span></span></code></pre></div><h3 id="隱藏的副作用">隱藏的副作用</h3>
<p>函式名稱說「取得」，但偷偷修改了資料庫；方法說「計算」，但同時更新了快取。這些隱藏的副作用強迫閱讀者不能只看名稱理解行為，必須讀進每一行實作，才能確保自己真的理解了這個函式做了什麼。</p>
<p>「命令與查詢分離」（Command-Query Separation）的原則在這裡特別有用：查詢只讀取狀態，命令只修改狀態，兩者不混用。</p>
<h2 id="降低認知負擔的五個原則">降低認知負擔的五個原則</h2>
<p><strong>單一責任</strong>。一個函式只做一件事。自我檢查方法：用一句話描述這個函式做什麼，句子裡出現「和」或「並且」，通常就代表它做了超過一件事。</p>
<p><strong>自說明命名</strong>。名稱本身就是文件。理想的命名讓閱讀者不看實作也能理解函式的用途、變數的內容、類別的職責。<code>isEmailFormatValid</code> 比 <code>isValid</code> 好，<code>onUserLoginSuccess</code> 比 <code>handle</code> 好，<code>calculateMonthlyRevenue</code> 比 <code>calc</code> 好。</p>
<p><strong>避免副作用</strong>。函式的行為應該和名稱一致、可以預測。修改輸入以外的任何東西——全域狀態、資料庫、快取——都應該在名稱中反映，或者提取為獨立的命令函式。</p>
<p><strong>扁平化結構</strong>。巢狀深度每增加一層，都在要求閱讀者多記住一層上下文。1-2 層巢狀是優良的，3 層開始值得考慮是否能用 Guard Clause 或提取函式來簡化，超過 3 層通常就應該重構了。</p>
<p><strong>資訊就近原則</strong>。理解一段程式碼時，如果需要跳到其他三個地方去查定義、查常數、查相關邏輯，認知負擔就在這些跳轉中累積。相關的資訊放在一起，讓閱讀者可以在同一個地方理解完整的概念。</p>
<h2 id="solid-原則的另一種解讀">SOLID 原則的另一種解讀</h2>
<p>用認知負擔視角回頭看 SOLID，會發現它們有了更直覺的意義。</p>
<p><strong>單一責任原則（SRP）</strong> 傳統解釋是「一個類別只有一個改變的原因」。認知負擔的版本是：一個類別只代表一個概念，閱讀者不需要建立多個心智模型。<code>UserRepository</code> 的認知負擔低，<code>UserServiceAndValidatorAndNotifier</code> 的認知負擔高——因為閱讀者必須同時掌握多個不同的概念。</p>
<p><strong>開放封閉原則（OCP）</strong> 說對擴展開放、對修改封閉。認知負擔的版本是：新增功能時，不需要理解現有程式碼的全部細節。新增一種折扣類型，如果要讀懂 <code>calculatePrice()</code> 的所有邏輯和現有折扣的互動，認知負擔很高。如果只需要理解 <code>Discount</code> 介面的契約然後實作它，認知負擔就被控制在最小範圍。</p>
<p><strong>介面隔離原則（ISP）</strong> 要求介面不應包含使用者不需要的方法。認知負擔的視角：一個胖介面迫使閱讀者掃描並判斷哪些方法與自己相關，這是純粹的額外負擔。</p>
<h2 id="量化認知負擔">量化認知負擔</h2>
<p>主觀感覺「這段程式碼很複雜」很難作為行動依據。我們引入了一個簡單的量化公式：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">認知負擔指數 = 變數數 + 分支數 + 巢狀深度 + 外部依賴數</span></span></code></pre></div><p>1-5 分優良，6-10 分可接受，11-15 分需要重構，超過 15 分必須立即處理。</p>
<p>這個公式不是精確科學，但它提供了可以討論的共同語言。一個函式的指數算出來是 18，「這段程式碼太複雜」就不再是模糊的感覺，而是有數字支撐的判斷。</p>
<p>同樣的思路也用在任務規劃：一個任務需要同時追蹤超過 7 個概念、跨越 2 個以上的架構層、修改超過 5 個檔案，認知負擔就超標了，應該拆分，而不是強行一次完成。</p>
<h2 id="為了讓人更容易讀">為了讓人更容易讀</h2>
<p>電腦不在意程式碼怎麼寫。一個八層巢狀的函式和一個 Guard Clause 寫法的函式，在機器眼中差異幾乎是零。真正在乎的，是接下來要讀這段程式碼的人——三個月後的自己，剛加入的新同事，凌晨三點在修 bug 的任何人。</p>
<p>把「降低閱讀者的認知負擔」作為首要目標，所有設計決策就有了一致的評估標準：這樣寫，閱讀者需要記住更多還是更少？這樣命名，不讀實作也能理解嗎？這樣拆分，每個部分的概念有沒有變得更單純？</p>]]></content:encoded></item></channel></rss>