<?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>AI協作心得 on Tarragon</title><link>https://tarrragon.github.io/blog/tags/ai%E5%8D%94%E4%BD%9C%E5%BF%83%E5%BE%97/</link><description>Recent content in AI協作心得 on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Mon, 15 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/ai%E5%8D%94%E4%BD%9C%E5%BF%83%E5%BE%97/index.xml" rel="self" type="application/rss+xml"/><item><title>開發記錄</title><link>https://tarrragon.github.io/blog/record/</link><pubDate>Mon, 15 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/</guid><description>&lt;p>這個資料夾收錄&lt;strong>方法論記錄&lt;/strong> — 寫法是中性 frame 的「某個工作模式 / 流程 / 原則是什麼、怎麼用」、不一定有具體 case 觸發。&lt;/p>
&lt;p>內容大致分六類：&lt;/p>
&lt;p>&lt;strong>自察與認知方法論&lt;/strong> — 工作前 / 工作中的自我檢視框架。例：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="5w1h-self-awareness-methodology/">5W1H 自察方法論&lt;/a>&lt;/li>
&lt;li>&lt;a href="ai-task-avoidance-detection-methodology/">AI 任務迴避偵測方法論&lt;/a>&lt;/li>
&lt;li>&lt;a href="%e5%a4%a7%e8%a6%8f%e6%a8%a1%e9%87%8d%e6%a7%8b%e6%96%b9%e6%b3%95%e8%ab%96/">大規模重構方法論&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>設計判斷與選型&lt;/strong> — 技術決策框架，避免過度設計與設計瑕疵的判斷標準（如 YAGNI 的真實適用條件、成本不對稱性下的選擇邏輯）。例：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="yagni-boundary-three-axes/">YAGNI 的真實適用條件&lt;/a>&lt;/li>
&lt;li>&lt;a href="saas-selection-interview-methodology/">SaaS 選型訪談方法論&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>敏捷 / 工程流程&lt;/strong> — 敏捷實作、重構流程、文件分層。例：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="agile-programing-methodology/">敏捷編程方法論&lt;/a>&lt;/li>
&lt;li>&lt;a href="agile-refactor-methodology/">敏捷重構方法論&lt;/a>&lt;/li>
&lt;li>&lt;a href="5-layer-doc-system/">5 層文件系統&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>寫作 / 溝通標準&lt;/strong> — 驗收條件、寫作規範。例：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="acceptance-criteria-methodology/">驗收條件方法論&lt;/a>&lt;/li>
&lt;li>&lt;a href="writing-guidelines/">經驗分享文章的寫作準則&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>文件與 API 設計&lt;/strong> — function 文件分層、測試命名作為 spec、commit message vs source code doc 的職責邊界、型別取代 doc 等表達設計議題。例：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="function-doc-layered-design/">函式文件分層設計&lt;/a>&lt;/li>
&lt;li>&lt;a href="types-replacing-docs/">型別取代 doc 的收益曲線&lt;/a>&lt;/li>
&lt;li>&lt;a href="test-naming-as-documentation/">測試命名作為文件&lt;/a>&lt;/li>
&lt;li>&lt;a href="commit-message-vs-source-doc/">Commit message vs source code doc&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>AI 協作工具評估&lt;/strong> — Claude Code / Codex / MCP server 等 AI 協作工具的設計拆解、能力邊界、選型對照。包含個別工具的 deep-dive 跟「同題不同工具」的實測。例：&lt;/p>
&lt;ul>
&lt;li>&lt;a href="mcp-three-way-workflow-and-dart-experiment/">三 MCP 工作流與 Dart 實測&lt;/a>：cbm / codegraph / serena 的職責分工與三刀流（附三個工具各自的 deep-dive：&lt;a href="mcp-codebase-memory-deep-dive/">cbm&lt;/a> / &lt;a href="mcp-codegraph-deep-dive/">codegraph&lt;/a> / &lt;a href="mcp-serena-deep-dive/">serena&lt;/a>）&lt;/li>
&lt;li>&lt;a href="lsp-first-development-methodology/">LSP-first 開發方法論&lt;/a>：LSP 路線跟其他 code intelligence 路線的取捨&lt;/li>
&lt;li>&lt;a href="background-agent-parallel-research/">Background agent 並行研究&lt;/a>：用 Claude Code background agent 平行做研究的工作流&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="跟其他資料夾的邊界">跟其他資料夾的邊界&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>議題&lt;/th>
 &lt;th>該放&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>從具體 case 抽出可重用的工程原則&lt;/td>
 &lt;td>&lt;code>report/&lt;/code>（case-driven、編號連續）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>工作中遇到的具體事件 / 工具事故&lt;/td>
 &lt;td>&lt;code>work-log/&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>blog 本身的設計 / 規範&lt;/td>
 &lt;td>&lt;code>posts/&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>中性 frame 的方法論說明（不綁特定 case）&lt;/td>
 &lt;td>&lt;strong>本資料夾&lt;/strong>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>跟 &lt;code>report/&lt;/code> 的區別&lt;/strong>：record 是「方法論本身怎麼用」（教學 / 中性說明）、report 是「從某個 case 抽出來的原則」（事後檢討 / case-driven）。同一個議題若先有方法論再有 case、方法論寫 record、case 抽出的原則寫 report；若是先出問題再抽原則、直接寫 report。&lt;/p>
&lt;hr>
&lt;p>底下自動列出本資料夾的所有文章、依日期排序。&lt;/p></description><content:encoded><![CDATA[<p>這個資料夾收錄<strong>方法論記錄</strong> — 寫法是中性 frame 的「某個工作模式 / 流程 / 原則是什麼、怎麼用」、不一定有具體 case 觸發。</p>
<p>內容大致分六類：</p>
<p><strong>自察與認知方法論</strong> — 工作前 / 工作中的自我檢視框架。例：</p>
<ul>
<li><a href="5w1h-self-awareness-methodology/">5W1H 自察方法論</a></li>
<li><a href="ai-task-avoidance-detection-methodology/">AI 任務迴避偵測方法論</a></li>
<li><a href="%e5%a4%a7%e8%a6%8f%e6%a8%a1%e9%87%8d%e6%a7%8b%e6%96%b9%e6%b3%95%e8%ab%96/">大規模重構方法論</a></li>
</ul>
<p><strong>設計判斷與選型</strong> — 技術決策框架，避免過度設計與設計瑕疵的判斷標準（如 YAGNI 的真實適用條件、成本不對稱性下的選擇邏輯）。例：</p>
<ul>
<li><a href="yagni-boundary-three-axes/">YAGNI 的真實適用條件</a></li>
<li><a href="saas-selection-interview-methodology/">SaaS 選型訪談方法論</a></li>
</ul>
<p><strong>敏捷 / 工程流程</strong> — 敏捷實作、重構流程、文件分層。例：</p>
<ul>
<li><a href="agile-programing-methodology/">敏捷編程方法論</a></li>
<li><a href="agile-refactor-methodology/">敏捷重構方法論</a></li>
<li><a href="5-layer-doc-system/">5 層文件系統</a></li>
</ul>
<p><strong>寫作 / 溝通標準</strong> — 驗收條件、寫作規範。例：</p>
<ul>
<li><a href="acceptance-criteria-methodology/">驗收條件方法論</a></li>
<li><a href="writing-guidelines/">經驗分享文章的寫作準則</a></li>
</ul>
<p><strong>文件與 API 設計</strong> — function 文件分層、測試命名作為 spec、commit message vs source code doc 的職責邊界、型別取代 doc 等表達設計議題。例：</p>
<ul>
<li><a href="function-doc-layered-design/">函式文件分層設計</a></li>
<li><a href="types-replacing-docs/">型別取代 doc 的收益曲線</a></li>
<li><a href="test-naming-as-documentation/">測試命名作為文件</a></li>
<li><a href="commit-message-vs-source-doc/">Commit message vs source code doc</a></li>
</ul>
<p><strong>AI 協作工具評估</strong> — Claude Code / Codex / MCP server 等 AI 協作工具的設計拆解、能力邊界、選型對照。包含個別工具的 deep-dive 跟「同題不同工具」的實測。例：</p>
<ul>
<li><a href="mcp-three-way-workflow-and-dart-experiment/">三 MCP 工作流與 Dart 實測</a>：cbm / codegraph / serena 的職責分工與三刀流（附三個工具各自的 deep-dive：<a href="mcp-codebase-memory-deep-dive/">cbm</a> / <a href="mcp-codegraph-deep-dive/">codegraph</a> / <a href="mcp-serena-deep-dive/">serena</a>）</li>
<li><a href="lsp-first-development-methodology/">LSP-first 開發方法論</a>：LSP 路線跟其他 code intelligence 路線的取捨</li>
<li><a href="background-agent-parallel-research/">Background agent 並行研究</a>：用 Claude Code background agent 平行做研究的工作流</li>
</ul>
<hr>
<h2 id="跟其他資料夾的邊界">跟其他資料夾的邊界</h2>
<table>
  <thead>
      <tr>
          <th>議題</th>
          <th>該放</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>從具體 case 抽出可重用的工程原則</td>
          <td><code>report/</code>（case-driven、編號連續）</td>
      </tr>
      <tr>
          <td>工作中遇到的具體事件 / 工具事故</td>
          <td><code>work-log/</code></td>
      </tr>
      <tr>
          <td>blog 本身的設計 / 規範</td>
          <td><code>posts/</code></td>
      </tr>
      <tr>
          <td>中性 frame 的方法論說明（不綁特定 case）</td>
          <td><strong>本資料夾</strong></td>
      </tr>
  </tbody>
</table>
<p><strong>跟 <code>report/</code> 的區別</strong>：record 是「方法論本身怎麼用」（教學 / 中性說明）、report 是「從某個 case 抽出來的原則」（事後檢討 / case-driven）。同一個議題若先有方法論再有 case、方法論寫 record、case 抽出的原則寫 report；若是先出問題再抽原則、直接寫 report。</p>
<hr>
<p>底下自動列出本資料夾的所有文章、依日期排序。</p>
]]></content:encoded></item><item><title>codebase-memory-mcp：155 語言 tree-sitter 知識圖譜 MCP 的能力與邊界</title><link>https://tarrragon.github.io/blog/record/codebase-memory-mcp155-%E8%AA%9E%E8%A8%80-tree-sitter-%E7%9F%A5%E8%AD%98%E5%9C%96%E8%AD%9C-mcp-%E7%9A%84%E8%83%BD%E5%8A%9B%E8%88%87%E9%82%8A%E7%95%8C/</link><pubDate>Mon, 25 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/codebase-memory-mcp155-%E8%AA%9E%E8%A8%80-tree-sitter-%E7%9F%A5%E8%AD%98%E5%9C%96%E8%AD%9C-mcp-%E7%9A%84%E8%83%BD%E5%8A%9B%E8%88%87%E9%82%8A%E7%95%8C/</guid><description>&lt;h2 id="這個-mcp-解什麼問題">這個 MCP 解什麼問題&lt;/h2>
&lt;p>codebase-memory-mcp（下稱 cbm）的核心定位是「&lt;strong>把整個 codebase 預先解析成可被 LLM 廉價查詢的知識圖譜&lt;/strong>」。它要替代的是 agent 在不熟悉的 codebase 上「拿 grep / glob / read 連環翻檔」的探索 pattern——人類用 IDE 編輯、agent 用 cbm 探索、兩者服務不同的工作流。&lt;/p>
&lt;p>設計上跟其他「graph + LLM」工具的關鍵分野，在於它&lt;strong>不內嵌任何 LLM 做自然語言 → 查詢轉換&lt;/strong>：&lt;/p>
&lt;blockquote>
&lt;p>Other code graph tools embed an LLM for natural language → graph query translation. This means extra API keys, extra cost, and another model to configure. With MCP, the agent you&amp;rsquo;re already talking to &lt;em>is&lt;/em> the query translator.&lt;/p>&lt;/blockquote>
&lt;p>所以 cbm 自己只是個提供高品質 graph 查詢 API 的 server，「翻譯自然語言」這件事直接讓呼叫端的 agent 做。這個取捨對 Claude Code 這類 host 是理想的，因為 host 端已經有一顆夠強的模型在跑。&lt;/p>
&lt;h2 id="部署形態決定它的甜蜜點">部署形態決定它的甜蜜點&lt;/h2>
&lt;p>cbm 是&lt;strong>單一靜態 binary&lt;/strong>，所有依賴（155 種 tree-sitter grammar、SQLite、tokenizer）都在 binary 內，安裝後沒有外部 runtime 依賴。&lt;/p>
&lt;p>這個取捨直接影響它的甜蜜點：&lt;/p>
&lt;ul>
&lt;li>跨平台分發成本低，CI 上跑也方便&lt;/li>
&lt;li>不需要為個別語言裝 toolchain（不像 LSP 路線要對應 language server）&lt;/li>
&lt;li>但代價是「能力上限」被 binary 內附的 grammar 跟自寫的 type resolution 算法綁住，無法靠 IDE 生態的成熟度借力&lt;/li>
&lt;/ul>
&lt;p>知道這個取捨之後，後面所有能力差異都解釋得通：能做的事多半是「靜態可推導」的，需要 query 外部 toolchain（如 IDE language server）的場景多半要靠別的工具補。&lt;/p>
&lt;h2 id="索引架構多-pass--ram-first">索引架構：多 pass + RAM-first&lt;/h2>
&lt;p>cbm 的索引流程是 &lt;strong>RAM-first 的多 pass pipeline&lt;/strong>，pass 之間有明確的責任分工：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Pass&lt;/th>
 &lt;th>責任&lt;/th>
 &lt;th>抽出的 edge / node（為主）&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>structure&lt;/td>
 &lt;td>tree-sitter 解 AST，建初始 node&lt;/td>
 &lt;td>Project / Package / Folder / File / Module&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>definitions&lt;/td>
 &lt;td>抽函式 / 類別 / 介面 / 型別定義&lt;/td>
 &lt;td>Class / Function / Method / Interface / Enum / Type&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>calls&lt;/td>
 &lt;td>解析 function call、結合 import 與型別&lt;/td>
 &lt;td>CALLS / ASYNC_CALLS / USAGE / USES_TYPE / IMPLEMENTS&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>HTTP links&lt;/td>
 &lt;td>偵測 REST / gRPC / GraphQL route&lt;/td>
 &lt;td>Route、HTTP_CALLS、HANDLES&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>configuration&lt;/td>
 &lt;td>掃 Docker / Kubernetes / Kustomize&lt;/td>
 &lt;td>Resource、CONFIGURES、WRITES&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>tests&lt;/td>
 &lt;td>偵測測試函式與被測對象關係&lt;/td>
 &lt;td>TESTS、FILE_CHANGES_WITH&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>執行期用 LZ4 壓縮的記憶體 SQLite 加速，所有 pass 跑完一次性 dump 成持久化 DB（路徑 &lt;code>~/.cache/codebase-memory-mcp/&lt;/code>，WAL mode）。team 共享情境下可加跑 zstd 壓縮（best tier 用 &lt;code>zstd -9&lt;/code> + index strip、fast tier 用 &lt;code>zstd -3&lt;/code> 走 watcher 增量），匯出成 &lt;code>.codebase-memory/graph.db.zst&lt;/code> artifact 給 CI / 隊友共用。&lt;/p></description><content:encoded><![CDATA[<h2 id="這個-mcp-解什麼問題">這個 MCP 解什麼問題</h2>
<p>codebase-memory-mcp（下稱 cbm）的核心定位是「<strong>把整個 codebase 預先解析成可被 LLM 廉價查詢的知識圖譜</strong>」。它要替代的是 agent 在不熟悉的 codebase 上「拿 grep / glob / read 連環翻檔」的探索 pattern——人類用 IDE 編輯、agent 用 cbm 探索、兩者服務不同的工作流。</p>
<p>設計上跟其他「graph + LLM」工具的關鍵分野，在於它<strong>不內嵌任何 LLM 做自然語言 → 查詢轉換</strong>：</p>
<blockquote>
<p>Other code graph tools embed an LLM for natural language → graph query translation. This means extra API keys, extra cost, and another model to configure. With MCP, the agent you&rsquo;re already talking to <em>is</em> the query translator.</p></blockquote>
<p>所以 cbm 自己只是個提供高品質 graph 查詢 API 的 server，「翻譯自然語言」這件事直接讓呼叫端的 agent 做。這個取捨對 Claude Code 這類 host 是理想的，因為 host 端已經有一顆夠強的模型在跑。</p>
<h2 id="部署形態決定它的甜蜜點">部署形態決定它的甜蜜點</h2>
<p>cbm 是<strong>單一靜態 binary</strong>，所有依賴（155 種 tree-sitter grammar、SQLite、tokenizer）都在 binary 內，安裝後沒有外部 runtime 依賴。</p>
<p>這個取捨直接影響它的甜蜜點：</p>
<ul>
<li>跨平台分發成本低，CI 上跑也方便</li>
<li>不需要為個別語言裝 toolchain（不像 LSP 路線要對應 language server）</li>
<li>但代價是「能力上限」被 binary 內附的 grammar 跟自寫的 type resolution 算法綁住，無法靠 IDE 生態的成熟度借力</li>
</ul>
<p>知道這個取捨之後，後面所有能力差異都解釋得通：能做的事多半是「靜態可推導」的，需要 query 外部 toolchain（如 IDE language server）的場景多半要靠別的工具補。</p>
<h2 id="索引架構多-pass--ram-first">索引架構：多 pass + RAM-first</h2>
<p>cbm 的索引流程是 <strong>RAM-first 的多 pass pipeline</strong>，pass 之間有明確的責任分工：</p>
<table>
  <thead>
      <tr>
          <th>Pass</th>
          <th>責任</th>
          <th>抽出的 edge / node（為主）</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>structure</td>
          <td>tree-sitter 解 AST，建初始 node</td>
          <td>Project / Package / Folder / File / Module</td>
      </tr>
      <tr>
          <td>definitions</td>
          <td>抽函式 / 類別 / 介面 / 型別定義</td>
          <td>Class / Function / Method / Interface / Enum / Type</td>
      </tr>
      <tr>
          <td>calls</td>
          <td>解析 function call、結合 import 與型別</td>
          <td>CALLS / ASYNC_CALLS / USAGE / USES_TYPE / IMPLEMENTS</td>
      </tr>
      <tr>
          <td>HTTP links</td>
          <td>偵測 REST / gRPC / GraphQL route</td>
          <td>Route、HTTP_CALLS、HANDLES</td>
      </tr>
      <tr>
          <td>configuration</td>
          <td>掃 Docker / Kubernetes / Kustomize</td>
          <td>Resource、CONFIGURES、WRITES</td>
      </tr>
      <tr>
          <td>tests</td>
          <td>偵測測試函式與被測對象關係</td>
          <td>TESTS、FILE_CHANGES_WITH</td>
      </tr>
  </tbody>
</table>
<p>執行期用 LZ4 壓縮的記憶體 SQLite 加速，所有 pass 跑完一次性 dump 成持久化 DB（路徑 <code>~/.cache/codebase-memory-mcp/</code>，WAL mode）。team 共享情境下可加跑 zstd 壓縮（best tier 用 <code>zstd -9</code> + index strip、fast tier 用 <code>zstd -3</code> 走 watcher 增量），匯出成 <code>.codebase-memory/graph.db.zst</code> artifact 給 CI / 隊友共用。</p>
<p>Pass 排序遵循明確的依賴關係：calls 一定在 definitions 之後（因為 call edge 要連到已被建出來的 function / method node）、HTTP links 一定在 calls 之後（需要先有 call edge 才能比對 route 跟 handler）、configuration / tests 是 cross-cutting 的最終層（前面的結構與 call graph 都齊備、它們才能掛上 CONFIGURES / TESTS edge）。實務影響：HTTP links pass 在「單 service repo」上等於 no-op、configuration pass 在「缺 IaC manifest」的 repo 上也是 no-op、這兩個 pass 的價值高度依賴 repo 結構。</p>
<p>這個架構的副作用是：<strong>單次完整 index 速度快</strong>（README 聲稱 Linux kernel 3 分鐘），但<strong>增量更新採背景 git polling</strong>（IDE-style file watcher 是即時觸發、cbm 是定期掃描），對「邊改邊查」的工作流會有秒級延遲。</p>
<h2 id="11-signal-語意搜尋cbm-最強的差異化">11-signal 語意搜尋：cbm 最強的差異化</h2>
<p>如果只看 README 寫的「BM25 全文搜尋」，會嚴重低估 cbm 的搜尋能力。實際上 <code>search_graph</code> 的 ranking 是 <strong>11 個 signal 的加權組合</strong>：</p>
<table>
  <thead>
      <tr>
          <th>Signal</th>
          <th>角色</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>TF-IDF</td>
          <td>詞頻 / 逆文檔頻率，傳統文字相關性</td>
      </tr>
      <tr>
          <td>RRI</td>
          <td>Reverse rank importance，符號在 graph 中的重要性</td>
      </tr>
      <tr>
          <td>API / Type / Decorator signature</td>
          <td>函式簽章、型別標註、decorator 是高權重訊號</td>
      </tr>
      <tr>
          <td>AST profile</td>
          <td>AST 結構相似性</td>
      </tr>
      <tr>
          <td>Data flow</td>
          <td>變數與參數依賴鏈</td>
      </tr>
      <tr>
          <td>Halstead-lite</td>
          <td>簡化的程式複雜度指標</td>
      </tr>
      <tr>
          <td>MinHash</td>
          <td>近重複偵測（找變體 / 複製貼上）</td>
      </tr>
      <tr>
          <td>Module proximity</td>
          <td>符號在依賴 graph 上的距離</td>
      </tr>
      <tr>
          <td>Graph diffusion</td>
          <td>在 graph 上做 spreading activation</td>
      </tr>
  </tbody>
</table>
<p>表格列了 9 個明確 signal、README 另說有 11 個（剩 2 個是 implementation detail 沒公開細節）。實務上 11-signal 的價值在於<strong>幾個高權重 signal 各自負責不同 query 類型</strong>——權重分配有明顯的高低差：</p>
<ul>
<li><strong>RRI</strong> 是 cbm 對「重要符號優先」的 graph 結構 prior。一個被大量檔案 import 的核心 class、即使在 query 字串裡只有間接匹配、RRI 也會把它往上推。這層對「找這個 codebase 的入口 / 主要抽象」類 query 特別重要。</li>
<li><strong>Data flow</strong> 是 cbm 對「概念上接近、但符號名沒共字」的 query 的關鍵 signal。例如查「金額顯示」、<code>formatAmount</code> 跟 <code>_buildPriceDisplay</code> 在符號名上沒共字、但 data flow 能捕捉「<code>formatAmount</code> 的回傳值流入了 <code>_buildPriceDisplay</code> 的 widget」這層連結。</li>
<li><strong>Graph diffusion</strong> 是 cbm 對「擴散式相關性」的最終 boost——已經被前面 signal 推到高分的符號，會把分數擴散到 graph 上鄰近的符號。實務影響：monorepo 上效果最強（跨 module 鄰近性有意義）、單一檔案的小專案上幾乎沒效果。</li>
</ul>
<p>加上一層 <strong><code>cbm_camel_split</code> tokenizer</strong>：對 <code>getMoneyField</code> 這類 identifier 做 camelCase / snake_case 切詞，所以查 <code>money field display</code> 能命中 <code>getMoneyField</code>、<code>MoneyFieldRenderer</code> 之類符號。</p>
<p>這套組合的判讀價值在於：<strong>對「我不知道精確符號名」的概念性查詢，cbm 是少數能給出合理 top-N 的工具</strong>。例如查「金額顯示相關」、結果裡會出現 <code>formatAmount</code> 實作 + <code>_buildPriceDisplay</code> + <code>getBalanceDisplay</code>，這些都跟「金額顯示」業務概念相關、不會被 <code>displayName</code> / <code>displayTags</code> 這種只共享 <code>display</code> 子字串的雜訊淹沒。</p>
<p>下一步路由：要看實測案例，見 <a href="/blog/record/%E4%B8%89-mcp-%E5%B7%A5%E4%BD%9C%E6%B5%81%E8%88%87-dart-%E5%AF%A6%E6%B8%ACcbm-/-codegraph-/-serena-%E7%9A%84%E8%81%B7%E8%B2%AC%E5%88%86%E5%B7%A5%E8%88%87%E4%B8%89%E5%88%80%E6%B5%81/">三 MCP 工作流與 Dart 實測</a>。</p>
<h2 id="hybrid-type-resolution只給五個語言的特殊待遇">Hybrid type resolution：只給五個語言的特殊待遇</h2>
<p>cbm 對 <strong>Go / C / C++ / TypeScript / JavaScript</strong>（JS 含 JSX、TS 含 TSX）額外跑一層 type resolution，README 描述是：</p>
<blockquote>
<p>Clean-room reimplementation of tsserver / typescript-go&rsquo;s type resolution algorithms — parameter binding, return-type inference, generic substitution, JSX component dispatch, JSDoc inference for plain JS files.</p></blockquote>
<p>換言之，這幾個語言的 <code>CALLS</code> edge 在 syntactic match 之上多了一層 type-aware dispatch resolution，效果接近 LSP。其他 149 個語言只跑純 tree-sitter pass，能力會降到「<strong>結構抽得到、call edge 抽得很有限</strong>」。</p>
<p>實測對照（在某 Dart 商業專案上）：</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">cbm 索引完成統計：3,038 nodes、6,355 edges
</span></span><span class="line"><span class="ln">2</span><span class="cl">其中 CALLS edge 總數：2（整個專案僅 2 條）</span></span></code></pre></div><p>這個數字反映 cbm 的設計選擇——<strong>hybrid resolution 名單只涵蓋 5 個語言、Dart 在名單外</strong>——所以 <code>trace_call_path</code> 對 Dart symbol 一律回 0 caller，這個 0 屬於 by design 行為。對 Go / TS 主力專案，這個能力上限會完全不一樣。</p>
<p>判讀訊號：開發前先確認自己的主力語言在不在那五個語言內。在的話 cbm 是準 LSP；不在的話它只是個「結構 + 全文搜尋」工具，呼叫鏈相關問題要靠別的 MCP 補。</p>
<h2 id="跨-service-鏈接first-class-http_calls-edge">跨 service 鏈接：first-class HTTP_CALLS edge</h2>
<p>cbm 的另一個差異化能力是把 <strong>REST / gRPC / GraphQL / tRPC route 當 first-class node</strong>，建立跨 service 的 <code>HTTP_CALLS</code> edge：</p>
<ul>
<li>Route 偵測：對應主流 web framework（Express / NestJS / FastAPI / Gin / Rails 等）的 route 定義語法</li>
<li>Call site 比對：以 route pattern 比對 client 端的 URL 字面值或變數，附 confidence score</li>
<li>額外的 channel edge：Socket.IO / EventEmitter / 各種 pub-sub 的 <code>EMITS</code> / <code>LISTENS_ON</code></li>
</ul>
<p>這層能力對單一 monorepo 內的多 service 架構（microservice repo / BFF / API gateway pattern）特別有價值——可以查「這個前端 API call 對應哪個後端 handler」這種跨 service 問題。對單一 service 的單體 repo，這層能力派不上用場。</p>
<p>實際使用前提：要 index 的 repo 必須<strong>同時包含 client 跟 server 端</strong>，分散在多 repo 的話 cbm 不會自動跨 repo 連邊。</p>
<h2 id="cypher-子集支援的查詢與邊界">Cypher 子集：支援的查詢與邊界</h2>
<p>cbm 提供的 <code>query_graph</code> 是 Cypher 的<strong>真子集</strong>——覆蓋大部分 read-only query 語法、省略 mutation 與部分 aggregation 語法：</p>
<p><strong>支援</strong>：</p>
<ul>
<li><code>MATCH</code> 含 label / relationship type / 變長路徑</li>
<li><code>WHERE</code> 含比較 / regex / <code>CONTAINS</code></li>
<li><code>RETURN</code> 含 property access、<code>COUNT</code>、<code>DISTINCT</code></li>
<li><code>ORDER BY</code>、<code>LIMIT</code></li>
</ul>
<p><strong>不支援</strong>：</p>
<ul>
<li><code>WITH</code>（不能多階段 pipeline）</li>
<li><code>COLLECT</code>（不能 aggregate 成 list）</li>
<li><code>OPTIONAL MATCH</code>（不能 left-join）</li>
<li><code>labels(n)</code> / <code>type(r)</code> 等函數呼叫</li>
<li><code>AS</code> 別名</li>
<li>任何 mutation（純讀）</li>
</ul>
<p>幾個限制各自踩到的事故型態：</p>
<ul>
<li><strong><code>WITH</code> 缺席</strong>：所有需要「先 match 一組、再 filter / aggregate」的二階段 query 都寫不出來。例如「列出每個 module 內最常被呼叫的 function」這種 Top-K per group 的 query、在 Cypher 是 <code>MATCH ... WITH module, COUNT(*) AS c ORDER BY c LIMIT 1</code>、在 cbm 要拆成「先 list modules、再對每個 module 跑一次 callers query、外層排序」。</li>
<li><strong><code>OPTIONAL MATCH</code> 缺席</strong>：left-join 場景做不到。例如「列出所有 class、附上它的 supertype（若有）」這種「主結果不該因為某個關係缺失就丟掉」的 query 寫不出來。cbm 上的做法是先抓全部 class、再對每個 class 跑一次 supertype query、在 client 端合併。</li>
<li><strong><code>labels(n)</code> 缺席</strong>：拿不到 graph 內所有 node label 種類的清單。想做「我的 graph 裡有哪幾類 node」這種 schema 探索類 query、得退回 <code>get_graph_schema</code> 拿固定的 schema 介紹、看不到 instance 層真實分布。</li>
<li><strong><code>AS</code> 別名缺席</strong>：query 結果直接是 node / relationship object、沒法 rename 欄位給 downstream consumer。</li>
</ul>
<p>這些限制的共通實際影響：<strong>想做 group-by-count 類的 graph stats 查詢得退回 <code>search_graph</code> 逐 label 抽</strong>。例如「列出每個 file 有幾個 method」這種一行 Cypher 在標準 Neo4j 能寫的、在 cbm 上要拆成多次 query 在外層彙整。</p>
<p>判讀訊號：若 query 需要 <code>WITH ... COLLECT(...) AS xs</code> 這類二階段語法，先別硬寫 Cypher，改用 <code>search_graph</code> 加 client 端聚合。</p>
<h2 id="安裝行為與兩個要注意的小坑">安裝行為與兩個要注意的小坑</h2>
<p>cbm 的 <code>install.sh</code> 對 <code>~/.claude/settings.json</code> 動的範圍比 README 寫得多。實際安裝會：</p>
<ul>
<li>下載對應平台 binary、剝 macOS quarantine、ad-hoc sign</li>
<li>自動偵測 11 種 coding agent，逐一注入 MCP server config</li>
<li>對 Claude Code 寫入 <code>.claude/.mcp.json</code>、4 個 Skill、PreToolUse hook</li>
<li>Hook 名稱：<code>cbm-code-discovery-gate</code>，攔截 Grep / Glob 注入結構化 context</li>
</ul>
<p>兩個實際踩過的小坑：</p>
<p><strong>Hook matcher 與 README 不一致</strong>。README 描述「intercepts Grep/Glob — never Read」，實際安裝版本 matcher 是 <code>&quot;Grep|Glob|Read|Search&quot;</code>，連 Read 也被擋。修法：手動把 matcher 改成 <code>&quot;Grep|Glob|Search&quot;</code>。注意 <code>codebase-memory-mcp update</code> 可能會把這行改回原樣，每次升級要重新確認。</p>
<p><strong>uninstall 不清 hook</strong>。卸載 binary 不會主動把 <code>~/.claude/settings.json</code> 裡的 hook 條目移除。決定不再用 cbm 時要手動清掉 <code>PreToolUse</code> 下的 <code>cbm-code-discovery-gate</code> 條目，否則之後安裝其他工具或除錯時會看到神祕的 BLOCKED 訊息。</p>
<h2 id="14-個-mcp-tool-的分類">14 個 MCP tool 的分類</h2>
<table>
  <thead>
      <tr>
          <th>類別</th>
          <th>Tool</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>索引</td>
          <td><code>index_repository</code>、<code>list_projects</code>、<code>delete_project</code>、<code>index_status</code></td>
      </tr>
      <tr>
          <td>查詢</td>
          <td><code>search_graph</code>、<code>trace_call_path</code>、<code>detect_changes</code>、<code>query_graph</code>、<code>get_graph_schema</code>、<code>get_code_snippet</code>、<code>get_architecture</code>、<code>search_code</code></td>
      </tr>
      <tr>
          <td>管理</td>
          <td><code>manage_adr</code>（架構決策紀錄 CRUD）、<code>ingest_traces</code>（runtime trace 驗證 HTTP_CALLS）</td>
      </tr>
  </tbody>
</table>
<p>特別值得提的兩個：</p>
<ul>
<li><code>manage_adr</code>：把 Architecture Decision Records 當持久化資源管理。對長期專案有累積架構決策需求的場景有用，但若團隊已用 ADR-tools 或 Notion 管 ADR，這層會重複。</li>
<li><code>ingest_traces</code>：餵 runtime trace 進來驗證 <code>HTTP_CALLS</code> edge 是否反映實際的 runtime 調用。可以把靜態推測的 cross-service edge 與真實 runtime 行為對齊。實務上要先有 distributed tracing 基礎建設才開得了，門檻偏高。</li>
</ul>
<h2 id="適用--不適用情境的判讀">適用 / 不適用情境的判讀</h2>
<p><strong>適用情境</strong>：</p>
<ul>
<li><strong>主力語言在 Go / C / C++ / TS / JS 名單內</strong> → 享受 hybrid type resolution。判讀方法：對 5 個熱門 class 跑 <code>trace_call_path</code>、若 caller 數跟 IDE「Find Usages」結果對得上、表示 hybrid 正常工作。</li>
<li><strong>概念性 / 自然語言搜尋需求高</strong> → 11-signal scoring 是少數能勝任的 MCP。判讀方法：對「我只記得功能類別、不記得名字」的 query 跑 cbm 跟其他工具的 search、若 cbm top-10 命中率明顯高、值得當主要入口。</li>
<li><strong>跨 service 的 monorepo</strong> → first-class HTTP_CALLS edge 抽得到 cross-service 鏈。判讀方法：repo 內若有多個 service 用 HTTP / gRPC / GraphQL 互相呼叫、又分散在同一個 git tree 內、cbm 能跨 service 連邊；若只是單 service repo 這條沒效。</li>
<li><strong>偏好單 binary 部署</strong> → 不想為個別語言裝 toolchain、cbm 是少數零外部依賴的選項。</li>
</ul>
<p><strong>不適用情境</strong>：</p>
<ul>
<li><strong>主力語言不在 hybrid resolution 名單</strong>（如 Dart / Swift / Kotlin）且核心需求是 caller / blast radius 追蹤。判讀方法：在自己 repo 跑 cbm <code>trace_call_path</code> 對 5 個熱門 class、若 caller 數明顯偏低或 0、表示 cbm 在這語言只剩結構抽取、要靠 LSP 工具補。</li>
<li><strong>要 symbol-level 編輯</strong>（rename / replace_symbol_body）— cbm 純讀、沒這層。判讀方法：要做「rename method 並更新所有 reference」這類 atomic refactor 時、cbm 完全幫不上忙、要走 LSP 工具。</li>
<li><strong>要編譯 diagnostic 整合</strong> — cbm 不接 LSP、沒法把 type error / unused import 拋給 agent。</li>
</ul>
<p><strong>搭配建議</strong>：在不在 hybrid resolution 名單的語言上，cbm 通常需要配合一個 LSP-based MCP（如 <a href="/blog/record/serena%E6%8A%8A-lsp-%E5%8C%85%E6%88%90-agent-first-mcp-%E7%9A%84-symbol-level-%E7%B7%A8%E8%BC%AF%E6%96%B9%E6%A1%88/">serena</a>）做 caller / impact 補位，加上一個 tree-sitter call graph 工具（如 <a href="/blog/record/codegraph%E7%94%A8-tree-sitter-per-language-query-%E6%92%90%E8%B5%B7-19-%E8%AA%9E%E8%A8%80-call-graph-%E7%9A%84-mcp/">codegraph</a>）做日常結構查詢。三者怎麼分工見 <a href="/blog/record/%E4%B8%89-mcp-%E5%B7%A5%E4%BD%9C%E6%B5%81%E8%88%87-dart-%E5%AF%A6%E6%B8%ACcbm-/-codegraph-/-serena-%E7%9A%84%E8%81%B7%E8%B2%AC%E5%88%86%E5%B7%A5%E8%88%87%E4%B8%89%E5%88%80%E6%B5%81/">三 MCP 工作流與 Dart 實測</a>。</p>
<h2 id="結論">結論</h2>
<p>cbm 的核心價值在三件事：<strong>單 binary 部署</strong>、<strong>11-signal 語意搜尋</strong>、<strong>跨 service HTTP/RPC 鏈接</strong>。前兩件對任何語言都成立，第三件對微服務 monorepo 特別有意義。</p>
<p>它的能力上限被 hybrid type resolution 的語言名單卡死——名單內等於準 LSP，名單外只是個結構抽取器。評估時第一個要問的問題是：「我的主力語言在不在那五個（Go / C / C++ / TS / JS）？」答案決定 cbm 是主刀還是輔刀。</p>
]]></content:encoded></item><item><title>codegraph：用 tree-sitter per-language query 撐起 19+ 語言 call graph 的 MCP</title><link>https://tarrragon.github.io/blog/record/codegraph%E7%94%A8-tree-sitter-per-language-query-%E6%92%90%E8%B5%B7-19-%E8%AA%9E%E8%A8%80-call-graph-%E7%9A%84-mcp/</link><pubDate>Mon, 25 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/codegraph%E7%94%A8-tree-sitter-per-language-query-%E6%92%90%E8%B5%B7-19-%E8%AA%9E%E8%A8%80-call-graph-%E7%9A%84-mcp/</guid><description>&lt;h2 id="這個-mcp-解什麼問題">這個 MCP 解什麼問題&lt;/h2>
&lt;p>codegraph 的設計動機很具體：&lt;strong>Claude Code 探索 codebase 時 spawn 的 Explore agent 會用 grep / glob / read 連續刷檔，每個 tool call 都吃 token&lt;/strong>。codegraph 把這層探索預先做好，agent 直接查預建好的 graph。&lt;/p>
&lt;blockquote>
&lt;p>When Claude Code explores a codebase, it spawns Explore agents that scan files with grep, glob, and Read — consuming tokens on every tool call. CodeGraph gives those agents a pre-indexed knowledge graph — symbol relationships, call graphs, and code structure.&lt;/p>&lt;/blockquote>
&lt;p>跟 &lt;a href="https://tarrragon.github.io/blog/record/codebase-memory-mcp155-%E8%AA%9E%E8%A8%80-tree-sitter-%E7%9F%A5%E8%AD%98%E5%9C%96%E8%AD%9C-mcp-%E7%9A%84%E8%83%BD%E5%8A%9B%E8%88%87%E9%82%8A%E7%95%8C/">cbm&lt;/a> 比，codegraph 的 scope 更窄、更專注：不做跨 service 鏈接、不做 ADR / runtime trace 管理、不做 11-signal 語意搜尋，&lt;strong>只把 call graph 跟 symbol relationship 做好&lt;/strong>。這個取捨讓它的 MCP tool 只有 10 個、每個責任都很單一。&lt;/p>
&lt;h2 id="技術架構tree-sitter--per-language-query--fts5">技術架構：tree-sitter + per-language query + FTS5&lt;/h2>
&lt;p>codegraph 的核心 pipeline：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">tree-sitter parse → per-language query 抽 nodes/edges
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl"> → 解析 reference（import / extends / implements / calls）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl"> → 寫進 SQLite + FTS5&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>關鍵設計：&lt;strong>對每個語言寫專屬的 tree-sitter query&lt;/strong>——比起通用 AST visitor 路線、這個設計能對特定語言的 dispatch pattern 抽到更精確的 node 跟 edge。&lt;/p>
&lt;blockquote>
&lt;p>Language-specific queries extract nodes (functions, classes, methods) and edges (calls, imports, extends, implements).&lt;/p>&lt;/blockquote>
&lt;p>這個設計選擇直接決定了 codegraph 對非主流語言（如 Dart / Svelte / Liquid）的支援深度——因為每個語言都有專屬 query，所以 19+ 語言裡的 Dart 真的有 working call graph，不像純 tree-sitter wrapper 那樣只能抽結構。&lt;/p>
&lt;p>實際支援的 19+ 語言：&lt;/p>
&lt;p>TypeScript、JavaScript、Python、Go、Rust、Java、C#、PHP、Ruby、C、C++、Swift、Kotlin、Scala、Dart、Svelte、Vue、Liquid、Lua、Luau、Pascal/Delphi。&lt;/p>
&lt;p>過濾規則：「&lt;strong>Files larger than 1 MB are skipped&lt;/strong>」（generated bundle / minified JS / vendored blob 自動忽略）。&lt;/p>
&lt;h2 id="auto-syncnative-os-file-watcher--2s-debounce">Auto-sync：native OS file watcher + 2s debounce&lt;/h2>
&lt;p>codegraph 預設啟用 file watcher、用 native OS 事件（macOS FSEvents / Linux inotify / Windows ReadDirectoryChanges）：&lt;/p></description><content:encoded><![CDATA[<h2 id="這個-mcp-解什麼問題">這個 MCP 解什麼問題</h2>
<p>codegraph 的設計動機很具體：<strong>Claude Code 探索 codebase 時 spawn 的 Explore agent 會用 grep / glob / read 連續刷檔，每個 tool call 都吃 token</strong>。codegraph 把這層探索預先做好，agent 直接查預建好的 graph。</p>
<blockquote>
<p>When Claude Code explores a codebase, it spawns Explore agents that scan files with grep, glob, and Read — consuming tokens on every tool call. CodeGraph gives those agents a pre-indexed knowledge graph — symbol relationships, call graphs, and code structure.</p></blockquote>
<p>跟 <a href="/blog/record/codebase-memory-mcp155-%E8%AA%9E%E8%A8%80-tree-sitter-%E7%9F%A5%E8%AD%98%E5%9C%96%E8%AD%9C-mcp-%E7%9A%84%E8%83%BD%E5%8A%9B%E8%88%87%E9%82%8A%E7%95%8C/">cbm</a> 比，codegraph 的 scope 更窄、更專注：不做跨 service 鏈接、不做 ADR / runtime trace 管理、不做 11-signal 語意搜尋，<strong>只把 call graph 跟 symbol relationship 做好</strong>。這個取捨讓它的 MCP tool 只有 10 個、每個責任都很單一。</p>
<h2 id="技術架構tree-sitter--per-language-query--fts5">技術架構：tree-sitter + per-language query + FTS5</h2>
<p>codegraph 的核心 pipeline：</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">tree-sitter parse → per-language query 抽 nodes/edges
</span></span><span class="line"><span class="ln">2</span><span class="cl">                  → 解析 reference（import / extends / implements / calls）
</span></span><span class="line"><span class="ln">3</span><span class="cl">                  → 寫進 SQLite + FTS5</span></span></code></pre></div><p>關鍵設計：<strong>對每個語言寫專屬的 tree-sitter query</strong>——比起通用 AST visitor 路線、這個設計能對特定語言的 dispatch pattern 抽到更精確的 node 跟 edge。</p>
<blockquote>
<p>Language-specific queries extract nodes (functions, classes, methods) and edges (calls, imports, extends, implements).</p></blockquote>
<p>這個設計選擇直接決定了 codegraph 對非主流語言（如 Dart / Svelte / Liquid）的支援深度——因為每個語言都有專屬 query，所以 19+ 語言裡的 Dart 真的有 working call graph，不像純 tree-sitter wrapper 那樣只能抽結構。</p>
<p>實際支援的 19+ 語言：</p>
<p>TypeScript、JavaScript、Python、Go、Rust、Java、C#、PHP、Ruby、C、C++、Swift、Kotlin、Scala、Dart、Svelte、Vue、Liquid、Lua、Luau、Pascal/Delphi。</p>
<p>過濾規則：「<strong>Files larger than 1 MB are skipped</strong>」（generated bundle / minified JS / vendored blob 自動忽略）。</p>
<h2 id="auto-syncnative-os-file-watcher--2s-debounce">Auto-sync：native OS file watcher + 2s debounce</h2>
<p>codegraph 預設啟用 file watcher、用 native OS 事件（macOS FSEvents / Linux inotify / Windows ReadDirectoryChanges）：</p>
<ul>
<li>Debounce window：2 秒（避免快速連續存檔重複觸發）</li>
<li>過濾範圍：只看 source 檔案（按副檔名）</li>
<li>行為描述：「<strong>Incremental sync. The graph stays current as you code — no configuration needed</strong>」</li>
</ul>
<p>這層比 <a href="/blog/record/codebase-memory-mcp155-%E8%AA%9E%E8%A8%80-tree-sitter-%E7%9F%A5%E8%AD%98%E5%9C%96%E8%AD%9C-mcp-%E7%9A%84%E8%83%BD%E5%8A%9B%E8%88%87%E9%82%8A%E7%95%8C/">cbm</a> 的「背景 git polling」更貼近 IDE — 改完檔案 2 秒內 graph 就同步好，「邊改邊問」工作流更順。</p>
<p>判讀訊號：剛存完檔立刻問 caller 還是漏，等 3 秒再試一次；持續漏的話跑 <code>codegraph status</code> 看 indexed 數字對不對得上預期。</p>
<h2 id="call-graph-抽取的能力與聲稱">Call graph 抽取的能力與聲稱</h2>
<p>codegraph 對 caller / callee / impact / trace 這四個查詢的覆蓋是它的主賣點。README 對 <code>codegraph_trace</code> 的聲稱是：</p>
<blockquote>
<p>Follow dynamic-dispatch hops (callbacks, React re-render, interface→impl) that grep can&rsquo;t.</p></blockquote>
<p>實際機制 README 沒詳細寫，從 source 推測是「<strong>對某些常見動態 dispatch pattern 寫了專屬 query</strong>」——比如 React component 的 JSX → component definition 解析、interface method → implementation 對應這類。</p>
<p>這個 claim 在實測上<strong>有但有限</strong>——對 type-inferred receiver 仍會漏。例如 Dart 上（<code>Money</code> 在該專案是 extension type）：</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="kd">final</span> <span class="n">Money</span> <span class="n">samplePrice</span> <span class="o">=</span> <span class="p">...;</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">samplePrice</span><span class="p">.</span><span class="n">multiplyByRate</span><span class="p">(</span><span class="n">rate</span><span class="p">);</span>   <span class="o">//</span> <span class="err">←</span> <span class="n">codegraph</span> <span class="err">抽不到這條</span> <span class="n">edge</span></span></span></code></pre></div><p><code>samplePrice</code> 是 local variable，要做型別推斷才知道 receiver 是 <code>Money</code>。tree-sitter 看到的只是 <code>&lt;identifier&gt;.multiplyByRate(...)</code>、不知道 <code>samplePrice</code> 的型別、無法 dispatch 到 <code>Money.multiplyByRate</code>。</p>
<p>判讀訊號：<strong>對「靠型別解析才能找到的 callsite」會漏</strong>。如果專案大量使用 generics、type aliasing、factory pattern 隱藏型別、duck typing，codegraph 的 caller 數字會系統性偏低。重要 refactor 別只看它的數字決策。</p>
<p>下一步路由：實測對照數字見 <a href="/blog/record/%E4%B8%89-mcp-%E5%B7%A5%E4%BD%9C%E6%B5%81%E8%88%87-dart-%E5%AF%A6%E6%B8%ACcbm-/-codegraph-/-serena-%E7%9A%84%E8%81%B7%E8%B2%AC%E5%88%86%E5%B7%A5%E8%88%87%E4%B8%89%E5%88%80%E6%B5%81/">三 MCP 工作流與 Dart 實測</a>。</p>
<h2 id="caller-跟-callsite-的計數單位差異">Caller 跟 callsite 的計數單位差異</h2>
<p>codegraph 的 <code>codegraph_callers</code> 採用的計數單位是「<strong>caller symbol 數</strong>」（同一個 method 內呼叫目標兩次仍然只算 1 個 caller）——跟「callsite 數」屬於兩種不同的統計方式。</p>
<p>這個設計的影響：跟 LSP-based 工具（如 <a href="/blog/record/serena%E6%8A%8A-lsp-%E5%8C%85%E6%88%90-agent-first-mcp-%E7%9A%84-symbol-level-%E7%B7%A8%E8%BC%AF%E6%96%B9%E6%A1%88/">serena</a>）對比時，數字會看起來「少」，但這是計數規則的差異、跟精度差距屬於兩個不同議題。寫實測 baseline 時要把這個單位寫死，避免「codegraph 回 3、serena 回 9」被誤判成「codegraph 漏 6 個」。</p>
<p>實際上這 3 vs 9 的差距要分兩段看：codegraph 抓到的 3 個 caller symbol 對應 6 個 callsite（同一個 method 內有多處呼叫、被計數規則合併成 1 caller）、剩下的 3 個 callsite 在第 4 個檔案（<code>product.dart</code>）、是真的漏（type-inferred dispatch）。算術：6 callsite（codegraph 算 3 caller）+ 3 callsite（真的漏）= serena 的 9。要拆開看才知道哪部分是計數差異、哪部分是能力差距。</p>
<h2 id="14-web-framework-的-route-識別">14 web framework 的 route 識別</h2>
<p>codegraph 內建對 web framework 的 route 識別：</p>
<p>Django、Flask、FastAPI、Express、NestJS、Laravel、Drupal、Rails、Spring、Gin / chi / gorilla / mux、Axum / actix / Rocket、ASP.NET、Vapor、React Router、SvelteKit。</p>
<p>README 標稱「14 個」、實際展開後是 15 條（Gin / chi / gorilla / mux 跟 Axum / actix / Rocket 各算一組路由生態）。這個小落差源自分組計數方式、不影響功能。</p>
<p>這層的角色是讓 <code>codegraph_search</code> 能用 URL pattern 找到對應 handler，不必去猜 handler 函式名。但跟 <a href="/blog/record/codebase-memory-mcp155-%E8%AA%9E%E8%A8%80-tree-sitter-%E7%9F%A5%E8%AD%98%E5%9C%96%E8%AD%9C-mcp-%E7%9A%84%E8%83%BD%E5%8A%9B%E8%88%87%E9%82%8A%E7%95%8C/">cbm</a> 的 first-class HTTP_CALLS edge 不一樣，codegraph 沒做「client URL 字面值 → server route 比對」，所以<strong>單一 service 內找 handler 可以、跨 service 鏈接做不到</strong>。</p>
<p>判讀訊號：純前端 / 純後端 repo 上這層夠用；要跨 service 追 cross-service call 仍要靠 cbm 或別的工具。</p>
<h2 id="10-個-mcp-tool-的責任分工">10 個 MCP tool 的責任分工</h2>
<table>
  <thead>
      <tr>
          <th>Tool</th>
          <th>責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>codegraph_search</code></td>
          <td>用名稱 / pattern 找 symbol</td>
      </tr>
      <tr>
          <td><code>codegraph_context</code></td>
          <td>給定 task，組出相關 code context</td>
      </tr>
      <tr>
          <td><code>codegraph_trace</code></td>
          <td>兩個 symbol 之間的 call path、每跳含 body</td>
      </tr>
      <tr>
          <td><code>codegraph_callers</code></td>
          <td>找誰呼叫了 X（一跳）</td>
      </tr>
      <tr>
          <td><code>codegraph_callees</code></td>
          <td>找 X 呼叫了誰（一跳）</td>
      </tr>
      <tr>
          <td><code>codegraph_impact</code></td>
          <td>改 X 會影響什麼（blast radius）</td>
      </tr>
      <tr>
          <td><code>codegraph_node</code></td>
          <td>取 symbol 詳情 + 原始碼</td>
      </tr>
      <tr>
          <td><code>codegraph_explore</code></td>
          <td>一次回多個相關 symbol 的原始碼</td>
      </tr>
      <tr>
          <td><code>codegraph_files</code></td>
          <td>已索引的檔案結構</td>
      </tr>
      <tr>
          <td><code>codegraph_status</code></td>
          <td>索引健康度跟統計</td>
      </tr>
  </tbody>
</table>
<p>設計上有四個值得單獨展開的 tool：</p>
<p><strong><code>codegraph_explore</code></strong> 是為了<strong>省 tool call</strong> — 不用對 N 個 symbol 各呼叫一次 <code>codegraph_node</code>、一次拿到所有 source。這直接呼應 codegraph 整體「省 token / 省 tool call」的設計目標。</p>
<p><strong><code>codegraph_trace</code></strong> <strong>單一 call 涵蓋整個路徑</strong>、每一跳的 function body 直接 inline 在結果裡。對「X 怎麼影響到 Y」這種多跳問題，傳統做法要 N 次 <code>codegraph_callers</code> + N 次 <code>codegraph_node</code>，trace 把這壓成 1 次。代價是若兩個 symbol 之間沒有 static-resolvable 路徑（如 type-inferred dispatch 中斷），會直接回「No direct path」、不會主動找替代解釋。</p>
<p><strong><code>codegraph_context</code></strong> 跟 <code>codegraph_explore</code> 的責任差別常被搞混。<code>codegraph_explore</code> 是「我已經知道要看哪幾個 symbol」、一次拿原始碼；<code>codegraph_context</code> 是「我有個 task description、不知道相關 symbol 是哪些」、由它從 task 內容拉出可能相關的 graph 鄰域。前者是「精確檢索」、後者是「概念性彙整」。實務上 task agent 開新任務時用 <code>codegraph_context</code>、debug 細節時用 <code>codegraph_explore</code>。</p>
<p><strong><code>codegraph_impact</code></strong> 是 blast radius 工具、但<strong>它的精度被 tree-sitter syntactic 限制卡住</strong>——跟 caller / callee 同源、type-inferred dispatch 的影響範圍會漏。實務影響：對「rename method 會影響什麼」這類重要 refactor 不能單看它的數字、要走 LSP 工具 cross-check。判讀訊號：<code>codegraph_impact X</code> 回的 affected symbol 數明顯少於預期、且 X 是被廣泛使用的 type / method 時、blast radius 多半有漏、要補 LSP 驗證。</p>
<h2 id="token-efficiency-benchmark方法論與限制">Token efficiency benchmark：方法論與限制</h2>
<p>README 聲稱「<strong>~35% cheaper · ~70% fewer tool calls · 100% local</strong>」、median 跨 7 codebase：</p>
<ul>
<li>Cost: 35% reduction</li>
<li>Tokens: 57% fewer</li>
<li>Time: 46% faster</li>
<li>Tool calls: 71% fewer</li>
</ul>
<p>方法論：</p>
<blockquote>
<p>Claude Opus 4.7 run headlessly. WITH = CodeGraph&rsquo;s MCP server enabled, WITHOUT = empty MCP config. Same question per repo, 4 runs per arm, median reported.</p></blockquote>
<p>7 個 benchmark codebase：</p>
<table>
  <thead>
      <tr>
          <th>Repo</th>
          <th>語言</th>
          <th>規模</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>VS Code</td>
          <td>TypeScript</td>
          <td>~10k 檔</td>
      </tr>
      <tr>
          <td>Excalidraw</td>
          <td>TypeScript</td>
          <td>~640 檔</td>
      </tr>
      <tr>
          <td>Django</td>
          <td>Python</td>
          <td>~3k 檔</td>
      </tr>
      <tr>
          <td>Tokio</td>
          <td>Rust</td>
          <td>~790 檔</td>
      </tr>
      <tr>
          <td>OkHttp</td>
          <td>Java</td>
          <td>~645 檔</td>
      </tr>
      <tr>
          <td>Gin</td>
          <td>Go</td>
          <td>~110 檔</td>
      </tr>
      <tr>
          <td>Alamofire</td>
          <td>Swift</td>
          <td>~110 檔</td>
      </tr>
  </tbody>
</table>
<p>幾個要注意的解讀偏差：</p>
<p><strong>Benchmark 集中在 codegraph 強項語言</strong>。VS Code / Django / Tokio 都是 codegraph 的核心支援語言、且 LSP 生態成熟。Dart / Svelte / Liquid 這類 long-tail 語言沒列在 benchmark 內，token 節省效果在那些語言上是否成立不知道。</p>
<p><strong>Empty MCP config 的對照組不一定貼近實務</strong>。沒裝任何 MCP 時 agent 的 baseline 探索行為跟「裝了其他 MCP」不同。實務 stack 通常多個 MCP 並用，這個 35% 對「加裝 codegraph 進已有 MCP stack」的邊際效益會打折。</p>
<p>判讀訊號：benchmark 數字當「值得試」的參考、不當「裝了就省 35%」的硬保證。實際省多少要在自己的 stack 上跑同樣 question set 才知。</p>
<h2 id="安裝行為">安裝行為</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">npm i -g @colbymchenry/codegraph
</span></span><span class="line"><span class="ln">2</span><span class="cl">codegraph install --target claude --location global -y
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nb">cd</span> your-project <span class="o">&amp;&amp;</span> codegraph init -i</span></span></code></pre></div><p><code>codegraph install</code> 會把 MCP server 條目寫進 <code>~/.claude.json</code> 的 <code>mcpServers</code>、<code>codegraph init -i</code> 在當前 repo 建 <code>.codegraph/codegraph.db</code>、啟動 watcher。</p>
<p>跟 <a href="/blog/record/codebase-memory-mcp155-%E8%AA%9E%E8%A8%80-tree-sitter-%E7%9F%A5%E8%AD%98%E5%9C%96%E8%AD%9C-mcp-%E7%9A%84%E8%83%BD%E5%8A%9B%E8%88%87%E9%82%8A%E7%95%8C/">cbm</a> 不一樣：codegraph <strong>不寫 PreToolUse hook</strong>、不攔截 Grep/Glob。它純粹當 MCP server 提供 tool、決策權留給 agent，對既有工作流的干擾較小。</p>
<p>CLI mode 是另一個方便點：所有 MCP tool 在 CLI 都有對應指令（<code>codegraph callers X</code> / <code>codegraph trace X Y</code>），不必等 Claude Code 重啟載入 MCP 就能先在 terminal 驗證效果。</p>
<h2 id="適用--不適用情境的判讀">適用 / 不適用情境的判讀</h2>
<p><strong>適用情境</strong>：</p>
<ul>
<li>主力語言在 19+ 支援列表內，且需要可靠的 caller / impact / trace 查詢</li>
<li>「邊改邊問」工作流（auto-sync 2s debounce 比較貼近 IDE）</li>
<li>希望 MCP 保持原生 grep / glob 行為、把決策權留給 agent 自主判斷（避開 hook 層強制介入）</li>
<li>要 CLI 跟 MCP 雙管道使用（CLI 可先試、MCP 給 agent 用）</li>
</ul>
<p><strong>不適用情境</strong>：</p>
<ul>
<li>語言不在支援列表（codegraph 不像 cbm 一次 vendor 155 個 grammar）</li>
<li>需要跨 service 的 client URL → server route 鏈接（codegraph 只認 route definition）</li>
<li>需要 symbol-level atomic edit（codegraph 純讀、沒 rename / replace_symbol_body）</li>
<li>重要 refactor 要保證不漏 callsite（tree-sitter syntactic 上限會漏 type-inferred dispatch）</li>
</ul>
<p><strong>搭配建議</strong>：對 type-inferred dispatch 漏的部分，可以靠 LSP-based 工具（如 <a href="/blog/record/serena%E6%8A%8A-lsp-%E5%8C%85%E6%88%90-agent-first-mcp-%E7%9A%84-symbol-level-%E7%B7%A8%E8%BC%AF%E6%96%B9%E6%A1%88/">serena</a>）補位。對概念性自然語言搜尋，<a href="/blog/record/codebase-memory-mcp155-%E8%AA%9E%E8%A8%80-tree-sitter-%E7%9F%A5%E8%AD%98%E5%9C%96%E8%AD%9C-mcp-%E7%9A%84%E8%83%BD%E5%8A%9B%E8%88%87%E9%82%8A%E7%95%8C/">cbm</a> 的 11-signal scoring 比 codegraph 的 symbol pattern match 更強。三者怎麼分工見 <a href="/blog/record/%E4%B8%89-mcp-%E5%B7%A5%E4%BD%9C%E6%B5%81%E8%88%87-dart-%E5%AF%A6%E6%B8%ACcbm-/-codegraph-/-serena-%E7%9A%84%E8%81%B7%E8%B2%AC%E5%88%86%E5%B7%A5%E8%88%87%E4%B8%89%E5%88%80%E6%B5%81/">三 MCP 工作流與 Dart 實測</a>。</p>
<h2 id="結論">結論</h2>
<p>codegraph 的核心價值是<strong>用 per-language tree-sitter query 把 call graph 做成 19+ 語言通用的 MCP 服務</strong>，加上 auto-sync 跟 CLI 雙管道。它的 scope 聚焦在 call graph、比 cbm 窄很多、但聚焦範圍內品質很高。</p>
<p>它的型別解析靠 tree-sitter syntactic：<strong>receiver 是顯式型別宣告或 literal 的 callsite 解得好、receiver 要靠型別推斷的 callsite 會漏</strong>。判斷 codegraph 在自己專案上的可信度，先估專案有多少比例的 call 是 type-inferred receiver——比例高就要配 LSP 工具補位、比例低就放心用。</p>
]]></content:encoded></item><item><title>serena：把 LSP 包成 agent-first MCP 的 symbol-level 編輯方案</title><link>https://tarrragon.github.io/blog/record/serena%E6%8A%8A-lsp-%E5%8C%85%E6%88%90-agent-first-mcp-%E7%9A%84-symbol-level-%E7%B7%A8%E8%BC%AF%E6%96%B9%E6%A1%88/</link><pubDate>Mon, 25 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/serena%E6%8A%8A-lsp-%E5%8C%85%E6%88%90-agent-first-mcp-%E7%9A%84-symbol-level-%E7%B7%A8%E8%BC%AF%E6%96%B9%E6%A1%88/</guid><description>&lt;h2 id="這個-mcp-解什麼問題">這個 MCP 解什麼問題&lt;/h2>
&lt;p>serena 的核心定位是「&lt;strong>把現成 LSP 生態包成適合 agent 用的高階抽象&lt;/strong>」。它不自建 type system、不自寫 parser，直接 spawn 各語言對應的 language server（Dart 用 &lt;code>dart analysis_server&lt;/code>、TS 用 &lt;code>tsserver&lt;/code>、Rust 用 &lt;code>rust-analyzer&lt;/code> 等），把 LSP 的能力轉成 MCP tool。&lt;/p>
&lt;p>設計哲學是 README 自己歸納的「agent-first tool design」：&lt;/p>
&lt;blockquote>
&lt;p>Involves robust high-level abstractions, distinguishing it from approaches that rely on low-level concepts like line numbers or primitive search patterns.&lt;/p>&lt;/blockquote>
&lt;p>換言之，serena 的所有編輯都是 &lt;strong>symbol-level&lt;/strong>——讓 agent 直接用 symbol 語意操作（「把 X function 的 body 整個換掉」、「在 Y class 後面插一段」、「rename Z」），跳過 line number 跟 text patch 這層 raw text 處理。對應的是 LSP 路線本來就有的 symbol 結構與 reference 追蹤。&lt;/p>
&lt;p>跟 tree-sitter 路線的本質分野：tree-sitter 只給結構、不給型別；LSP 給的是「IDE 等級的真型別系統」。代價是 LSP 要每個語言裝對應 language server、執行期 spawn process、per-session 維護狀態。&lt;/p>
&lt;h2 id="部署形態兩個-backend執行期-spawn-lsp">部署形態：兩個 backend、執行期 spawn LSP&lt;/h2>
&lt;p>serena 提供兩個 backend：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Backend&lt;/th>
 &lt;th>適用情境&lt;/th>
 &lt;th>取捨&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Language Server&lt;/td>
 &lt;td>預設、開源、跨平台&lt;/td>
 &lt;td>要對應語言的 language server 在環境內&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>JetBrains Plugin&lt;/td>
 &lt;td>已用 JetBrains IDE 的 paid 使用者&lt;/td>
 &lt;td>借用 IDE 完整能力（debug / breakpoint）&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Language Server backend 是 OSS 用戶會接觸的路線。serena 透過 LSP 抽象涵蓋 40+ 語言、實際能力依各語言 LSP 成熟度而定——Python / TypeScript / Go / Rust / Java / C# / Dart 等主流語言由 serena 內建 bootstrap 自動下載 server、冷門語言（如 Liquid / Pascal）需要使用者自己準備 server binary、無 server 的語言視同 fallback 到純文字工具。判讀訊號：跑 &lt;code>activate_project&lt;/code> 後若 serena 沒在背景 spawn 對應 LSP、表示該語言走 fallback 路線、&lt;code>find_referencing_symbols&lt;/code> 等型別敏感 tool 不可用。&lt;/p>
&lt;p>對 Dart 而言：serena 啟動時 spawn &lt;code>dart analysis_server&lt;/code>、跟 Flutter SDK 內附的同一隻。所以 serena 對 Dart 的能力等同 &lt;code>dart analysis_server&lt;/code> 暴露的能力——比 tree-sitter 路線高一個量級。&lt;/p>
&lt;h2 id="per-session-模型與-activate_project">Per-session 模型與 activate_project&lt;/h2>
&lt;p>serena 的 LSP backend 是 &lt;strong>per-session&lt;/strong> 的：&lt;/p></description><content:encoded><![CDATA[<h2 id="這個-mcp-解什麼問題">這個 MCP 解什麼問題</h2>
<p>serena 的核心定位是「<strong>把現成 LSP 生態包成適合 agent 用的高階抽象</strong>」。它不自建 type system、不自寫 parser，直接 spawn 各語言對應的 language server（Dart 用 <code>dart analysis_server</code>、TS 用 <code>tsserver</code>、Rust 用 <code>rust-analyzer</code> 等），把 LSP 的能力轉成 MCP tool。</p>
<p>設計哲學是 README 自己歸納的「agent-first tool design」：</p>
<blockquote>
<p>Involves robust high-level abstractions, distinguishing it from approaches that rely on low-level concepts like line numbers or primitive search patterns.</p></blockquote>
<p>換言之，serena 的所有編輯都是 <strong>symbol-level</strong>——讓 agent 直接用 symbol 語意操作（「把 X function 的 body 整個換掉」、「在 Y class 後面插一段」、「rename Z」），跳過 line number 跟 text patch 這層 raw text 處理。對應的是 LSP 路線本來就有的 symbol 結構與 reference 追蹤。</p>
<p>跟 tree-sitter 路線的本質分野：tree-sitter 只給結構、不給型別；LSP 給的是「IDE 等級的真型別系統」。代價是 LSP 要每個語言裝對應 language server、執行期 spawn process、per-session 維護狀態。</p>
<h2 id="部署形態兩個-backend執行期-spawn-lsp">部署形態：兩個 backend、執行期 spawn LSP</h2>
<p>serena 提供兩個 backend：</p>
<table>
  <thead>
      <tr>
          <th>Backend</th>
          <th>適用情境</th>
          <th>取捨</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Language Server</td>
          <td>預設、開源、跨平台</td>
          <td>要對應語言的 language server 在環境內</td>
      </tr>
      <tr>
          <td>JetBrains Plugin</td>
          <td>已用 JetBrains IDE 的 paid 使用者</td>
          <td>借用 IDE 完整能力（debug / breakpoint）</td>
      </tr>
  </tbody>
</table>
<p>Language Server backend 是 OSS 用戶會接觸的路線。serena 透過 LSP 抽象涵蓋 40+ 語言、實際能力依各語言 LSP 成熟度而定——Python / TypeScript / Go / Rust / Java / C# / Dart 等主流語言由 serena 內建 bootstrap 自動下載 server、冷門語言（如 Liquid / Pascal）需要使用者自己準備 server binary、無 server 的語言視同 fallback 到純文字工具。判讀訊號：跑 <code>activate_project</code> 後若 serena 沒在背景 spawn 對應 LSP、表示該語言走 fallback 路線、<code>find_referencing_symbols</code> 等型別敏感 tool 不可用。</p>
<p>對 Dart 而言：serena 啟動時 spawn <code>dart analysis_server</code>、跟 Flutter SDK 內附的同一隻。所以 serena 對 Dart 的能力等同 <code>dart analysis_server</code> 暴露的能力——比 tree-sitter 路線高一個量級。</p>
<h2 id="per-session-模型與-activate_project">Per-session 模型與 activate_project</h2>
<p>serena 的 LSP backend 是 <strong>per-session</strong> 的：</p>
<ul>
<li>沒有持久化 graph DB（不像 <a href="/blog/record/codebase-memory-mcp155-%E8%AA%9E%E8%A8%80-tree-sitter-%E7%9F%A5%E8%AD%98%E5%9C%96%E8%AD%9C-mcp-%E7%9A%84%E8%83%BD%E5%8A%9B%E8%88%87%E9%82%8A%E7%95%8C/">cbm</a> / <a href="/blog/record/codegraph%E7%94%A8-tree-sitter-per-language-query-%E6%92%90%E8%B5%B7-19-%E8%AA%9E%E8%A8%80-call-graph-%E7%9A%84-mcp/">codegraph</a> 把結果寫進 SQLite）</li>
<li>每個 session 啟動時要 <code>activate_project</code>、spawn 對應 language server、warm up index</li>
<li>Session 結束 server 也跟著 terminate，下次重來</li>
</ul>
<p><code>activate_project</code> 的角色是告訴 serena「這個 session 接下來要分析哪個 project root」，serena 才知道要 spawn 哪幾個 language server、index 哪個 workspace。一個 session 內可以切多次 project，但同時只 active 一個。</p>
<p>這個模型的取捨很清楚：</p>
<ul>
<li><strong>好處</strong>：永遠拿到當下最新狀態（不會有 stale index 問題）、不必管 watcher / debounce</li>
<li><strong>代價</strong>：每次 session warm-up 有秒級至分鐘級延遲（大專案 LSP indexing 慢）、跨 session 不能重用結果</li>
</ul>
<p>判讀訊號：第一次查詢回得慢、之後快——這是 LSP indexing warm-up。若每次查都慢、檢查 LSP 是否因記憶體不足重啟。</p>
<h2 id="symbol-level-atomic-edit-的價值">Symbol-level atomic edit 的價值</h2>
<p>serena 的 editing tool 都是 symbol-level：</p>
<ul>
<li><code>replace_symbol_body</code>：取代某個 function / method / class 的 body</li>
<li><code>insert_after_symbol</code> / <code>insert_before_symbol</code>：在指定 symbol 前後插入內容</li>
<li><code>safe_delete_symbol</code>：刪除 symbol 並檢查 reference</li>
<li><code>rename_symbol</code>：rename symbol、自動更新所有 reference（LS backend 限 symbol 範圍、JetBrains backend 額外支援 file / directory 層級重命名）</li>
</ul>
<p>對比 <code>Edit</code> tool 用「old_string / new_string」做 text-level patch：</p>
<table>
  <thead>
      <tr>
          <th>操作</th>
          <th>text-level edit</th>
          <th>symbol-level edit</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>改 method body</td>
          <td>要 match 整個 body 含縮排與空白</td>
          <td>指定 method 名、給新 body</td>
      </tr>
      <tr>
          <td>Method body 內某行有特殊字元</td>
          <td>容易 escape 錯、match fail</td>
          <td>不受影響、agent 不處理 raw text</td>
      </tr>
      <tr>
          <td>同名 method 在多個 class</td>
          <td>要 match 含 class 名上下文</td>
          <td>用 <code>ClassName/methodName</code> 路徑唯一定位</td>
      </tr>
      <tr>
          <td>Rename 跨檔</td>
          <td>要全 repo grep + 逐檔 patch</td>
          <td>一次 call 完成 + LSP 保證 reference 全更新</td>
      </tr>
  </tbody>
</table>
<p>實務上的價值：<strong>type-sensitive refactor 的事故率大幅降低</strong>。改 method 不會手抖把 indentation 改錯、rename 不會漏改 reference。代價是 symbol 路徑必須寫成包含父層的完整形式（<code>ClassName/methodName</code>）。</p>
<p>判讀訊號：寫 <code>replace_symbol_body</code> 後若 LSP 報 syntax error、先 <code>get_diagnostics_for_file</code> 看具體錯在哪、別直接 retry 同個 patch。</p>
<h2 id="find_referencing_symbolslsp-路線的型別精確-caller-來源">find_referencing_symbols：LSP 路線的型別精確 caller 來源</h2>
<p>對 Dart / Swift / Kotlin 這類 tree-sitter 工具支援薄弱的語言，<code>find_referencing_symbols</code> 是少數能拿到「<strong>型別精確的 caller 清單</strong>」的 MCP tool。</p>
<p>實測對 Dart <code>Money.multiplyByRate</code>（某商業專案、<code>Money</code> 是 extension type）：</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">serena find_referencing_symbols → 4 個檔案、9 個 callsite
</span></span><span class="line"><span class="ln">2</span><span class="cl">codegraph callers              → 3 個 caller symbol（漏 3 個 callsite）
</span></span><span class="line"><span class="ln">3</span><span class="cl">cbm trace_call_path            → 0 callers（Dart 不在 hybrid resolution 名單）</span></span></code></pre></div><p>差距來源就是型別解析：<code>samplePrice.multiplyByRate(...)</code> 這種 receiver 是 local variable 的 callsite，要知道 <code>samplePrice</code> 的型別是 <code>Money</code> 才能 dispatch 到正確 method。LSP 走 <code>dart analysis_server</code> 拿到完整型別資訊，所以這層 dispatch 是精確的。</p>
<p>下一步路由：對照數字與 5 個實測實驗見 <a href="/blog/record/%E4%B8%89-mcp-%E5%B7%A5%E4%BD%9C%E6%B5%81%E8%88%87-dart-%E5%AF%A6%E6%B8%ACcbm-/-codegraph-/-serena-%E7%9A%84%E8%81%B7%E8%B2%AC%E5%88%86%E5%B7%A5%E8%88%87%E4%B8%89%E5%88%80%E6%B5%81/">三 MCP 工作流與 Dart 實測</a>。</p>
<h2 id="30-mcp-tool-的分類">30+ MCP tool 的分類</h2>
<p>serena 的 tool 數量比 cbm / codegraph 都多、覆蓋更廣的工作流：</p>
<table>
  <thead>
      <tr>
          <th>類別</th>
          <th>Tool</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>檢索</td>
          <td><code>find_symbol</code>、<code>get_symbols_overview</code>、<code>find_referencing_symbols</code>、<code>find_declaration</code>、<code>find_implementations</code>、<code>get_diagnostics_for_file</code></td>
      </tr>
      <tr>
          <td>編輯（symbol）</td>
          <td><code>replace_symbol_body</code>、<code>insert_after_symbol</code>、<code>insert_before_symbol</code>、<code>safe_delete_symbol</code>、<code>rename_symbol</code></td>
      </tr>
      <tr>
          <td>編輯（text）</td>
          <td><code>replace_content</code>、<code>search_for_pattern</code></td>
      </tr>
      <tr>
          <td>檔案 / 目錄</td>
          <td><code>list_dir</code>、<code>find_file</code>、<code>read_file</code>、<code>create_text_file</code></td>
      </tr>
      <tr>
          <td>執行</td>
          <td><code>execute_shell_command</code></td>
      </tr>
      <tr>
          <td>Memory</td>
          <td><code>write_memory</code>、<code>read_memory</code>、<code>list_memories</code>、<code>delete_memory</code>、<code>rename_memory</code>、<code>edit_memory</code></td>
      </tr>
      <tr>
          <td>Project</td>
          <td><code>activate_project</code>、<code>get_current_config</code>、<code>onboarding</code>、<code>initial_instructions</code></td>
      </tr>
      <tr>
          <td>Debug</td>
          <td>（僅 JetBrains backend）breakpoint、variable inspection、expression eval</td>
      </tr>
  </tbody>
</table>
<p>幾個值得單獨展開的類別：</p>
<p><strong>檢索類</strong>是 serena 跟 LSP 黏最緊的入口——<code>find_symbol</code> / <code>find_declaration</code> / <code>find_implementations</code> 走 LSP 的 textDocument 命令、<code>find_referencing_symbols</code> 是 LSP <code>references</code> 的 wrapper。這層是 serena 不可替代的核心、所有需要型別精確的查詢都從這走。</p>
<p><strong><code>get_diagnostics_for_file</code></strong> 是把 LSP 的編譯診斷直接暴露給 agent。改完 code 不必跑 build 就能知道有沒有 type error / unused import / missing await。對 type-sensitive refactor 是必備。</p>
<p><strong>Symbol-level edit vs text-level edit 的選用</strong>：symbol-level（<code>replace_symbol_body</code> / <code>insert_after_symbol</code> / <code>safe_delete_symbol</code> / <code>rename_symbol</code>）對「有明確 symbol 邊界的修改」最穩、不會踩到 indentation 或 escape 問題；text-level（<code>replace_content</code> / <code>search_for_pattern</code>）保留給「跨 symbol 邊界、或非 code 內容」的場合（如改 markdown、config、log 字串）。判讀訊號：要動的內容能不能用「ClassName/methodName」這種 symbol path 定位？能就走 symbol-level、不能就 text-level。</p>
<p><strong><code>execute_shell_command</code></strong> 是 LSP-only 工具裡的「逃生門」——LSP 本身不執行命令、但實務上 agent 需要跑 test / build / git status / 任意 CLI 工具來驗證自己的修改。這條等於把 LSP-based 工具補成「能 query 又能執行」的完整 workflow 工具。安全考量：因為它能跑任意 shell command、Claude Code 對 serena 的 trust level 要跟 Bash tool 對齊看待、不要假設它「只是讀取工具」。</p>
<p><strong>Memory system</strong> 採用「跨 session 的 markdown 筆記檔」形式、屬於自由格式存儲。用途接近 agent 的本地長期記憶——存「這個專案的 setup 注意事項」、「上次 refactor 的決策紀錄」、「常用的 codebase pattern」。跟 <a href="/blog/record/codebase-memory-mcp155-%E8%AA%9E%E8%A8%80-tree-sitter-%E7%9F%A5%E8%AD%98%E5%9C%96%E8%AD%9C-mcp-%E7%9A%84%E8%83%BD%E5%8A%9B%E8%88%87%E9%82%8A%E7%95%8C/">cbm</a> 的 <code>manage_adr</code>（結構化 ADR）走相反取向：serena 把 schema 留給使用者自定、manage_adr 給定 ADR 欄位結構。</p>
<p><strong>Project 類</strong>（<code>activate_project</code> / <code>get_current_config</code> / <code>onboarding</code> / <code>initial_instructions</code>）是 serena 對「agent 第一次接觸新專案要先讀什麼」的明確協議。<code>onboarding</code> 讓 agent 主動 read 專案 onboarding doc、<code>initial_instructions</code> 給 agent 一份 serena 自己的使用手冊、<code>activate_project</code> 切 project root、<code>get_current_config</code> 暴露當前 session 的配置給 agent debug。這層降低盲目探索成本、是把 serena 從「LSP wrapper」抬升到「agent-first」的關鍵。</p>
<h2 id="per-session-與持久化-graph-的搭配問題">Per-session 與持久化 graph 的搭配問題</h2>
<p>serena 的 per-session 模型在「<strong>單純查 caller / refactor</strong>」工作流很合適，但對「<strong>自然語言搜尋 / 跨 session 累積 graph context</strong>」就不夠。</p>
<p>實際差距：</p>
<ul>
<li>想用「金額顯示相關」這種概念性 query 找 symbol → serena 沒有 BM25 / 11-signal scoring、只有 <code>search_for_pattern</code>（regex / literal）跟 <code>find_symbol</code>（exact name match）</li>
<li>想跨 session 累積「這個 codebase 有哪些 module」的整體 inventory → serena 每次重 index、沒有持久化的 graph 可查</li>
<li>想做跨 service HTTP_CALLS 鏈接 → serena 沒有這層</li>
</ul>
<p>判讀訊號：搜尋需求若是「我知道某個 symbol 的精確名稱、要找它的 references」就用 serena；若是「我不知道精確名稱、用概念找」要配合 cbm。</p>
<h2 id="安裝行為">安裝行為</h2>
<p>serena 在 Claude Code 是 plugin 形式：在 plugin marketplace enable 即可，不需要單獨 <code>npm i</code>。Plugin 啟動時 serena 會 spawn LSP，第一次 activate 某個 project 時 indexing 完成才能跑 query。</p>
<p>跟 <a href="/blog/record/codebase-memory-mcp155-%E8%AA%9E%E8%A8%80-tree-sitter-%E7%9F%A5%E8%AD%98%E5%9C%96%E8%AD%9C-mcp-%E7%9A%84%E8%83%BD%E5%8A%9B%E8%88%87%E9%82%8A%E7%95%8C/">cbm</a> / <a href="/blog/record/codegraph%E7%94%A8-tree-sitter-per-language-query-%E6%92%90%E8%B5%B7-19-%E8%AA%9E%E8%A8%80-call-graph-%E7%9A%84-mcp/">codegraph</a> 的差異：</p>
<ul>
<li><strong>不寫 PreToolUse hook</strong>、不攔截既有 grep / glob 行為</li>
<li><strong>不在 <code>~/.claude.json</code> 直接加 mcpServers</strong>（plugin 機制管理）</li>
<li><strong>每個 project 要顯式 activate</strong>——第一次 session 進新 project 時 agent 要主動跑 <code>activate_project</code> 或在 plugin config 預設 project root</li>
</ul>
<p>要注意的點：</p>
<p><strong>Language server 缺失時的失敗模式</strong>。對冷門語言（如 Liquid / Pascal）若環境沒裝 language server、<code>activate_project</code> 會回失敗但不會主動裝。需要使用者自己準備 server binary。Dart / TS / Python / Go / Rust 等主流語言 serena 會 bootstrap 處理。</p>
<p><strong>JetBrains backend 是付費</strong>。OSS 用戶只能用 LS backend、得不到 debug 整合那組能力。</p>
<h2 id="適用--不適用情境的判讀">適用 / 不適用情境的判讀</h2>
<p><strong>適用情境</strong>：</p>
<ul>
<li>主力語言有成熟 LSP（Dart / TS / Python / Go / Rust / Java / C# 等）</li>
<li>型別敏感的 refactor 場景（rename / extract method / 跨檔 reference 更新）</li>
<li>要編譯 diagnostic 即時反饋（取代 build / test cycle 的部分功能）</li>
<li>Symbol-level atomic edit 的可靠性比 graph 持久化重要</li>
</ul>
<p><strong>不適用情境</strong>：</p>
<ul>
<li>主力語言 LSP 不成熟或不存在（serena 沒得借力）</li>
<li>需要概念性 / 自然語言搜尋（用 <a href="/blog/record/codebase-memory-mcp155-%E8%AA%9E%E8%A8%80-tree-sitter-%E7%9F%A5%E8%AD%98%E5%9C%96%E8%AD%9C-mcp-%E7%9A%84%E8%83%BD%E5%8A%9B%E8%88%87%E9%82%8A%E7%95%8C/">cbm</a> 的 11-signal scoring）</li>
<li>需要跨 session 累積的 graph context（serena per-session、不持久化）</li>
<li>需要跨 service HTTP/RPC 鏈接（serena 沒這層）</li>
</ul>
<p><strong>搭配建議</strong>：serena 是「<strong>型別精確 + 編輯出口</strong>」的角色。在它擅長的語言上做 caller 追蹤 / refactor、把概念性搜尋讓給 cbm、把日常結構查詢讓給 codegraph。三者怎麼分工見 <a href="/blog/record/%E4%B8%89-mcp-%E5%B7%A5%E4%BD%9C%E6%B5%81%E8%88%87-dart-%E5%AF%A6%E6%B8%ACcbm-/-codegraph-/-serena-%E7%9A%84%E8%81%B7%E8%B2%AC%E5%88%86%E5%B7%A5%E8%88%87%E4%B8%89%E5%88%80%E6%B5%81/">三 MCP 工作流與 Dart 實測</a>。</p>
<h2 id="結論">結論</h2>
<p>serena 的核心價值在三件事：<strong>直接借 LSP 拿型別精確的 reference</strong>、<strong>symbol-level atomic edit 的可靠性</strong>、<strong>編譯 diagnostic 即時整合</strong>。前兩件對任何成熟 LSP 語言都成立，第三件對「改完 code 想立刻驗 type error」的工作流特別重要。</p>
<p>它的能力上限取決於「<strong>目標語言 LSP 成熟度</strong>」——LSP 強的語言上 serena 是強工具、LSP 弱的語言上 serena 也跟著弱。它的能力下限取決於「<strong>持久化 graph 與自然語言搜尋</strong>」這兩層空白——這兩層要靠別的 MCP 補齊。</p>
]]></content:encoded></item><item><title>三 MCP 工作流與 Dart 實測：cbm / codegraph / serena 的職責分工與三刀流</title><link>https://tarrragon.github.io/blog/record/%E4%B8%89-mcp-%E5%B7%A5%E4%BD%9C%E6%B5%81%E8%88%87-dart-%E5%AF%A6%E6%B8%ACcbm-/-codegraph-/-serena-%E7%9A%84%E8%81%B7%E8%B2%AC%E5%88%86%E5%B7%A5%E8%88%87%E4%B8%89%E5%88%80%E6%B5%81/</link><pubDate>Mon, 25 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/%E4%B8%89-mcp-%E5%B7%A5%E4%BD%9C%E6%B5%81%E8%88%87-dart-%E5%AF%A6%E6%B8%ACcbm-/-codegraph-/-serena-%E7%9A%84%E8%81%B7%E8%B2%AC%E5%88%86%E5%B7%A5%E8%88%87%E4%B8%89%E5%88%80%E6%B5%81/</guid><description>&lt;h2 id="為什麼需要對照為什麼選-dart">為什麼需要對照、為什麼選 Dart&lt;/h2>
&lt;p>評估 code intelligence MCP 不能只看 README benchmark：每個工具的 benchmark 都選自己擅長的 codebase 跟語言，readme 數字只能參考、不能直接套到自家 stack。&lt;/p>
&lt;p>這次選一個 Dart 商業專案做對照場域有兩個理由：&lt;/p>
&lt;ul>
&lt;li>Dart 是三個工具的「中間地帶」——&lt;a href="https://tarrragon.github.io/blog/record/codebase-memory-mcp155-%E8%AA%9E%E8%A8%80-tree-sitter-%E7%9F%A5%E8%AD%98%E5%9C%96%E8%AD%9C-mcp-%E7%9A%84%E8%83%BD%E5%8A%9B%E8%88%87%E9%82%8A%E7%95%8C/">cbm&lt;/a> 不在 hybrid resolution 名單、&lt;a href="https://tarrragon.github.io/blog/record/codegraph%E7%94%A8-tree-sitter-per-language-query-%E6%92%90%E8%B5%B7-19-%E8%AA%9E%E8%A8%80-call-graph-%E7%9A%84-mcp/">codegraph&lt;/a> 列為 full support、&lt;a href="https://tarrragon.github.io/blog/record/serena%E6%8A%8A-lsp-%E5%8C%85%E6%88%90-agent-first-mcp-%E7%9A%84-symbol-level-%E7%B7%A8%E8%BC%AF%E6%96%B9%E6%A1%88/">serena&lt;/a> 借 &lt;code>dart analysis_server&lt;/code> 有完整 LSP。三條技術路線在同一語言上的能力差距會被最大化。&lt;/li>
&lt;li>Dart 大量用 extension type、generic、factory pattern，這些是 type-inferred dispatch 的高發場景，能逼出每個工具的真實精度差。&lt;/li>
&lt;/ul>
&lt;p>在 Go / TypeScript 上跑同樣對照，結論會反過來——cbm 的 hybrid resolution 在那裡會接近 LSP 精度，三刀流的必要性會降低。所以這篇結論限定「LSP 成熟但 cbm 不在 hybrid resolution 名單」的語言。&lt;/p>
&lt;h2 id="本質差異tree-sitter-syntactic-vs-lsp-type-aware">本質差異：tree-sitter syntactic vs LSP type-aware&lt;/h2>
&lt;p>三個工具在 Dart 上的能力差距，根源是兩條技術路線的本質落差：&lt;/p>
&lt;p>&lt;strong>tree-sitter syntactic&lt;/strong>：只看語法結構。看到 &lt;code>a.b()&lt;/code> 知道有個 method call、不知道 &lt;code>a&lt;/code> 是什麼型別、不知道 &lt;code>b()&lt;/code> 連到哪個 declaration。對 receiver 是 literal 或顯式型別宣告的 callsite 可以解、對 local variable / parameter / 推斷型別的 callsite 會漏。&lt;/p>
&lt;p>&lt;strong>LSP type-aware&lt;/strong>：走 language server 內建的型別推斷引擎。跟 IDE 用同一套後端、能解出 &lt;code>a&lt;/code> 的真實型別、再從 type declaration 找到對應的 method。所以 reference 是型別精確的。&lt;/p>
&lt;p>cbm 的 hybrid type resolution（限 Go / C / C++ / TS / JS）是把 LSP 的型別解析算法 clean-room 重寫進 binary、所以那幾個語言上 cbm 等於有 LSP 級精度但沒 LSP 依賴。Dart 沒得到這個待遇，所以 cbm 在 Dart 上只剩純 syntactic 結構抽取。&lt;/p>
&lt;p>判讀訊號：看一個工具對某語言的能力強弱，問「&lt;strong>它在這語言上做型別解析嗎？&lt;/strong>」——做的話接近 LSP，不做的話只是個結構抽取器。&lt;/p>
&lt;p>這個 framework 建立後、下節展開到 9 個維度的設計對照。&lt;/p>
&lt;h2 id="三個工具的設計差異對照">三個工具的設計差異對照&lt;/h2>
&lt;p>三個工具雖然都是「code intelligence MCP」，設計取向互補：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>維度&lt;/th>
 &lt;th>&lt;a href="https://tarrragon.github.io/blog/record/codebase-memory-mcp155-%E8%AA%9E%E8%A8%80-tree-sitter-%E7%9F%A5%E8%AD%98%E5%9C%96%E8%AD%9C-mcp-%E7%9A%84%E8%83%BD%E5%8A%9B%E8%88%87%E9%82%8A%E7%95%8C/">cbm&lt;/a>&lt;/th>
 &lt;th>&lt;a href="https://tarrragon.github.io/blog/record/codegraph%E7%94%A8-tree-sitter-per-language-query-%E6%92%90%E8%B5%B7-19-%E8%AA%9E%E8%A8%80-call-graph-%E7%9A%84-mcp/">codegraph&lt;/a>&lt;/th>
 &lt;th>&lt;a href="https://tarrragon.github.io/blog/record/serena%E6%8A%8A-lsp-%E5%8C%85%E6%88%90-agent-first-mcp-%E7%9A%84-symbol-level-%E7%B7%A8%E8%BC%AF%E6%96%B9%E6%A1%88/">serena&lt;/a>&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>解析後端&lt;/td>
 &lt;td>tree-sitter + 自寫 type resolver&lt;/td>
 &lt;td>tree-sitter + per-language query&lt;/td>
 &lt;td>LSP（per-language server）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>語言覆蓋&lt;/td>
 &lt;td>155（vendored grammar）&lt;/td>
 &lt;td>19+（每語言寫 query）&lt;/td>
 &lt;td>視 LSP 支援度（40+）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>持久化&lt;/td>
 &lt;td>SQLite + WAL（可 zstd 匯出為 team artifact）&lt;/td>
 &lt;td>SQLite + FTS5&lt;/td>
 &lt;td>per-session、不持久化&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Sync 機制&lt;/td>
 &lt;td>背景 git polling&lt;/td>
 &lt;td>native OS file watcher 2s debounce&lt;/td>
 &lt;td>session warm-up&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Type resolution&lt;/td>
 &lt;td>Go / C / C++ / TS / JS 有 hybrid、其他語言只有 syntactic&lt;/td>
 &lt;td>tree-sitter syntactic 為主、聲稱對部分 dynamic dispatch 有解&lt;/td>
 &lt;td>完整 LSP 型別解析&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>跨 service&lt;/td>
 &lt;td>first-class HTTP_CALLS edge + channel&lt;/td>
 &lt;td>route definition 識別、不做 client URL → server route 比對&lt;/td>
 &lt;td>無&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>概念性自然語言搜尋&lt;/td>
 &lt;td>11-signal scoring + camel split&lt;/td>
 &lt;td>symbol pattern match&lt;/td>
 &lt;td>無&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Symbol-level 編輯&lt;/td>
 &lt;td>無（純讀）&lt;/td>
 &lt;td>無（純讀）&lt;/td>
 &lt;td>完整（replace_symbol_body / rename）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>編譯 diagnostic&lt;/td>
 &lt;td>無&lt;/td>
 &lt;td>無&lt;/td>
 &lt;td>有（&lt;code>get_diagnostics_for_file&lt;/code>）&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>這張表的判讀重點：&lt;strong>三者擅長的事不重疊&lt;/strong>。cbm 強在「找東西」、codegraph 強在「日常 call graph + auto-sync」、serena 強在「型別精確 reference + 編輯出口」。&lt;/p></description><content:encoded><![CDATA[<h2 id="為什麼需要對照為什麼選-dart">為什麼需要對照、為什麼選 Dart</h2>
<p>評估 code intelligence MCP 不能只看 README benchmark：每個工具的 benchmark 都選自己擅長的 codebase 跟語言，readme 數字只能參考、不能直接套到自家 stack。</p>
<p>這次選一個 Dart 商業專案做對照場域有兩個理由：</p>
<ul>
<li>Dart 是三個工具的「中間地帶」——<a href="/blog/record/codebase-memory-mcp155-%E8%AA%9E%E8%A8%80-tree-sitter-%E7%9F%A5%E8%AD%98%E5%9C%96%E8%AD%9C-mcp-%E7%9A%84%E8%83%BD%E5%8A%9B%E8%88%87%E9%82%8A%E7%95%8C/">cbm</a> 不在 hybrid resolution 名單、<a href="/blog/record/codegraph%E7%94%A8-tree-sitter-per-language-query-%E6%92%90%E8%B5%B7-19-%E8%AA%9E%E8%A8%80-call-graph-%E7%9A%84-mcp/">codegraph</a> 列為 full support、<a href="/blog/record/serena%E6%8A%8A-lsp-%E5%8C%85%E6%88%90-agent-first-mcp-%E7%9A%84-symbol-level-%E7%B7%A8%E8%BC%AF%E6%96%B9%E6%A1%88/">serena</a> 借 <code>dart analysis_server</code> 有完整 LSP。三條技術路線在同一語言上的能力差距會被最大化。</li>
<li>Dart 大量用 extension type、generic、factory pattern，這些是 type-inferred dispatch 的高發場景，能逼出每個工具的真實精度差。</li>
</ul>
<p>在 Go / TypeScript 上跑同樣對照，結論會反過來——cbm 的 hybrid resolution 在那裡會接近 LSP 精度，三刀流的必要性會降低。所以這篇結論限定「LSP 成熟但 cbm 不在 hybrid resolution 名單」的語言。</p>
<h2 id="本質差異tree-sitter-syntactic-vs-lsp-type-aware">本質差異：tree-sitter syntactic vs LSP type-aware</h2>
<p>三個工具在 Dart 上的能力差距，根源是兩條技術路線的本質落差：</p>
<p><strong>tree-sitter syntactic</strong>：只看語法結構。看到 <code>a.b()</code> 知道有個 method call、不知道 <code>a</code> 是什麼型別、不知道 <code>b()</code> 連到哪個 declaration。對 receiver 是 literal 或顯式型別宣告的 callsite 可以解、對 local variable / parameter / 推斷型別的 callsite 會漏。</p>
<p><strong>LSP type-aware</strong>：走 language server 內建的型別推斷引擎。跟 IDE 用同一套後端、能解出 <code>a</code> 的真實型別、再從 type declaration 找到對應的 method。所以 reference 是型別精確的。</p>
<p>cbm 的 hybrid type resolution（限 Go / C / C++ / TS / JS）是把 LSP 的型別解析算法 clean-room 重寫進 binary、所以那幾個語言上 cbm 等於有 LSP 級精度但沒 LSP 依賴。Dart 沒得到這個待遇，所以 cbm 在 Dart 上只剩純 syntactic 結構抽取。</p>
<p>判讀訊號：看一個工具對某語言的能力強弱，問「<strong>它在這語言上做型別解析嗎？</strong>」——做的話接近 LSP，不做的話只是個結構抽取器。</p>
<p>這個 framework 建立後、下節展開到 9 個維度的設計對照。</p>
<h2 id="三個工具的設計差異對照">三個工具的設計差異對照</h2>
<p>三個工具雖然都是「code intelligence MCP」，設計取向互補：</p>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th><a href="/blog/record/codebase-memory-mcp155-%E8%AA%9E%E8%A8%80-tree-sitter-%E7%9F%A5%E8%AD%98%E5%9C%96%E8%AD%9C-mcp-%E7%9A%84%E8%83%BD%E5%8A%9B%E8%88%87%E9%82%8A%E7%95%8C/">cbm</a></th>
          <th><a href="/blog/record/codegraph%E7%94%A8-tree-sitter-per-language-query-%E6%92%90%E8%B5%B7-19-%E8%AA%9E%E8%A8%80-call-graph-%E7%9A%84-mcp/">codegraph</a></th>
          <th><a href="/blog/record/serena%E6%8A%8A-lsp-%E5%8C%85%E6%88%90-agent-first-mcp-%E7%9A%84-symbol-level-%E7%B7%A8%E8%BC%AF%E6%96%B9%E6%A1%88/">serena</a></th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>解析後端</td>
          <td>tree-sitter + 自寫 type resolver</td>
          <td>tree-sitter + per-language query</td>
          <td>LSP（per-language server）</td>
      </tr>
      <tr>
          <td>語言覆蓋</td>
          <td>155（vendored grammar）</td>
          <td>19+（每語言寫 query）</td>
          <td>視 LSP 支援度（40+）</td>
      </tr>
      <tr>
          <td>持久化</td>
          <td>SQLite + WAL（可 zstd 匯出為 team artifact）</td>
          <td>SQLite + FTS5</td>
          <td>per-session、不持久化</td>
      </tr>
      <tr>
          <td>Sync 機制</td>
          <td>背景 git polling</td>
          <td>native OS file watcher 2s debounce</td>
          <td>session warm-up</td>
      </tr>
      <tr>
          <td>Type resolution</td>
          <td>Go / C / C++ / TS / JS 有 hybrid、其他語言只有 syntactic</td>
          <td>tree-sitter syntactic 為主、聲稱對部分 dynamic dispatch 有解</td>
          <td>完整 LSP 型別解析</td>
      </tr>
      <tr>
          <td>跨 service</td>
          <td>first-class HTTP_CALLS edge + channel</td>
          <td>route definition 識別、不做 client URL → server route 比對</td>
          <td>無</td>
      </tr>
      <tr>
          <td>概念性自然語言搜尋</td>
          <td>11-signal scoring + camel split</td>
          <td>symbol pattern match</td>
          <td>無</td>
      </tr>
      <tr>
          <td>Symbol-level 編輯</td>
          <td>無（純讀）</td>
          <td>無（純讀）</td>
          <td>完整（replace_symbol_body / rename）</td>
      </tr>
      <tr>
          <td>編譯 diagnostic</td>
          <td>無</td>
          <td>無</td>
          <td>有（<code>get_diagnostics_for_file</code>）</td>
      </tr>
  </tbody>
</table>
<p>這張表的判讀重點：<strong>三者擅長的事不重疊</strong>。cbm 強在「找東西」、codegraph 強在「日常 call graph + auto-sync」、serena 強在「型別精確 reference + 編輯出口」。</p>
<p>對照表的維度很多、但實務上踩到事故的多半集中在三個維度，把它們各自展開：</p>
<p><strong>Type resolution 決定 caller 數字的可信度</strong>。Dart / Swift / Kotlin 這類「LSP 完整、但 cbm 走純 syntactic 路線」的語言上、tree-sitter 工具回的 caller 數字是 lower bound（實際值通常更高）。<code>samplePrice.multiplyByRate(...)</code> 這種 type-inferred receiver 是這層差距的主戰場。判讀訊號：對熱門 class 跑同一 query、若 tree-sitter 工具 caller 數比 LSP 工具低過半、type-inferred dispatch 在這語言是主流模式、tree-sitter 結果只能當 starting point。</p>
<p><strong>Sync 機制決定「邊改邊問」是否可用</strong>。codegraph 的 native OS file watcher + 2s debounce 最貼近 IDE、cbm 的背景 git polling 有秒級至分級延遲、serena 的 session warm-up 是「啟動時等一次、之後即時」。事故型態：在 codegraph 改完檔案立刻問 caller 多半 OK、在 cbm 立刻問會拿到 stale graph。判讀訊號：問完 query 對結果存疑時、先檢查工具的 sync 狀態（cbm 跑 <code>index_status</code>、codegraph 跑 <code>codegraph_status</code>、serena 直接重 query）。</p>
<p><strong>持久化模式決定跨 session 的累積成本</strong>。cbm / codegraph 寫 SQLite、跨 session 重用；serena per-session、每次 spawn LSP warm up。對「短任務反覆 ad-hoc 查詢」cbm / codegraph 邊際成本更低、對「會做 symbol-level edit 跟 diagnostic」serena 的 per-session warm up 是必要 cost。判讀訊號：第一次 query 慢、之後快——LSP indexing warm up、正常；每次 query 都慢——LSP 可能因記憶體不足重啟、需排查。</p>
<p>下面的實測是這張表在 Dart 上的數字驗證。</p>
<h2 id="dart-實測對照同題不同工具">Dart 實測對照：同題不同工具</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">專案類型：Dart 商業專案（POS / 零售領域）
</span></span><span class="line"><span class="ln">2</span><span class="cl">Branch：refactor/money-value-object
</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">  cbm:        3,038 nodes,  6,355 edges（Dart 沒 CALLS edge）
</span></span><span class="line"><span class="ln">5</span><span class="cl">  codegraph:  6,244 nodes, 12,223 edges（含 CALLS edge）
</span></span><span class="line"><span class="ln">6</span><span class="cl">  serena:     per-session、無索引統計</span></span></code></pre></div><p>cbm 跟 codegraph 的 nodes 約 2x、edges 約 2x，差異關鍵不在 nodes（cbm 缺 import / enum_member 等次要 node）、而在「<strong>有沒有 CALLS edge</strong>」——這直接決定 caller / impact 類查詢能不能用。</p>
<blockquote>
<p><strong>實測數字的適用範圍</strong>：本節的所有 callsite / caller / impact 數字（含查詢 1-5）都是<strong>單一 Dart 商業專案的內部 baseline</strong>、不保證跨專案重現。Dart 上 type-inferred receiver 比例高的專案會放大三個工具的差距、比例低的專案會縮小差距。換到 Swift / Kotlin / Rust 等語言上、絕對數字會不同但「tree-sitter syntactic vs LSP type-aware」的差距方向通常一致。讀者要套用結論時、先在自家 repo 跑一遍同題對照、看自己的數字落差。</p></blockquote>
<h3 id="查詢-1誰呼叫了-moneymultiplybyrate">查詢 1：誰呼叫了 <code>Money.multiplyByRate</code></h3>
<table>
  <thead>
      <tr>
          <th>工具</th>
          <th>結果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>cbm</td>
          <td>0（hybrid resolution 不含 Dart）</td>
      </tr>
      <tr>
          <td>codegraph</td>
          <td>3 caller symbols（4 個檔案中漏 product.dart 的 3 個 callsite）</td>
      </tr>
      <tr>
          <td>serena</td>
          <td>4 個檔案、9 個 callsite</td>
      </tr>
  </tbody>
</table>
<p>codegraph 漏掉的 3 個 callsite 共同特徵：</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">// lib/data/models/product/product.dart
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span><span class="kd">final</span> <span class="n">Money</span> <span class="n">samplePrice</span> <span class="o">=</span> <span class="p">...;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="n">samplePrice</span><span class="p">.</span><span class="n">multiplyByRate</span><span class="p">(</span><span class="n">Decimal</span><span class="p">.</span><span class="n">parse</span><span class="p">(</span><span class="s1">&#39;0.9&#39;</span><span class="p">));</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="n">samplePrice</span><span class="p">.</span><span class="n">multiplyByRate</span><span class="p">(</span><span class="n">Decimal</span><span class="p">.</span><span class="n">parse</span><span class="p">(</span><span class="s1">&#39;0.6&#39;</span><span class="p">));</span></span></span></code></pre></div><p><code>samplePrice</code> 是 local variable、要型別推斷才知道是 <code>Money</code>。tree-sitter 看到的只是 <code>&lt;identifier&gt;.multiplyByRate(...)</code>、解不出 dispatch target。</p>
<p>serena 透過 <code>dart analysis_server</code> 拿到完整型別資訊、知道 <code>samplePrice</code> 宣告是 <code>Money</code>、能精確 dispatch。</p>
<h3 id="查詢-2誰呼叫了-localesymbolconfigformatamount">查詢 2：誰呼叫了 <code>LocaleSymbolConfig.formatAmount</code></h3>
<table>
  <thead>
      <tr>
          <th>工具</th>
          <th>結果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>cbm</td>
          <td>0</td>
      </tr>
      <tr>
          <td>codegraph</td>
          <td>30（<code>--limit 30</code>，預設 20 截斷）</td>
      </tr>
      <tr>
          <td>serena</td>
          <td>5 個檔案、21 個 callsite</td>
      </tr>
  </tbody>
</table>
<p>這題 codegraph 跟 serena 的差距比較小——<code>formatAmount</code> 在很多地方是用顯式 receiver 呼叫（如 <code>LocaleSymbolConfig.cny.formatAmount(...)</code>），tree-sitter 對顯式 receiver 解得到。</p>
<p>兩邊數字的差異主因是 <strong>caller symbol 數 vs callsite 數</strong>的計數單位差：</p>
<ul>
<li>codegraph 算 caller symbol（一個 method 內呼叫幾次都算 1）</li>
<li>serena 算 callsite</li>
</ul>
<p>寫實測 baseline 時這個單位要寫死、否則 3 vs 9 看起來像精度差距、實際上一部分只是計數規則不同。</p>
<h3 id="查詢-3money-符號的內部結構">查詢 3：<code>Money</code> 符號的內部結構</h3>
<table>
  <thead>
      <tr>
          <th>工具</th>
          <th>結果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>cbm</td>
          <td>只認得 File / Module、extension type 子結構抽不到</td>
      </tr>
      <tr>
          <td>codegraph</td>
          <td>認得 class 但 extension type 支援度未驗證</td>
      </tr>
      <tr>
          <td>serena</td>
          <td>Namespace kind、3 個 Field、16 個 Method、3 個 Property 都附行號</td>
      </tr>
  </tbody>
</table>
<p>Dart <code>extension type</code> 是相對新的特性、tree-sitter grammar 對它的支援深度不一。serena 走 LSP 直接拿到 <code>dart analysis_server</code> 對 extension type 的完整解析。</p>
<p>對需要「列出某 class / extension 所有 member」的場景、serena 是 Dart 上 LSP 級精度最可信的選項（其他 MCP 在 Dart extension type 上做不到完整 member 列舉）。</p>
<h3 id="查詢-4概念性搜尋金額顯示相關函式">查詢 4：概念性搜尋「金額顯示」相關函式</h3>
<p>對「我不知道精確名稱、只記得功能類別」這種 query：</p>
<table>
  <thead>
      <tr>
          <th>名次</th>
          <th>cbm（11-signal scoring）</th>
          <th>codegraph_search</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>1-4</td>
          <td>4 個 <code>formatAmount</code> 實作（兩邊一致）</td>
          <td>4 個 <code>formatAmount</code> 實作（兩邊一致）</td>
      </tr>
      <tr>
          <td>5</td>
          <td><code>externalDisplayMain</code></td>
          <td><code>displayCategories</code></td>
      </tr>
      <tr>
          <td>6</td>
          <td><code>connectExternalDisplay</code></td>
          <td><code>displayTags</code></td>
      </tr>
      <tr>
          <td>7</td>
          <td><code>_buildQuantityDisplay</code></td>
          <td><code>displayName</code></td>
      </tr>
      <tr>
          <td>8</td>
          <td><code>connectExternalDisplay</code>（另一個）</td>
          <td><code>displayCover</code></td>
      </tr>
      <tr>
          <td>9</td>
          <td><code>getBalanceDisplay</code></td>
          <td><code>displayName</code>（另一個）</td>
      </tr>
      <tr>
          <td>10</td>
          <td><code>_buildPriceDisplay</code></td>
          <td><code>displayName</code>（另一個）</td>
      </tr>
  </tbody>
</table>
<p>前 4 名兩邊都抓到核心 <code>formatAmount</code> 實作，第 5 名後分歧明顯：</p>
<ul>
<li>cbm 補進的 <code>getBalanceDisplay</code> / <code>_buildPriceDisplay</code> / <code>connectExternalDisplay</code> 都跟「金額顯示」概念相關（顯示金額 / 顯示餘額 / 外接顯示器）</li>
<li>codegraph 補進的 <code>displayName</code> / <code>displayTags</code> 只是符號名含 &ldquo;display&rdquo; 子字串、跟金額無關</li>
</ul>
<p>差異來源是 cbm 的 11-signal scoring + <code>cbm_camel_split</code> 對 camelCase 切詞做語意切分（<code>getMoneyField</code> → <code>get</code> + <code>money</code> + <code>field</code>）。codegraph 的 search 是 symbol pattern match、沒對自然語言 query 做語意處理。</p>
<p>這題的判讀很關鍵——<strong>cbm 在「找東西」的角色不能被 codegraph 取代</strong>。即使 codegraph 在 Dart 上有可用的 call graph、它的 search 仍然贏不了 cbm 的概念性 query。</p>
<h3 id="查詢-5money-的-impact-範圍--cross-symbol-trace">查詢 5：<code>Money</code> 的 impact 範圍 / cross-symbol trace</h3>
<table>
  <thead>
      <tr>
          <th>工具</th>
          <th>結果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>cbm</td>
          <td>無 impact 概念、回不出</td>
      </tr>
      <tr>
          <td>codegraph</td>
          <td>5 個 affected symbol、全在 MoneyFieldRenderer 一檔</td>
      </tr>
      <tr>
          <td>serena</td>
          <td>走 <code>find_referencing_symbols</code> 跨 4 個檔案找完整 reference</td>
      </tr>
  </tbody>
</table>
<p>Money 是該專案大量使用的 value object、實際被使用的檔案橫跨 receipt_data 實作、settlement、cart_item、order_dto 等業務模組。codegraph 只回 1 個檔案 5 個 symbol、嚴重低估 blast radius。</p>
<p>漏掉的原因跟查詢 1 同源——<code>something.multiplyByRate(...)</code>、<code>Money</code> 在 factory 內被隱式構造這些都不在 tree-sitter 能解的範圍。MoneyFieldRenderer 之所以被抓到、是因為它的 field 顯式宣告為 <code>Money</code>，這是少數 tree-sitter syntactic 能抓的場合。</p>
<p>對 cross-symbol trace：</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">codegraph_trace(from: &#34;Money/multiplyByRate&#34;, to: &#34;ProductSpecification&#34;)
</span></span><span class="line"><span class="ln">2</span><span class="cl">→ &#34;No direct path&#34;、建議跳到 dynamic dispatch</span></span></code></pre></div><p>graph 上根本沒這條 edge（漏掉的 product.dart 那 3 個 callsite 正是這條 trace 的關鍵跳）、所以 trace 直接失敗。</p>
<p>判讀訊號：<strong>重要 refactor 不能單看 codegraph 的 impact 數字</strong>。要走 serena <code>find_referencing_symbols</code> 二次確認；對 cbm 不在 hybrid resolution 名單的語言、blast radius 必須用 LSP 工具驗證。</p>
<h2 id="三刀流工作流">三刀流工作流</h2>
<p>實測結論：cbm / codegraph / serena 各有不可替代的角色，組合使用才是 Dart 主力專案的合理 stack。</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">找東西（不知道精確名稱、概念性 query）
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  → cbm search_graph(query=&#34;...&#34;)           ← 11-signal scoring 對概念性 query 最強
</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">知道精確名稱、找 caller / callee
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  → codegraph_callers / codegraph_callees   ← auto-sync 2s 反應最快
</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">  發現結果可能不完整（type-inferred dispatch 多的場合）
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  → serena find_referencing_symbols         ← LSP 完整精度補位
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">重要 refactor 確認 blast radius
</span></span><span class="line"><span class="ln">11</span><span class="cl">  → serena find_referencing_symbols         ← 不能單靠 codegraph_impact
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">符號層級的編輯
</span></span><span class="line"><span class="ln">14</span><span class="cl">  → serena replace_symbol_body / rename     ← symbol-level atomic edit
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">跨 service HTTP/RPC 鏈接（若 monorepo 含 client + server）
</span></span><span class="line"><span class="ln">17</span><span class="cl">  → cbm HTTP_CALLS edge                     ← 三個工具中只有 cbm 有這層</span></span></code></pre></div><p>幾個關鍵的判讀原則：</p>
<p><strong>入口跟出口要分清楚</strong>：cbm 是「廣度索引 + 模糊搜尋」的入口、拿到 qualified name 後轉給 serena 做精確查詢與編輯。codegraph 補在中間、做日常結構查詢。</p>
<p><strong>重要 refactor 必走 serena 補位</strong>：codegraph 的 caller / impact 在 Dart 上系統性偏低、不能單看數字判斷影響範圍。決定 rename 或大幅修改 method 之前、用 serena 跑一次 <code>find_referencing_symbols</code> 對齊。</p>
<p><strong>Hook 不要打架</strong>：cbm 會寫 PreToolUse hook 攔截 Grep / Glob / Read / Search（README 描述只擋前兩者、實裝版本含 Read / Search）、codegraph / serena 都不寫 hook。同時用三個工具時、注意 cbm hook 是否誤判把正常的 markdown grep 也擋掉（實測有 false positive）。</p>
<h2 id="對其他語言-stack-怎麼變化">對其他語言 stack 怎麼變化</h2>
<p>這個三刀流結論限定 Dart。不同語言 stack 的真實壓力不一樣、推薦組合也跟著變——把幾個常見 stack 各自展開。</p>
<h3 id="go--typescript--c--c-主力">Go / TypeScript / C / C++ 主力</h3>
<p>這層是 cbm 的甜蜜點：hybrid type resolution 涵蓋這四個語族、CALLS edge 抽得到、cbm 的 caller / blast radius 精度接近 LSP。實務影響是「cbm 在 Dart 上需要 codegraph + serena 補位」的場景大幅縮小——cbm 自己就能處理 caller / impact、加上它原本就強的 11-signal 概念搜尋跟跨 service HTTP_CALLS，等於一個工具撐住「找東西」「caller / impact」「cross-service」三層。</p>
<p>serena 在這個 stack 仍是 symbol-level edit 跟 compile diagnostic 的關鍵來源——cbm 純讀、沒 rename / replace_symbol_body、沒 LSP 診斷整合。所以合理組合是「cbm + serena 雙刀流」、codegraph 的角色被 cbm 取代掉。判讀訊號：在自家 repo 跑 cbm <code>trace_call_path</code> 對 5 個熱門 class、若 caller 數跟 serena 的 <code>find_referencing_symbols</code> 對得上、codegraph 確實可以省下。</p>
<h3 id="swift--kotlin--rust-主力">Swift / Kotlin / Rust 主力</h3>
<p>這層跟 Dart 場景結構接近：serena 透過 sourcekit-lsp / kotlin-language-server / rust-analyzer 能拿到完整型別解析、cbm 不在 hybrid resolution 名單只剩純 syntactic。所以「三刀流」的論證仍適用。</p>
<p>但 codegraph 在這三個語言的 query 品質要實測——19+ 列表內這幾個都列為 supported、實際解析深度因語言成熟度而異。Swift 特別容易踩坑的點是 Objective-C interop（dispatch table 跨語言）跟 protocol extension 的型別推斷、Kotlin 則是 reified generics 跟 inline function、Rust 是 trait method 跟 macro 展開後的 callsite。判讀訊號：對自家專案最常用的 dispatch pattern 寫一個 minimal example、跑 codegraph callers、看抓不抓得到。</p>
<h3 id="python-主力">Python 主力</h3>
<p>三個工具的 Python 支援都成熟、但著力點不同：cbm 對 Python 有完整 hybrid resolution、codegraph 對 Python 是核心支援語言之一（VS Code benchmark 在它的 7 codebase 列表內）、serena 透過 pyright / pylsp 拿型別資訊。</p>
<p>Python 的特殊壓力是 dynamic dispatch（duck typing / monkey patching / metaclass / <strong>getattr</strong>）——這層任何 static 工具都會漏。判讀訊號：對自家 codebase 跑「找 X class 的所有 method 呼叫」、若大量真實 callsite 在 type annotation 缺失的位置、所有工具都只能給 lower bound。實務組合多半雙刀（codegraph + serena）夠用、cbm 對 Python 的不可替代價值在 cross-service HTTP_CALLS（Django / FastAPI 跨 service 場景）。</p>
<h3 id="冷門語言--dslliquid--pascal--svelte-template-等">冷門語言 / DSL（Liquid / Pascal / Svelte template 等）</h3>
<p>這層 serena 多半沒 LSP 可借（除非自備 server）、cbm 純 syntactic（hybrid 名單外）、codegraph 是少數仍有 query 的工具——但 query 品質要看 codegraph 對該語言投入多深、Pascal / Delphi / Liquid 這類列表末段的支援度可能只到 symbol 抽取、callsite 不一定有。</p>
<p>實務上對這層語言、退回 <code>grep + codegraph</code> 比強推三刀流合理——caller / impact 用 codegraph 試、不夠就 grep 補、別期待 LSP 級精度。判讀訊號：若 codegraph status 顯示 indexed file 多但 edges 數明顯偏低（&lt; 1 條 edge per file）、call graph 多半沒抽起來、視同純 syntactic 工具用。</p>
<h3 id="共通的評估方法">共通的評估方法</h3>
<p>無論哪個 stack、第一次裝 MCP 前在自家 repo 跑「找重要 class / function 的所有 caller」這個基準題、把不同工具的數字並列比較、再決定組合。README benchmark 是行銷數字、自家 stack 跑出的數字才是真實 baseline。</p>
<h2 id="評估新-mcp-工具的-checklist">評估新 MCP 工具的 checklist</h2>
<p>從這次踩三個（含一個跳過實裝的 GitNexus）的經驗回推、未來評估新 code intelligence MCP 要先確認：</p>
<p><strong>License</strong>：商業專案要 MIT / Apache 2.0 / BSD。PolyForm Noncommercial 之類限制商業使用的 license 直接刷掉。這條最便宜、最早做、最少人記得做。</p>
<p><strong>目標語言的 call graph 支援</strong>：README 寫「full support」要實測。tree-sitter wrapper 通常只到「結構抽得到」、沒到「call edge 抽得到」。同樣是「有 CALLS edge」、有 type-inferred dispatch 的 syntactic 工具跟有完整 LSP 的差距可能 2-3x callsite 數。</p>
<p><strong>MCP tool 數量不等於能力</strong>：14 個 tool 不一定贏過 10 個。看 caller / impact / find_referencing_symbols 這類核心功能有沒有、品質好不好、勝過 tool 多寡。</p>
<p><strong>是否會自動改 <code>~/.claude/</code> 設定</strong>：大多會。先看 install script 動了哪些檔案、能不能還原、uninstall 是否徹底（cbm uninstall 不清 hook 是踩過的坑）。</p>
<p><strong>是否有 CLI 模式</strong>：有的話本 session 就能實測、不必等 Claude Code 重啟載入 MCP。CLI mode 對「驗證 baseline」特別重要——拿 CLI 結果當 ground truth、再對 MCP 結果做差異比對。</p>
<p><strong>Auto-sync 機制</strong>：file watcher / git polling / 純手動 reindex 差異很大。「邊改邊問」工作流對 sync 延遲很敏感、選錯會踩到 stale graph 的事故。</p>
<h2 id="結論">結論</h2>
<p>對 Dart 主力專案：<strong>三刀流（cbm + codegraph + serena）是合理 stack</strong>。三者擅長的事不重疊、互相補位有明確角色：</p>
<ul>
<li><a href="/blog/record/codebase-memory-mcp155-%E8%AA%9E%E8%A8%80-tree-sitter-%E7%9F%A5%E8%AD%98%E5%9C%96%E8%AD%9C-mcp-%E7%9A%84%E8%83%BD%E5%8A%9B%E8%88%87%E9%82%8A%E7%95%8C/">cbm</a>：概念性搜尋入口、跨 service HTTP/RPC 鏈接</li>
<li><a href="/blog/record/codegraph%E7%94%A8-tree-sitter-per-language-query-%E6%92%90%E8%B5%B7-19-%E8%AA%9E%E8%A8%80-call-graph-%E7%9A%84-mcp/">codegraph</a>：日常 80% 的結構查詢、auto-sync 反應最快</li>
<li><a href="/blog/record/serena%E6%8A%8A-lsp-%E5%8C%85%E6%88%90-agent-first-mcp-%E7%9A%84-symbol-level-%E7%B7%A8%E8%BC%AF%E6%96%B9%E6%A1%88/">serena</a>：型別精確 reference、symbol-level atomic edit、編譯 diagnostic</li>
</ul>
<p>對其他語言 stack、cbm 進入 hybrid resolution 名單後組合會收斂、但 serena 的 symbol edit 跟 diagnostic 角色仍不可取代。</p>
<p>評估方法的更普遍結論：<strong>README benchmark 只是起點、要在自己的 stack 上跑同樣的基準題才算數</strong>。每個工具的 benchmark 都選自己擅長的語言跟 codebase、跨語言遷移結論需要重新驗證。用 5 個查詢做 baseline、把 CLI 數字當 ground truth、再對 MCP 結果做差異對比、是現階段最低成本的工具評估法。</p>
]]></content:encoded></item><item><title>Codex 與 Claude Code Statusline 相容設計方法</title><link>https://tarrragon.github.io/blog/record/codex-%E8%88%87-claude-code-statusline-%E7%9B%B8%E5%AE%B9%E8%A8%AD%E8%A8%88%E6%96%B9%E6%B3%95/</link><pubDate>Wed, 13 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/codex-%E8%88%87-claude-code-statusline-%E7%9B%B8%E5%AE%B9%E8%A8%AD%E8%A8%88%E6%96%B9%E6%B3%95/</guid><description>&lt;h2 id="問題錨點">問題錨點&lt;/h2>
&lt;p>Statusline 相容設計的核心責任是把「資料輸入契約」和「畫面渲染邏輯」分開。Claude Code 已經提供 command-backed statusline，會把 session JSON 丟進命令的 stdin；Codex 目前公開的設定則是 &lt;code>tui.status_line&lt;/code> 字串項目陣列，契約停在內建 footer item 的排列與選擇。&lt;/p>
&lt;p>這個差異讓「同一個 statusline 工具同時支援兩邊」要從輸入契約對齊開始。真正要做的是先建立一層輸入正規化：Claude JSON、Codex 既有或未來 JSON、手動測試 JSON 都先轉成同一個內部狀態，再交給同一套 renderer。&lt;/p>
&lt;h2 id="case-first-觀察">Case-first 觀察&lt;/h2>
&lt;p>Case-first 查詢的目的，是先看社群實際卡在哪裡，再決定要改工具還是改使用方式。本次查詢到的案例集中在 OpenAI Codex repo issue 與官方文件，顯示需求已經存在，但 Codex 的 command-backed statusline 仍屬提案或缺口。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Case&lt;/th>
 &lt;th>觀察&lt;/th>
 &lt;th>判讀&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://code.claude.com/docs/en/statusline">Claude Code status line 官方文件&lt;/a>&lt;/td>
 &lt;td>Claude Code 的 statusline command 會從 stdin 收到 JSON，stdout 的每一行會顯示成 status area。&lt;/td>
 &lt;td>Claude 端是穩定可用的 producer，工具可依賴 &lt;code>model&lt;/code>、&lt;code>workspace&lt;/code>、&lt;code>context_window&lt;/code>、&lt;code>rate_limits&lt;/code> 這類欄位。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://developers.openai.com/codex/config-reference">OpenAI Codex config reference&lt;/a>&lt;/td>
 &lt;td>&lt;code>tui.status_line&lt;/code> 的型別是 &lt;code>array&amp;lt;string&amp;gt;&lt;/code> 或 &lt;code>null&lt;/code>，用途是排列 footer status-line item identifiers。&lt;/td>
 &lt;td>Codex 端目前公開契約屬於內建項目清單。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://github.com/openai/codex/issues/17827">openai/codex #17827&lt;/a>&lt;/td>
 &lt;td>使用者明確要求 Codex 加入類似 Claude Code 的 &lt;code>statusLine.command&lt;/code>。&lt;/td>
 &lt;td>社群已把 Claude Code statusline 當成對照基準，混用痛點是真實需求。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://github.com/openai/codex/issues/20043">openai/codex #20043&lt;/a>&lt;/td>
 &lt;td>提案列出 Codex 既有 &lt;code>status_line&lt;/code> picker，並要求外部 command 模式、ANSI 顏色與 stdin JSON。&lt;/td>
 &lt;td>未來若 Codex 採納此類設計，statusline 工具需要同時支援 Codex 風格 JSON 與 Claude 欄位。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://github.com/openai/codex/issues/20244">openai/codex #20244&lt;/a>&lt;/td>
 &lt;td>另一個使用者提出 command-backed item 或 persistent banner，並被標為 #17827 的 duplicate。&lt;/td>
 &lt;td>重複 issue 表示需求已經多次出現；相容設計應預留 Codex command input，讓後續定案只需要調整 mapper。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://github.com/openai/codex/issues/21324">openai/codex #21324&lt;/a>&lt;/td>
 &lt;td>使用者在 local branch 實作 context/token usage 狀態項目與進度條。&lt;/td>
 &lt;td>Codex 社群也在補足使用量可視化，但路徑偏向內建 item，和 Claude 的外部 renderer 是兩種不同擴充模型。&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="wrap-判讀">WRAP 判讀&lt;/h2>
&lt;p>Anchor Check：目標是讓 &lt;code>cc-statusline&lt;/code> 的核心能力可被兩種工具共用。使用者真正需要的是少維護一套 statusline 邏輯，並在 Codex 具備 command-backed 入口時保留既有 renderer。&lt;/p>
&lt;p>Step 0 資料充足度：足以做工具內部改造，尚不足以宣稱 Codex TUI 目前能直接執行 &lt;code>cc-statusline&lt;/code>。官方文件只保證 &lt;code>tui.status_line&lt;/code> 是字串陣列；社群 issue 裡的 command JSON 仍是提案階段。&lt;/p>
&lt;p>Widen Options：可選方案有三種。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>選項&lt;/th>
 &lt;th>策略&lt;/th>
 &lt;th>適用條件&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>A：只用 Codex 內建 &lt;code>tui.status_line&lt;/code>&lt;/td>
 &lt;td>不改 &lt;code>cc-statusline&lt;/code>，在 Codex 設定內建項目。&lt;/td>
 &lt;td>只需要模型、目錄、git branch、context 這類內建欄位時可用。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>B：改 &lt;code>cc-statusline&lt;/code> 成雙 schema renderer&lt;/td>
 &lt;td>保留 Claude JSON，新增 Codex / generic JSON normalization。&lt;/td>
 &lt;td>希望同一套 renderer 服務 Claude、未來 Codex command hook、tmux / wrapper 測試時最划算。&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>C：Fork 兩套工具&lt;/td>
 &lt;td>Claude 一套、Codex 一套，各自用不同資料模型。&lt;/td>
 &lt;td>只有在兩邊 UI 契約長期分歧且需求完全不同時才合理。&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Reality Test：目前 Codex 的公開設定停在內建 item 排列，所以 B 的立即價值是讓工具「具備 Codex 相容輸入能力」。反向驗證是：若未來 Codex 最終採用完全不同的 command JSON，B 的 normalization 層仍只需新增一個 mapper，renderer 可維持同一套。&lt;/p></description><content:encoded><![CDATA[<h2 id="問題錨點">問題錨點</h2>
<p>Statusline 相容設計的核心責任是把「資料輸入契約」和「畫面渲染邏輯」分開。Claude Code 已經提供 command-backed statusline，會把 session JSON 丟進命令的 stdin；Codex 目前公開的設定則是 <code>tui.status_line</code> 字串項目陣列，契約停在內建 footer item 的排列與選擇。</p>
<p>這個差異讓「同一個 statusline 工具同時支援兩邊」要從輸入契約對齊開始。真正要做的是先建立一層輸入正規化：Claude JSON、Codex 既有或未來 JSON、手動測試 JSON 都先轉成同一個內部狀態，再交給同一套 renderer。</p>
<h2 id="case-first-觀察">Case-first 觀察</h2>
<p>Case-first 查詢的目的，是先看社群實際卡在哪裡，再決定要改工具還是改使用方式。本次查詢到的案例集中在 OpenAI Codex repo issue 與官方文件，顯示需求已經存在，但 Codex 的 command-backed statusline 仍屬提案或缺口。</p>
<table>
  <thead>
      <tr>
          <th>Case</th>
          <th>觀察</th>
          <th>判讀</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="https://code.claude.com/docs/en/statusline">Claude Code status line 官方文件</a></td>
          <td>Claude Code 的 statusline command 會從 stdin 收到 JSON，stdout 的每一行會顯示成 status area。</td>
          <td>Claude 端是穩定可用的 producer，工具可依賴 <code>model</code>、<code>workspace</code>、<code>context_window</code>、<code>rate_limits</code> 這類欄位。</td>
      </tr>
      <tr>
          <td><a href="https://developers.openai.com/codex/config-reference">OpenAI Codex config reference</a></td>
          <td><code>tui.status_line</code> 的型別是 <code>array&lt;string&gt;</code> 或 <code>null</code>，用途是排列 footer status-line item identifiers。</td>
          <td>Codex 端目前公開契約屬於內建項目清單。</td>
      </tr>
      <tr>
          <td><a href="https://github.com/openai/codex/issues/17827">openai/codex #17827</a></td>
          <td>使用者明確要求 Codex 加入類似 Claude Code 的 <code>statusLine.command</code>。</td>
          <td>社群已把 Claude Code statusline 當成對照基準，混用痛點是真實需求。</td>
      </tr>
      <tr>
          <td><a href="https://github.com/openai/codex/issues/20043">openai/codex #20043</a></td>
          <td>提案列出 Codex 既有 <code>status_line</code> picker，並要求外部 command 模式、ANSI 顏色與 stdin JSON。</td>
          <td>未來若 Codex 採納此類設計，statusline 工具需要同時支援 Codex 風格 JSON 與 Claude 欄位。</td>
      </tr>
      <tr>
          <td><a href="https://github.com/openai/codex/issues/20244">openai/codex #20244</a></td>
          <td>另一個使用者提出 command-backed item 或 persistent banner，並被標為 #17827 的 duplicate。</td>
          <td>重複 issue 表示需求已經多次出現；相容設計應預留 Codex command input，讓後續定案只需要調整 mapper。</td>
      </tr>
      <tr>
          <td><a href="https://github.com/openai/codex/issues/21324">openai/codex #21324</a></td>
          <td>使用者在 local branch 實作 context/token usage 狀態項目與進度條。</td>
          <td>Codex 社群也在補足使用量可視化，但路徑偏向內建 item，和 Claude 的外部 renderer 是兩種不同擴充模型。</td>
      </tr>
  </tbody>
</table>
<h2 id="wrap-判讀">WRAP 判讀</h2>
<p>Anchor Check：目標是讓 <code>cc-statusline</code> 的核心能力可被兩種工具共用。使用者真正需要的是少維護一套 statusline 邏輯，並在 Codex 具備 command-backed 入口時保留既有 renderer。</p>
<p>Step 0 資料充足度：足以做工具內部改造，尚不足以宣稱 Codex TUI 目前能直接執行 <code>cc-statusline</code>。官方文件只保證 <code>tui.status_line</code> 是字串陣列；社群 issue 裡的 command JSON 仍是提案階段。</p>
<p>Widen Options：可選方案有三種。</p>
<table>
  <thead>
      <tr>
          <th>選項</th>
          <th>策略</th>
          <th>適用條件</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>A：只用 Codex 內建 <code>tui.status_line</code></td>
          <td>不改 <code>cc-statusline</code>，在 Codex 設定內建項目。</td>
          <td>只需要模型、目錄、git branch、context 這類內建欄位時可用。</td>
      </tr>
      <tr>
          <td>B：改 <code>cc-statusline</code> 成雙 schema renderer</td>
          <td>保留 Claude JSON，新增 Codex / generic JSON normalization。</td>
          <td>希望同一套 renderer 服務 Claude、未來 Codex command hook、tmux / wrapper 測試時最划算。</td>
      </tr>
      <tr>
          <td>C：Fork 兩套工具</td>
          <td>Claude 一套、Codex 一套，各自用不同資料模型。</td>
          <td>只有在兩邊 UI 契約長期分歧且需求完全不同時才合理。</td>
      </tr>
  </tbody>
</table>
<p>Reality Test：目前 Codex 的公開設定停在內建 item 排列，所以 B 的立即價值是讓工具「具備 Codex 相容輸入能力」。反向驗證是：若未來 Codex 最終採用完全不同的 command JSON，B 的 normalization 層仍只需新增一個 mapper，renderer 可維持同一套。</p>
<p>Attain Distance：B 的長期成本最低，因為 statusline 最容易變動的是輸入欄位名稱，最穩定的是使用者想看的資訊：專案、環境、輸入法、模型、context、rate limit、git worktree。把欄位差異收斂在 normalization 層，能避免每加入一個工具就複製一次畫面邏輯。</p>
<p>Prepare to be Wrong：若 Codex 不採納外部 command statusline，這次改造仍可用於手動測試、tmux status、其他 wrapper，且不影響 Claude Code 原始入口。若 Codex 採納但欄位名稱不同，新增 mapper 即可。</p>
<p>Tripwire：當 OpenAI Codex 文件把 <code>tui.status_line</code> 從 <code>array&lt;string&gt;</code> 擴充為 command 或 table schema 時，重新檢查 <code>cc-statusline</code> 的 Codex mapper。若 Codex issue #17827 關閉並附帶實作 PR，也應重新校準欄位名稱。</p>
<h2 id="實作策略">實作策略</h2>
<p>相容設計的正確切點是輸入正規化層。<code>cc-statusline</code> 應維持一個內部狀態模型，並接受多種外部 payload：</p>
<table>
  <thead>
      <tr>
          <th>外部 payload</th>
          <th>正規化規則</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Claude Code</td>
          <td>直接讀 <code>model.display_name</code>、<code>workspace.project_dir</code>、<code>context_window.used_percentage</code>、<code>rate_limits</code>。</td>
      </tr>
      <tr>
          <td>Codex proposed / generic</td>
          <td>接受 <code>model</code> 字串、<code>cwd</code> / <code>project_root</code>、<code>context.used_percent</code> / <code>context.remaining_percent</code>、<code>limits.five_hour</code> / <code>limits.weekly</code>。</td>
      </tr>
      <tr>
          <td>手動測試 payload</td>
          <td>只要能提供模型與目錄，就輸出可讀 statusline；缺 rate limit 時自動省略。</td>
      </tr>
  </tbody>
</table>
<p>這個切點保留了 Claude Code 既有功能，因為原本的欄位不需要改名，也不需要改設定檔。新增行為只在非 Claude payload 進來時啟動，屬於向後相容的讀取能力。</p>
<h2 id="操作路由">操作路由</h2>
<p>現在可立即使用的路由是 Claude Code 原設定：在 <code>~/.claude/settings.json</code> 裡設定 <code>statusLine.command</code> 指向 <code>cc-statusline</code>。這條路由使用官方支援的 stdin JSON，適合日常使用。</p>
<p>Codex 目前可立即使用的路由是內建 footer item：在 <code>~/.codex/config.toml</code> 設定 <code>tui.status_line = [...]</code>。這條路由使用 Codex 內建 renderer，能顯示 Codex 已支援的內建狀態。</p>
<p>未來 Codex 若支援 command-backed statusline，路由應該指向同一個 <code>cc-statusline</code> binary。工具端已經能接受 Codex / generic JSON 時，設定層只要補 command 指向，不需要重寫 renderer。</p>
<h2 id="實測記錄2026-05-14">實測記錄（2026-05-14）</h2>
<p>這次排查的核心責任是先確認「工具本身可用」還是「接入路由不對」。先把 binary 行為跟 TUI 設定拆開檢查，才能避免把路由問題誤判成程式 bug。</p>
<h3 id="觀察">觀察</h3>
<ul>
<li><code>cc-statusline</code> 程式已支援 generic/Codex-style payload，手動餵 JSON 可正確輸出模型與 context 資訊。</li>
<li><code>~/.claude/settings.json</code> 使用 <code>statusLine.command</code> 指向 <code>/Users/mac-eric/go/bin/cc-statusline</code>，Claude Code 路由成立。</li>
<li><code>~/.codex/config.toml</code> 的 <code>tui.status_line</code> 是內建 item 陣列，這條路由不會執行外部 binary。</li>
<li>Codex 內建 footer 的實際輸出已觀察到：<code>gpt-5.3-codex medium · Context 100% left · ~/project/blog</code>。</li>
</ul>
<h3 id="判讀">判讀</h3>
<p>Codex 端「沒有生效」的主因是契約邊界：<code>tui.status_line</code> 只負責排列內建欄位，不負責執行 command。<code>cc-statusline</code> 的 renderer 相容能力屬於預留未來入口，不會在現有 Codex 內建 footer 流程自動觸發。</p>
<h3 id="操作">操作</h3>
<p>為了讓 Codex 內建 footer 至少顯示模型與 context 資訊，已調整：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="p">[</span><span class="nx">tui</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nx">status_line</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;model-with-reasoning&#34;</span><span class="p">,</span> <span class="s2">&#34;context-remaining&#34;</span><span class="p">,</span> <span class="s2">&#34;current-dir&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nx">status_line_use_colors</span> <span class="p">=</span> <span class="kc">true</span></span></span></code></pre></div><p>這個設定可讓 Codex 使用內建項目顯示 <code>model-with-reasoning</code> 與 context remaining；格式由 Codex 內建 renderer 決定，不等同 <code>cc-statusline</code> 的自訂輸出字串。</p>
<h3 id="驗證指令">驗證指令</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="nb">printf</span> <span class="s1">&#39;%s\n&#39;</span> <span class="s1">&#39;{&#34;model&#34;:&#34;gpt-5.3-codex&#34;,&#34;reasoning_effort&#34;:&#34;medium&#34;,&#34;project_root&#34;:&#34;~/project/blog&#34;,&#34;context&#34;:{&#34;remaining_percent&#34;:100}}&#39;</span> <span class="p">|</span> /Users/mac-eric/go/bin/cc-statusline</span></span></code></pre></div><p>預期結果是主行包含 <code>gpt-5.3-codex medium</code>，context 顯示為 <code>Context 100% left</code>。這一步驗證的是 binary 能力，不是 Codex 內建 footer contract。</p>
<h2 id="檢查清單">檢查清單</h2>
<ul>
<li>Claude Code 原本的 JSON payload 仍能輸出相同欄位。</li>
<li>Codex / generic payload 不造成 parse error。</li>
<li><code>model</code> 同時支援 object 與 string。</li>
<li><code>context</code> 同時支援 used percentage 與 remaining percentage。</li>
<li>rate limit 缺席時只省略對應 segment，不影響專案、模型、git worktree。</li>
<li>README 明確標示 Codex 目前限制，避免讀者以為 Codex 已能直接執行外部 statusline command。</li>
</ul>
]]></content:encoded></item><item><title>Blog 文章模板設計：作者品質閘門與正文分工</title><link>https://tarrragon.github.io/blog/posts/blog-article-template-design/</link><pubDate>Sat, 02 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/posts/blog-article-template-design/</guid><description>&lt;h2 id="問題定位">問題定位&lt;/h2>
&lt;p>文章模板的責任是穩定作者流程，正文的責任是承載技術文章本身。讀者進入文章時，需要看到概念、判讀、取捨與路由；作者在寫作時，需要一組欄位檢查文章是否具備可維護的最低結構。&lt;/p>
&lt;p>本 blog 同時由人類作者、Claude Code 與 Codex 協作產生內容。模板若只放在單一 agent 的設定裡，就會形成工具分岔；模板若直接放進 backend 正文，又會把作者工作流暴露成讀者負擔。因此模板的單一真實來源放在 &lt;code>content/posts/&lt;/code>，作為本 blog 專屬的寫作設定記錄。&lt;/p>
&lt;h2 id="放置決策">放置決策&lt;/h2>
&lt;p>模板放置位置的核心判準是讀者與維護者是否一致。backend 文章面向技術讀者，report 面向可重用事後檢討，posts 面向 blog 自身的規範、設計與工具鏈紀錄。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>位置&lt;/th>
 &lt;th>適合內容&lt;/th>
 &lt;th>本議題判斷&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>content/backend/&lt;/code>&lt;/td>
 &lt;td>技術文章正文、概念推導、案例分析&lt;/td>
 &lt;td>保持讀者主線，作者模板留在上游&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>content/report/&lt;/code>&lt;/td>
 &lt;td>從具體 case 抽出的工程原則&lt;/td>
 &lt;td>可寫抽象原則，操作模板留在 posts&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>content/posts/&lt;/code>&lt;/td>
 &lt;td>blog 規範、設計決策、工具鏈契約&lt;/td>
 &lt;td>作為模板設計的 SSoT&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>.claude/&lt;/code>&lt;/td>
 &lt;td>Claude Code 執行規則&lt;/td>
 &lt;td>可引用本文，本文維持語意來源&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>.codex/&lt;/code>&lt;/td>
 &lt;td>Codex 執行規則&lt;/td>
 &lt;td>可引用本文，本文維持語意來源&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>這個分工讓模板同時支援 Claude Code 與 Codex，也保留文章對讀者的自然技術敘事。&lt;/p>
&lt;h2 id="模板責任">模板責任&lt;/h2>
&lt;p>模板是品質閘門，責任是讓文章保留關鍵判準。它要檢查文章是否具備責任、判讀、風險、邊界與下一步路由，正文仍用技術文章的推導順序排列。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>模板欄位&lt;/th>
 &lt;th>作者要回答的問題&lt;/th>
 &lt;th>正文呈現方式&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>責任&lt;/td>
 &lt;td>這篇文章解決哪一類工程問題&lt;/td>
 &lt;td>概念定位或開頭原則段&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>判讀&lt;/td>
 &lt;td>讀者如何知道自己遇到這個問題&lt;/td>
 &lt;td>核心判讀、判讀訊號、表格&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>風險&lt;/td>
 &lt;td>判讀錯誤或缺漏會造成什麼代價&lt;/td>
 &lt;td>風險段、反模式、情境段&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>邊界&lt;/td>
 &lt;td>這篇文章處理到哪裡，交給誰接續&lt;/td>
 &lt;td>交接路由、與其他章節分工&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>回寫&lt;/td>
 &lt;td>案例或事故教訓應回寫到哪個章節&lt;/td>
 &lt;td>下一步路由、復盤段&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>模板欄位可以出現在文章大綱、作者檢查清單或 agent brief 裡。正文要以技術文章方式展開，讓讀者看到推導，作者填表痕跡留在工作流內部。&lt;/p>
&lt;h2 id="backend-技術文章最小模板">Backend 技術文章最小模板&lt;/h2>
&lt;p>Backend 技術文章的最小模板是「概念定位 → 核心判讀 → 判讀訊號 → 風險與邊界 → 交接路由」。這組欄位適合 04 / 06 / 08 這類語言無關的後端能力文章。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="gu">## 概念定位
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">{概念} 是 {能力 / 流程 / 控制面}，責任是 {它在系統中承擔什麼問題}。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">這一頁處理的是 {抽象層級}。它跟 {相鄰章節} 的分工是 {邊界}。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="gu">## 核心判讀
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">判讀 {概念} 時，先看 {第一判準}，再看 {第二判準}。
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">重點訊號包括：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="k">-&lt;/span> {訊號 1}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="k">-&lt;/span> {訊號 2}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="k">-&lt;/span> {訊號 3}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">| 判讀面向 | 最小可用判準 | 常見失真 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">| --- | --- | --- |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">| {面向} | {判準} | {失真} |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">&lt;span class="gu">## 判讀訊號
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">23&lt;/span>&lt;span class="cl">&lt;span class="k">-&lt;/span> {真實服務中會看到的徵兆}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">24&lt;/span>&lt;span class="cl">&lt;span class="k">-&lt;/span> {工程團隊會踩到的操作問題}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">25&lt;/span>&lt;span class="cl">&lt;span class="k">-&lt;/span> {事故或演練會暴露的缺口}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">26&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">27&lt;/span>&lt;span class="cl">&lt;span class="gu">## 交接路由
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">28&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">29&lt;/span>&lt;span class="cl">&lt;span class="k">-&lt;/span> {上游章節}：{承接內容}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">30&lt;/span>&lt;span class="cl">- {下游章節}：{下一步處理}&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>這份模板只定義文章最低結構。若某篇文章需要完整案例、方案比較或實作步驟，正文可以增加章節；增加章節時仍要保留責任、判讀、風險與路由。&lt;/p>
&lt;h2 id="案例前置欄位">案例前置欄位&lt;/h2>
&lt;p>案例前置欄位的責任是讓服務案例能回寫到文章系統。它屬於作者拆案例時的內部欄位，正文只吸收欄位背後的判讀與取捨。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>欄位&lt;/th>
 &lt;th>用途&lt;/th>
 &lt;th>例子&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>來源&lt;/td>
 &lt;td>案例來自事故、演練或公開實踐&lt;/td>
 &lt;td>post-incident review、SRE case&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>觸發&lt;/td>
 &lt;td>事件如何被發現&lt;/td>
 &lt;td>alert、customer ticket、vendor status&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Evidence&lt;/td>
 &lt;td>判讀使用哪些證據&lt;/td>
 &lt;td>log、metric、trace、audit log&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Decision&lt;/td>
 &lt;td>當時做了什麼取捨&lt;/td>
 &lt;td>rollback、degradation、containment&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Impact&lt;/td>
 &lt;td>影響到誰與什麼功能&lt;/td>
 &lt;td>tenant、region、feature、financial impact&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>回寫&lt;/td>
 &lt;td>教訓回到哪個章節&lt;/td>
 &lt;td>04.17、6.20、8.19&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>04 / 06 / 08 的案例拆解可共用這組欄位，但正文仍要用技術文章敘事。欄位幫作者維持可比性，文章幫讀者理解機制。&lt;/p></description><content:encoded><![CDATA[<h2 id="問題定位">問題定位</h2>
<p>文章模板的責任是穩定作者流程，正文的責任是承載技術文章本身。讀者進入文章時，需要看到概念、判讀、取捨與路由；作者在寫作時，需要一組欄位檢查文章是否具備可維護的最低結構。</p>
<p>本 blog 同時由人類作者、Claude Code 與 Codex 協作產生內容。模板若只放在單一 agent 的設定裡，就會形成工具分岔；模板若直接放進 backend 正文，又會把作者工作流暴露成讀者負擔。因此模板的單一真實來源放在 <code>content/posts/</code>，作為本 blog 專屬的寫作設定記錄。</p>
<h2 id="放置決策">放置決策</h2>
<p>模板放置位置的核心判準是讀者與維護者是否一致。backend 文章面向技術讀者，report 面向可重用事後檢討，posts 面向 blog 自身的規範、設計與工具鏈紀錄。</p>
<table>
  <thead>
      <tr>
          <th>位置</th>
          <th>適合內容</th>
          <th>本議題判斷</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>content/backend/</code></td>
          <td>技術文章正文、概念推導、案例分析</td>
          <td>保持讀者主線，作者模板留在上游</td>
      </tr>
      <tr>
          <td><code>content/report/</code></td>
          <td>從具體 case 抽出的工程原則</td>
          <td>可寫抽象原則，操作模板留在 posts</td>
      </tr>
      <tr>
          <td><code>content/posts/</code></td>
          <td>blog 規範、設計決策、工具鏈契約</td>
          <td>作為模板設計的 SSoT</td>
      </tr>
      <tr>
          <td><code>.claude/</code></td>
          <td>Claude Code 執行規則</td>
          <td>可引用本文，本文維持語意來源</td>
      </tr>
      <tr>
          <td><code>.codex/</code></td>
          <td>Codex 執行規則</td>
          <td>可引用本文，本文維持語意來源</td>
      </tr>
  </tbody>
</table>
<p>這個分工讓模板同時支援 Claude Code 與 Codex，也保留文章對讀者的自然技術敘事。</p>
<h2 id="模板責任">模板責任</h2>
<p>模板是品質閘門，責任是讓文章保留關鍵判準。它要檢查文章是否具備責任、判讀、風險、邊界與下一步路由，正文仍用技術文章的推導順序排列。</p>
<table>
  <thead>
      <tr>
          <th>模板欄位</th>
          <th>作者要回答的問題</th>
          <th>正文呈現方式</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>責任</td>
          <td>這篇文章解決哪一類工程問題</td>
          <td>概念定位或開頭原則段</td>
      </tr>
      <tr>
          <td>判讀</td>
          <td>讀者如何知道自己遇到這個問題</td>
          <td>核心判讀、判讀訊號、表格</td>
      </tr>
      <tr>
          <td>風險</td>
          <td>判讀錯誤或缺漏會造成什麼代價</td>
          <td>風險段、反模式、情境段</td>
      </tr>
      <tr>
          <td>邊界</td>
          <td>這篇文章處理到哪裡，交給誰接續</td>
          <td>交接路由、與其他章節分工</td>
      </tr>
      <tr>
          <td>回寫</td>
          <td>案例或事故教訓應回寫到哪個章節</td>
          <td>下一步路由、復盤段</td>
      </tr>
  </tbody>
</table>
<p>模板欄位可以出現在文章大綱、作者檢查清單或 agent brief 裡。正文要以技術文章方式展開，讓讀者看到推導，作者填表痕跡留在工作流內部。</p>
<h2 id="backend-技術文章最小模板">Backend 技術文章最小模板</h2>
<p>Backend 技術文章的最小模板是「概念定位 → 核心判讀 → 判讀訊號 → 風險與邊界 → 交接路由」。這組欄位適合 04 / 06 / 08 這類語言無關的後端能力文章。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 概念定位
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</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">這一頁處理的是 {抽象層級}。它跟 {相鄰章節} 的分工是 {邊界}。
</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 class="gu">## 核心判讀
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">判讀 {概念} 時，先看 {第一判準}，再看 {第二判準}。
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">重點訊號包括：
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">-</span> {訊號 1}
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">-</span> {訊號 2}
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">-</span> {訊號 3}
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl">| 判讀面向 | 最小可用判準 | 常見失真 |
</span></span><span class="line"><span class="ln">18</span><span class="cl">| --- | --- | --- |
</span></span><span class="line"><span class="ln">19</span><span class="cl">| {面向} | {判準} | {失真} |
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="gu">## 判讀訊號
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="k">-</span> {真實服務中會看到的徵兆}
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="k">-</span> {工程團隊會踩到的操作問題}
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="k">-</span> {事故或演練會暴露的缺口}
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="gu">## 交接路由
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="k">-</span> {上游章節}：{承接內容}
</span></span><span class="line"><span class="ln">30</span><span class="cl">- {下游章節}：{下一步處理}</span></span></code></pre></div><p>這份模板只定義文章最低結構。若某篇文章需要完整案例、方案比較或實作步驟，正文可以增加章節；增加章節時仍要保留責任、判讀、風險與路由。</p>
<h2 id="案例前置欄位">案例前置欄位</h2>
<p>案例前置欄位的責任是讓服務案例能回寫到文章系統。它屬於作者拆案例時的內部欄位，正文只吸收欄位背後的判讀與取捨。</p>
<table>
  <thead>
      <tr>
          <th>欄位</th>
          <th>用途</th>
          <th>例子</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>來源</td>
          <td>案例來自事故、演練或公開實踐</td>
          <td>post-incident review、SRE case</td>
      </tr>
      <tr>
          <td>觸發</td>
          <td>事件如何被發現</td>
          <td>alert、customer ticket、vendor status</td>
      </tr>
      <tr>
          <td>Evidence</td>
          <td>判讀使用哪些證據</td>
          <td>log、metric、trace、audit log</td>
      </tr>
      <tr>
          <td>Decision</td>
          <td>當時做了什麼取捨</td>
          <td>rollback、degradation、containment</td>
      </tr>
      <tr>
          <td>Impact</td>
          <td>影響到誰與什麼功能</td>
          <td>tenant、region、feature、financial impact</td>
      </tr>
      <tr>
          <td>回寫</td>
          <td>教訓回到哪個章節</td>
          <td>04.17、6.20、8.19</td>
      </tr>
  </tbody>
</table>
<p>04 / 06 / 08 的案例拆解可共用這組欄位，但正文仍要用技術文章敘事。欄位幫作者維持可比性，文章幫讀者理解機制。</p>
<h2 id="agent-共用方式">Agent 共用方式</h2>
<p>Claude Code 與 Codex 共用模板時，本文是 blog 內的穩定契約。<code>.claude/</code> 與 <code>.codex/</code> 可以引用本文的欄位與判準，但實際模板語意以本文為準。</p>
<table>
  <thead>
      <tr>
          <th>使用者</th>
          <th>使用方式</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>人類作者</td>
          <td>寫作前確認文章是否需要這組欄位</td>
      </tr>
      <tr>
          <td>Claude Code</td>
          <td>依本文判斷正文與作者模板的分工</td>
      </tr>
      <tr>
          <td>Codex</td>
          <td>依本文建立、補寫與檢查 content 文章</td>
      </tr>
      <tr>
          <td>mdtools</td>
          <td>檢查 Markdown 結構與連結，語意模板交給作者流程</td>
      </tr>
  </tbody>
</table>
<p>這個安排避免 <code>.claude/</code> 與 <code>.codex/</code> 各自演化成不同模板，也避免把 agent 操作細節寫進 backend 讀者正文。</p>
<h2 id="使用邊界">使用邊界</h2>
<p>模板適合用在多篇系列文章、跨模組路由與案例回寫。單篇短文、事故紀錄或工具使用筆記可以採較輕的結構，只要仍能說清楚核心責任與下一步。</p>
<table>
  <thead>
      <tr>
          <th>情境</th>
          <th>使用方式</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Backend 能力章節</td>
          <td>使用完整最小模板</td>
      </tr>
      <tr>
          <td>服務案例拆解</td>
          <td>使用案例前置欄位，正文呈現案例判讀與取捨</td>
      </tr>
      <tr>
          <td>Blog 工具鏈規範</td>
          <td>依主題調整，保留 posts 的規範與工具鏈定位</td>
      </tr>
      <tr>
          <td>Report 原則卡</td>
          <td>依 report 固有結構，維持 case-driven 原則抽象</td>
      </tr>
      <tr>
          <td>Skill reference</td>
          <td>使用 skill 自身 portable 結構，維持跨專案可移植</td>
      </tr>
  </tbody>
</table>
<p>模板開始主導正文時，需要降級成作者檢查清單。文章完成後的檢查重點是讀者能否理解技術推導，並確認欄位已在文章背後支撐判讀。</p>
<h2 id="完稿檢查">完稿檢查</h2>
<p>完稿檢查的責任是確認技術文章維持主線。檢查時先看正文是否能獨立閱讀，再看欄位是否完整支撐交接。</p>
<ul>
<li>首段是否先說概念責任</li>
<li>判讀訊號是否來自真實服務情境</li>
<li>表格項目是否有延伸說明</li>
<li>交接路由是否指向具體章節</li>
<li>案例欄位是否能回寫到 04 / 06 / 08</li>
<li>正文是否保留技術推導，並把欄位轉成讀者可理解的判讀</li>
</ul>
]]></content:encoded></item><item><title>Blog Markdown 寫作規範與 mdtools 檢查</title><link>https://tarrragon.github.io/blog/posts/blog-markdown-%E5%AF%AB%E4%BD%9C%E8%A6%8F%E7%AF%84%E8%88%87-mdtools-%E6%AA%A2%E6%9F%A5/</link><pubDate>Fri, 24 Apr 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/posts/blog-markdown-%E5%AF%AB%E4%BD%9C%E8%A6%8F%E7%AF%84%E8%88%87-mdtools-%E6%AA%A2%E6%9F%A5/</guid><description>&lt;h2 id="這篇要解決什麼">這篇要解決什麼&lt;/h2>
&lt;p>隨著 blog 文章與知識卡片成長，純靠寫作紀律維持排版一致性越來越不可靠。反覆踩到的問題橫跨兩個層級：&lt;/p>
&lt;p>&lt;strong>結構與安全層級&lt;/strong>（這是工具鏈存在的主要理由）：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>裸 URL 在段落與表格中爆版&lt;/strong>（MD034），降低閱讀體驗。&lt;/li>
&lt;li>&lt;strong>表格管線風格混用&lt;/strong>（MD060），同一張表格有的有空白、有的沒有。&lt;/li>
&lt;li>&lt;strong>平行模板章節重複標題&lt;/strong>（MD024），例如多案例文章的 &lt;code>### 弱點環節&lt;/code> 出現 13 次。&lt;/li>
&lt;li>&lt;strong>顯示文字與實際 href 不一致&lt;/strong>（反釣魚）— 不在標準 markdownlint 規則內，但紅隊教材脈絡下必要。&lt;/li>
&lt;li>&lt;strong>卡片雙向完整性&lt;/strong>（orphan 卡片、斷連結、K4 合規）— 跨文件檢查，現成工具做不到。&lt;/li>
&lt;li>&lt;strong>Front matter schema&lt;/strong> — Hugo 依賴 YAML front matter 提供 title / date / weight 等欄位，缺失會破壞列表渲染、排序、SEO。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>基礎格式層級&lt;/strong>（容易被忽略但影響 parser 穩定性或語義結構）：&lt;/p>
&lt;ul>
&lt;li>正文禁止使用 H1（嚴於 MD025）— Hugo front matter &lt;code>title&lt;/code> 已產生 H1。&lt;/li>
&lt;li>標題前後需保留空行（MD022），parser 才能正確識別標題邊界。&lt;/li>
&lt;li>標題結尾禁止標點（MD026）— 例如 &lt;code>## 常見問題：&lt;/code> 應改為 &lt;code>## 常見問題&lt;/code>。&lt;/li>
&lt;li>禁止用 &lt;code>**bold**&lt;/code> 段落當標題（MD036）— 破壞語義階層與 TOC 產生。&lt;/li>
&lt;li>程式碼區塊需註明語言（MD040），影響 syntax highlighting 與 accessibility。&lt;/li>
&lt;li>列表前後需空行（MD032）、fenced code block 前後需空行（MD031）— 否則部分 parser 會把列表吃進段落。&lt;/li>
&lt;li>有序列表編號風格一致（MD029）— 全部 &lt;code>1.&lt;/code> 或全部 &lt;code>1./2./3.&lt;/code>。&lt;/li>
&lt;li>檔案結尾需有換行（MD047），POSIX 規範。&lt;/li>
&lt;li>行長度上限（MD013）— &lt;strong>預設關閉&lt;/strong>，中英混用技術寫作不適用 80-char 慣例。&lt;/li>
&lt;/ul>
&lt;p>前兩類混合在同一份寫作規範裡，因為都由同一個工具鏈檢查、都要落地到相同的 pre-commit hook。純靠紀律記住這十幾條在大型 repo 上不可行，純 regex 又無法穩定處理「平行結構下的標題重複」「卡片段落歸屬」這類語意判斷。因此 blog 專案採用 Go + goldmark AST 做自訂 linter：&lt;code>scripts/mdtools&lt;/code>。本文是 linter 與寫作規範的對齊文件；AGENTS.md 引用本文作為排版規範來源。&lt;/p>
&lt;hr>
&lt;h2 id="1-工具總覽">1. 工具總覽&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>子命令&lt;/th>
 &lt;th>職責&lt;/th>
 &lt;th>改檔&lt;/th>
 &lt;th>觸發時機&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>mdtools fmt [--fix|--check]&lt;/code>&lt;/td>
 &lt;td>格式正規化（URL、表格、空行、列表間距、trailing newline）&lt;/td>
 &lt;td>&lt;code>--fix&lt;/code> 會改&lt;/td>
 &lt;td>pre-commit（&lt;code>--fix&lt;/code>）、pre-push / CI（&lt;code>--check&lt;/code>）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>mdtools lint&lt;/code>&lt;/td>
 &lt;td>結構檢查（標題、反釣魚、code block 語言、front matter schema）&lt;/td>
 &lt;td>否&lt;/td>
 &lt;td>pre-commit、pre-push、CI&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>mdtools cards&lt;/code>&lt;/td>
 &lt;td>跨文件完整性（連結、orphan、K4）&lt;/td>
 &lt;td>否&lt;/td>
 &lt;td>pre-commit、pre-push、CI&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>工具原始碼在 &lt;code>scripts/mdtools/&lt;/code>，binary build 到 &lt;code>bin/mdtools&lt;/code>（已 gitignore）。&lt;/p>
&lt;p>作用範圍是 &lt;code>content/**/*.md&lt;/code>。&lt;code>public/&lt;/code>、&lt;code>themes/&lt;/code>、&lt;code>node_modules/&lt;/code> 等輸出或第三方資源不檢查。&lt;/p>
&lt;hr>
&lt;h2 id="2-標題規則">2. 標題規則&lt;/h2>
&lt;h3 id="21-標題結構與格式規則">2.1 標題結構與格式規則&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>正文禁止使用 H1&lt;/strong>。Hugo 的 front matter &lt;code>title&lt;/code> 會自動產生 H1，若正文再寫 &lt;code># ...&lt;/code> 會出現兩個 H1 並列，破壞語義階層與 SEO 訊號。正文一律從 H2 開始，最深到 H6。&lt;/li>
&lt;li>&lt;strong>同一父標題（直接上層）底下，子標題文字必須唯一&lt;/strong>（MD024 siblings_only 模式）。&lt;/li>
&lt;li>不同父標題底下，子標題允許重名。&lt;/li>
&lt;li>標題前後需保留空行（MD022），&lt;code>mdtools fmt --fix&lt;/code> 自動補。&lt;/li>
&lt;li>&lt;strong>標題結尾禁止標點&lt;/strong>（MD026）— 禁用字元：&lt;code>.&lt;/code>、&lt;code>,&lt;/code>、&lt;code>:&lt;/code>、&lt;code>;&lt;/code>、&lt;code>。&lt;/code>、&lt;code>，&lt;/code>、&lt;code>：&lt;/code>、&lt;code>；&lt;/code>。允許 &lt;code>?&lt;/code>、&lt;code>！&lt;/code>、&lt;code>？&lt;/code>、&lt;code>!&lt;/code> 作為語氣結尾。&lt;code>mdtools fmt --fix&lt;/code> 自動去除結尾禁用標點。&lt;/li>
&lt;li>&lt;strong>禁止用粗體當標題&lt;/strong>（MD036）— 若段落整段只由 &lt;code>**文字**&lt;/code> 或 &lt;code>*文字*&lt;/code> 組成，視為視覺性標題濫用。&lt;code>mdtools lint&lt;/code> 只報警、不自動修；作者需手動判斷正確的標題層級（通常是 H3 / H4）並改寫。&lt;/li>
&lt;/ul>
&lt;h3 id="22-補充範例md026-與-md036-的典型誤用">2.2 補充範例：MD026 與 MD036 的典型誤用&lt;/h3>
&lt;p>MD026（標題尾標點）常見誤用：&lt;/p></description><content:encoded><![CDATA[<h2 id="這篇要解決什麼">這篇要解決什麼</h2>
<p>隨著 blog 文章與知識卡片成長，純靠寫作紀律維持排版一致性越來越不可靠。反覆踩到的問題橫跨兩個層級：</p>
<p><strong>結構與安全層級</strong>（這是工具鏈存在的主要理由）：</p>
<ul>
<li><strong>裸 URL 在段落與表格中爆版</strong>（MD034），降低閱讀體驗。</li>
<li><strong>表格管線風格混用</strong>（MD060），同一張表格有的有空白、有的沒有。</li>
<li><strong>平行模板章節重複標題</strong>（MD024），例如多案例文章的 <code>### 弱點環節</code> 出現 13 次。</li>
<li><strong>顯示文字與實際 href 不一致</strong>（反釣魚）— 不在標準 markdownlint 規則內，但紅隊教材脈絡下必要。</li>
<li><strong>卡片雙向完整性</strong>（orphan 卡片、斷連結、K4 合規）— 跨文件檢查，現成工具做不到。</li>
<li><strong>Front matter schema</strong> — Hugo 依賴 YAML front matter 提供 title / date / weight 等欄位，缺失會破壞列表渲染、排序、SEO。</li>
</ul>
<p><strong>基礎格式層級</strong>（容易被忽略但影響 parser 穩定性或語義結構）：</p>
<ul>
<li>正文禁止使用 H1（嚴於 MD025）— Hugo front matter <code>title</code> 已產生 H1。</li>
<li>標題前後需保留空行（MD022），parser 才能正確識別標題邊界。</li>
<li>標題結尾禁止標點（MD026）— 例如 <code>## 常見問題：</code> 應改為 <code>## 常見問題</code>。</li>
<li>禁止用 <code>**bold**</code> 段落當標題（MD036）— 破壞語義階層與 TOC 產生。</li>
<li>程式碼區塊需註明語言（MD040），影響 syntax highlighting 與 accessibility。</li>
<li>列表前後需空行（MD032）、fenced code block 前後需空行（MD031）— 否則部分 parser 會把列表吃進段落。</li>
<li>有序列表編號風格一致（MD029）— 全部 <code>1.</code> 或全部 <code>1./2./3.</code>。</li>
<li>檔案結尾需有換行（MD047），POSIX 規範。</li>
<li>行長度上限（MD013）— <strong>預設關閉</strong>，中英混用技術寫作不適用 80-char 慣例。</li>
</ul>
<p>前兩類混合在同一份寫作規範裡，因為都由同一個工具鏈檢查、都要落地到相同的 pre-commit hook。純靠紀律記住這十幾條在大型 repo 上不可行，純 regex 又無法穩定處理「平行結構下的標題重複」「卡片段落歸屬」這類語意判斷。因此 blog 專案採用 Go + goldmark AST 做自訂 linter：<code>scripts/mdtools</code>。本文是 linter 與寫作規範的對齊文件；AGENTS.md 引用本文作為排版規範來源。</p>
<hr>
<h2 id="1-工具總覽">1. 工具總覽</h2>
<table>
  <thead>
      <tr>
          <th>子命令</th>
          <th>職責</th>
          <th>改檔</th>
          <th>觸發時機</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>mdtools fmt [--fix|--check]</code></td>
          <td>格式正規化（URL、表格、空行、列表間距、trailing newline）</td>
          <td><code>--fix</code> 會改</td>
          <td>pre-commit（<code>--fix</code>）、pre-push / CI（<code>--check</code>）</td>
      </tr>
      <tr>
          <td><code>mdtools lint</code></td>
          <td>結構檢查（標題、反釣魚、code block 語言、front matter schema）</td>
          <td>否</td>
          <td>pre-commit、pre-push、CI</td>
      </tr>
      <tr>
          <td><code>mdtools cards</code></td>
          <td>跨文件完整性（連結、orphan、K4）</td>
          <td>否</td>
          <td>pre-commit、pre-push、CI</td>
      </tr>
  </tbody>
</table>
<p>工具原始碼在 <code>scripts/mdtools/</code>，binary build 到 <code>bin/mdtools</code>（已 gitignore）。</p>
<p>作用範圍是 <code>content/**/*.md</code>。<code>public/</code>、<code>themes/</code>、<code>node_modules/</code> 等輸出或第三方資源不檢查。</p>
<hr>
<h2 id="2-標題規則">2. 標題規則</h2>
<h3 id="21-標題結構與格式規則">2.1 標題結構與格式規則</h3>
<ul>
<li><strong>正文禁止使用 H1</strong>。Hugo 的 front matter <code>title</code> 會自動產生 H1，若正文再寫 <code># ...</code> 會出現兩個 H1 並列，破壞語義階層與 SEO 訊號。正文一律從 H2 開始，最深到 H6。</li>
<li><strong>同一父標題（直接上層）底下，子標題文字必須唯一</strong>（MD024 siblings_only 模式）。</li>
<li>不同父標題底下，子標題允許重名。</li>
<li>標題前後需保留空行（MD022），<code>mdtools fmt --fix</code> 自動補。</li>
<li><strong>標題結尾禁止標點</strong>（MD026）— 禁用字元：<code>.</code>、<code>,</code>、<code>:</code>、<code>;</code>、<code>。</code>、<code>，</code>、<code>：</code>、<code>；</code>。允許 <code>?</code>、<code>！</code>、<code>？</code>、<code>!</code> 作為語氣結尾。<code>mdtools fmt --fix</code> 自動去除結尾禁用標點。</li>
<li><strong>禁止用粗體當標題</strong>（MD036）— 若段落整段只由 <code>**文字**</code> 或 <code>*文字*</code> 組成，視為視覺性標題濫用。<code>mdtools lint</code> 只報警、不自動修；作者需手動判斷正確的標題層級（通常是 H3 / H4）並改寫。</li>
</ul>
<h3 id="22-補充範例md026-與-md036-的典型誤用">2.2 補充範例：MD026 與 MD036 的典型誤用</h3>
<p>MD026（標題尾標點）常見誤用：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gu">#### 字型選擇說明：        ← 違規（結尾 `：`）
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gu"></span>#### 字型選擇說明          ← 合法</span></span></code></pre></div><p>中文寫作習慣用冒號引入後續內容，這個模式在「段首句」合理、在「標題」就不合理 — 標題本身的存在就暗示了後續有內容，冒號變成冗餘訊號。</p>
<p>MD036（粗體當標題）常見誤用：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">**字型選擇說明**           ← 違規：整段只有粗體，視覺像標題但不是真標題
</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">### 字型選擇說明           ← 合法：用正式的 H3 取代</span></span></code></pre></div><p>差異看起來微小，實際影響包含：Hugo TOC 不會抓到、卡片反向連結失效、screen reader 無法跳轉。這是「語義 vs 視覺」錯位的典型案例，AST linter 容易檢出（Paragraph 節點唯一子節點為 Strong/Emph）。</p>
<h3 id="23-為什麼採-siblings_only-而非全域唯一">2.3 為什麼採 siblings_only 而非全域唯一</h3>
<p>平行結構（多案例、多模板章節）的複用語義來自上層標題賦予的脈絡。例如：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gu">## 【案例一】Uber 2022
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gu">### 弱點環節        ← 合法
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="gu">### 攻擊路徑
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="gu">## 【案例二】Okta 2023
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="gu">### 弱點環節        ← 合法，因為在不同的父層下
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="gu"></span>### 攻擊路徑</span></span></code></pre></div><p>重名只有在同層並列時才代表結構錯誤。強制全域唯一會逼作者寫 <code>### 【案例二】弱點環節</code>，破壞平行結構的視覺一致性，收益並不大。</p>
<hr>
<h2 id="3-url-與連結規則">3. URL 與連結規則</h2>
<h3 id="31-裸-url-轉換mdtools-fmt---fix-自動處理">3.1 裸 URL 轉換（<code>mdtools fmt --fix</code> 自動處理）</h3>
<p>段落或表格儲存格內的裸 URL 會自動包成 markdown 連結。顯示文字依路徑可識別性分級：</p>
<table>
  <thead>
      <tr>
          <th>情境</th>
          <th>顯示文字</th>
          <th>範例（before → after）</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>路徑含識別碼（例如 CVE）</td>
          <td><code>domain.com/識別碼</code></td>
          <td><code>https://nvd.nist.gov/vuln/detail/CVE-2023-34362</code> → <code>[nvd.nist.gov/CVE-2023-34362](https://nvd.nist.gov/vuln/detail/CVE-2023-34362)</code></td>
      </tr>
      <tr>
          <td>路徑冗長但無識別性</td>
          <td><code>domain.com</code></td>
          <td><code>https://www.cisa.gov/news-events/alerts/2024/06/03/snowflake-recommends-...</code> → <code>[cisa.gov](https://www.cisa.gov/news-events/alerts/2024/06/03/snowflake-recommends-...)</code></td>
      </tr>
      <tr>
          <td>已是 markdown 連結</td>
          <td>不動</td>
          <td>—</td>
      </tr>
  </tbody>
</table>
<p>識別碼偵測用 regex 白名單，初始清單專注在高頻識別碼格式（例如 <code>CVE-YYYY-N</code>），其他格式以「遇到再加」原則擴充。清單維護在 <code>scripts/mdtools/internal/rules/identifiers.go</code>。</p>
<h3 id="32-反釣魚校驗mdtools-lint-強制檢查">3.2 反釣魚校驗（<code>mdtools lint</code> 強制檢查）</h3>
<p>Markdown 語法允許顯示文字與實際 href 完全不符，這是釣魚攻擊的結構基礎。本規則在 AST 層阻擋此模式。</p>
<ul>
<li><strong>R-URL-1（URL 樣顯示文字一致性）</strong>：若顯示文字含 <code>.com</code> / <code>.org</code> / <code>.gov</code> / <code>.net</code> / <code>.io</code> / <code>.dev</code> / <code>.tw</code> 等 TLD 字樣，則顯示文字的 domain 必須等於 href 的 domain（含子網域比對）。</li>
<li><strong>R-URL-2（描述型顯示文字自由）</strong>：顯示文字不含 TLD 字樣時，視為人類可讀描述，不做 domain 比對。</li>
</ul>
<p>違規範例（會被 lint 阻擋）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">[<span class="nt">nvd.nist.gov</span>](<span class="na">https://malicious.example.com/fake</span>)     ← 顯示文字暗示 NVD，href 卻不是
</span></span><span class="line"><span class="ln">2</span><span class="cl">[<span class="nt">cisa.gov/advisory</span>](<span class="na">https://cisa-gov.evil.example</span>)     ← 顯示文字抄 CISA 格式，domain 不符</span></span></code></pre></div><p>合法範例：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">[<span class="nt">Uber 事件公告</span>](<span class="na">https://www.uber.com/newsroom/security-update/</span>)
</span></span><span class="line"><span class="ln">2</span><span class="cl">[<span class="nt">nvd.nist.gov/CVE-2023-34362</span>](<span class="na">https://nvd.nist.gov/vuln/detail/CVE-2023-34362</span>)</span></span></code></pre></div><p>這條規則在紅隊 / 安全相關教材中特別重要：讀者本來就該對來源警戒，排版規則不該削弱這個警戒訊號。縮短顯示文字提升可讀性，反釣魚校驗守住安全底線，兩者互補。</p>
<h3 id="33-例外情境">3.3 例外情境</h3>
<ul>
<li><strong>程式碼區塊</strong>（fenced code block，<code>```</code> 包圍）內的 URL <strong>不做任何處理</strong>（不縮短、不校驗）。代碼範例經常需要展示完整 URL 給讀者複製執行。</li>
<li><strong>引用區塊</strong>（<code>&gt;</code> 開頭）內的 URL <strong>比照段落處理</strong>，會縮短也會做反釣魚校驗。</li>
</ul>
<hr>
<h2 id="4-表格規則">4. 表格規則</h2>
<ul>
<li>統一使用 <strong>aligned 風格</strong>：每欄內容用空白補齊到該欄的最大寬度，使 <code>|</code> 在 monospace 渲染下垂直對齊。</li>
<li>欄位分隔線使用 <code>| --- |</code> 形式，不含對齊冒號 <code>:</code>（分隔線內的 <code>-</code> 數量跟隨該欄寬度自動填足）。</li>
<li>寬度計算使用顯示寬度（display width）— CJK 字元佔 2 欄寬、ASCII 佔 1 欄寬，分隔列與資料列按同一套寬度對齊。</li>
<li><code>mdtools fmt --fix</code> 自動正規化：插入新行或改動欄寬時會全表重算，作者不需手工維持對齊。</li>
</ul>
<p>選 aligned 而非 compact 的理由是<strong>原始檔可讀性</strong>：技術教材的表格常需在 code review 裡對照，aligned 風格讓 reviewer 直接看出哪些欄位對應哪些內容，不用在腦中解析鋸齒狀的 pipes。手工對齊在長表格反覆編輯時確實會失效（新增一行就全表要重對齊），但這正是 <code>mdtools fmt --fix</code> 接手的地方。</p>
<hr>
<h2 id="5-基礎格式細節">5. 基礎格式細節</h2>
<p>這節整理容易被忽略、但會影響 parser 正確性或渲染品質的小規則。</p>
<h3 id="51-程式碼區塊必須註明語言md040">5.1 程式碼區塊必須註明語言（MD040）</h3>
<p>由 <code>mdtools lint</code> 檢查。未註明語言的 fenced code block 會被報警：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">``<span class="sb">`                   ← 違規：缺語言標示
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="sb">func main() {
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="sb">    fmt.Println(&#34;hi&#34;)
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="sb">}
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="sb">`</span>`<span class="sb">`
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="sb">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="sb">`</span>`<span class="sb">`go                 ← 合法
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="sb">func main() {
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="sb">    fmt.Println(&#34;hi&#34;)
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="sb">}
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="sb">`</span>``</span></span></code></pre></div><p>純文字輸出（例如 terminal output、log 片段）使用 <code>text</code> 或 <code>plain</code>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="s">```text
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="s"></span>Error: permission denied
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="s">```</span></span></span></code></pre></div><p>Shell 範例統一用 <code>bash</code>（即使是 zsh 語法，讓 syntax highlighter 有合理預設）；純設定檔依實際格式（<code>toml</code>、<code>yaml</code>、<code>json</code>、<code>ini</code>）。</p>
<h3 id="52-fenced-code-block-前後需空行md031">5.2 fenced code block 前後需空行（MD031）</h3>
<p>由 <code>mdtools fmt --fix</code> 自動處理。缺空行會讓前後段落被 parser 併入 code block 或反之。</p>
<h3 id="53-列表前後需空行md032">5.3 列表前後需空行（MD032）</h3>
<p>由 <code>mdtools fmt --fix</code> 自動處理。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">上一段結束。
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="k">-</span> 列表項一           ← 違規：列表前無空行，會被部分 parser 當段落延續
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="k">-</span> 列表項二
</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">上一段結束。
</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 class="k">-</span> 列表項一           ← 合法
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">-</span> 列表項二
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">下一段開始。</span></span></code></pre></div><h3 id="54-有序列表編號一致性md029">5.4 有序列表編號一致性（MD029）</h3>
<p>由 <code>mdtools fmt --fix</code> 正規化。本專案採 <code>ordered</code> 風格（全部遞增編號）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">1.</span> 第一步
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="k">2.</span> 第二步           ← 合法
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="k">3.</span> 第三步
</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"><span class="k">1.</span> 第一步
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="k">1.</span> 第二步           ← 違規：混用風格（fmt --fix 會改成 1./2./3.）
</span></span><span class="line"><span class="ln">7</span><span class="cl">2. 第三步</span></span></code></pre></div><p>選擇 <code>ordered</code> 的理由：原始檔可讀性高，作者直接看到步驟數；插入新項目的對齊代價比全部重新渲染低。</p>
<h3 id="55-段落間空行">5.5 段落間空行</h3>
<p>段落之間、標題前後、列表與段落之間都需空行。<code>mdtools fmt --fix</code> 會自動規範化多餘 / 缺失的空行，作者不需手工維護。</p>
<h3 id="56-檔案結尾需有換行md047">5.6 檔案結尾需有換行（MD047）</h3>
<p>POSIX 文字檔規範；缺失時 git diff 會出現 <code>\ No newline at end of file</code>。<code>mdtools fmt --fix</code> 自動補。</p>
<h3 id="57-tab-字元md010-僅限-fenced-code-block">5.7 Tab 字元（MD010）— 僅限 fenced code block</h3>
<p>由 <code>mdtools lint</code> 檢查（warn 等級）。Prose / 列表 / 表格 / 引用等非 code-block 行內若出現 tab 字元，會被標記並建議改成空白；fenced code block 內的 tab 保留（Go 原始碼依 gofmt 慣例用 tab，文章要讓讀者能直接複製貼用）。</p>
<p>Repo 根目錄的 <code>.markdownlint.json</code> 用 <code>&quot;MD010&quot;: { &quot;code_blocks&quot;: false }</code> 告知 IDE 的 markdownlint extension 採用同一套 policy，讓編輯器跟 CI 的警告保持一致。</p>
<h3 id="58-行長度上限md013-預設關閉">5.8 行長度上限（MD013）— 預設關閉</h3>
<p>本規則<strong>預設關閉</strong>。中英混用的技術寫作不適用 80-char 慣例：</p>
<ul>
<li>中文每字元算 1 個寬度時，80-char ≈ 40 個中文字，寫到一半就要斷行，嚴重影響可讀性。</li>
<li>中文每字元算 2 個寬度時，80-char 相當於 20-30 個中文字，更離譜。</li>
<li>Markdown 編輯器普遍支援軟斷行與 IDE word wrap，實體行長度對閱讀體驗影響小。</li>
</ul>
<p>若未來需要打開（例如發現真的有人寫出 2000-char 單行段落），建議上限 <strong>400 字元</strong>（軟上限，warn 不阻擋）。設定在 <code>scripts/mdtools/internal/rules/config.go</code> 的 <code>LineLengthLimit</code> 欄位。</p>
<h3 id="59-裝飾符號禁用emoji--視覺記號">5.9 裝飾符號禁用（emoji / 視覺記號）</h3>
<blockquote>
<p><strong>本節本身豁免</strong>：規範要描述「哪些符號禁用」必然要列舉這些符號（use-mention distinction）。本節舉例的 emoji 屬 mention（指稱）、非 use（裝飾使用）、不違反規則。掃描指令會 hit 到本節、判讀時跳過。</p></blockquote>
<p><code>content/**</code> 正文不可使用 emoji（如 ✅ ❌ ⚠️ 🚨 🟡 🟢 ⭐ 📌 💡 ⚡ 🎯）與裝飾性 unicode 符號（✓ ✗ ✘）。<strong>表格、列表、行內標記都不行</strong>。</p>
<p><strong>替換策略</strong>（emoji 承載的語意要回到文字結構、不是純粹刪除符號）：</p>
<table>
  <thead>
      <tr>
          <th>原寫法</th>
          <th>改成</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>表格 status <code>| ✅ 解了 |</code></td>
          <td>純文字描述：「解了」/「是」/「適用」</td>
      </tr>
      <tr>
          <td>表格 status <code>| ❌ 漏 |</code></td>
          <td>純文字描述：「漏」/「否」/「不適用」</td>
      </tr>
      <tr>
          <td>列表優缺點 <code>- ✅ 簡單</code> / <code>- ❌ 慢</code></td>
          <td>拆成 <code>**優點**：簡單</code> / <code>**缺點**：慢</code> 段落或標題段</td>
      </tr>
      <tr>
          <td>列表錯誤示範 <code>- ❌ 把 key 寄 email</code> / <code>- ✅ 用 CSR</code></td>
          <td>拆成 <code>**錯誤做法**：</code> / <code>**正確做法**：</code> 標題段</td>
      </tr>
      <tr>
          <td>行內視覺強調 <code>🚨 critical</code></td>
          <td>markdown 粗體 <code>**critical**</code> 或引用塊 <code>&gt; **critical**：...</code></td>
      </tr>
  </tbody>
</table>
<p><strong>理由</strong>：</p>
<ul>
<li><strong>Grep-ability</strong>：emoji 無法用 plain text grep 命中；視覺結構容易掩蓋語意結構、reviewer 看不出「優 / 缺」是用 emoji 區分還是用標題段區分</li>
<li><strong>CLI parser 相容性</strong>：部分 multi-byte emoji 在 Rust-based CLI 工具（如某些 mdtools / pagefind / lint pipeline）觸發 char-boundary panic</li>
<li><strong>跨語境穩定</strong>：emoji 在不同字型 / 平台 / 終端機渲染差異大、容易斷行或顯示為框</li>
</ul>
<p><strong>掃描指令</strong>（提交前自己跑一次、有 hit 就替換）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">rg <span class="s2">&#34;✅|❌|⚠️|🚨|🟡|🟢|🔴|🟠|🔵|⭐|📌|💡|⚡|🎯|✨|📝|🔍|🛠|⛔|✓|✗|✘&#34;</span> content/</span></span></code></pre></div><blockquote>
<p>本規則目前<strong>未進 <code>mdtools lint</code> 自動掃描</strong>、靠人工 grep。未來會加進 lint pipeline。</p></blockquote>
<hr>
<h3 id="510-位置引用與數量命名候選掃描ref1--ref2警告層">5.10 位置引用與數量命名候選掃描（REF1 / REF2、警告層）</h3>
<p><code>mdtools lint</code> 對 <code>content/**</code> 跑兩個警告層掃描、來源是引用紀律卡（<a href="/blog/report/reference-by-semantic-title-not-number/" data-link-title="引用章節用語意標題、不用位置編號：編號是結構排列的 derivation、會隨版本漂移" data-link-desc="跨段落、跨檔引用結構單位（章節 / 階段 / 條列項）時、引用語意標題（副標題）、不引用位置編號（Stage 3、第 5 章、第 3 點）。編號是「目前結構排列」的 derivation、不是 fact；結構重排時編號全部位移、引用點不會報錯、而是 silent 指向錯的內容 — 比 broken link 更難偵測。標題的存在意義就是承載可被引用的語意。是 #44 SSoT 在結構引用維度的實例、#93 identifier-as-fact 家族的 sibling、#84 命名承載語意的引用面延伸。">#155 引用章節用語意標題</a>、<a href="/blog/report/name-collections-by-role-not-count/" data-link-title="集合命名用角色、不內嵌數量：「核心七問」的七是成員數的 derivation、加一問就全面失真" data-link-desc="「核心七問」「成長六階段」「四大支柱」這類名稱把成員數量烤進名字裡 — 數量是集合當前成員的 derivation、不是集合的語意身分；成員增減時名稱失真、且名稱是被複製最多次的字串、缺陷隨每次引用繁殖。修法：命名只承載角色與層級（核心問題 / 次要問題 / 撞牆階段）、數量讓清單自己呈現。本卡是 #155 的命名端 sibling（#155 修引用端、本卡讓「語意標題是穩定錨」的前提真正成立）、#44 SSoT 在名稱內容的實例、#84 命名檢驗的數量維度。">#156 集合命名用角色</a>）：</p>
<ul>
<li><strong>REF1-positional-anchor</strong>：正文中的位置式引用候選 —「見第 3 點」「詳見第五章」「§4」。位置編號是當下排列的衍生值、目標是活文件時、結構重排會讓引用 silent 指向錯的內容。</li>
<li><strong>REF2-count-in-name</strong>：標題與 front matter <code>title</code> 中內嵌成員數的集合命名 —「六大原則」「遷移五階段流程」。成員增減時名稱先失真、且名稱是被複製最多次的字串。</li>
</ul>
<p>兩個規則都停在警告層、<strong>命中是候選、不是判決</strong> — 回報前要做語意判定：</p>
<table>
  <thead>
      <tr>
          <th>命中情境</th>
          <th>判定</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>引用發布方凍結的編號（RFC 段號、法條條號）</td>
          <td>合規、編號是 fact</td>
      </tr>
      <tr>
          <td>數字緊鄰它描述的清單（「確認三件事：」）</td>
          <td>合規、漂移在編輯當下可見</td>
      </tr>
      <tr>
          <td>外部凍結品牌名（SOLID 五原則）</td>
          <td>合規、數量由發布方凍結</td>
      </tr>
      <tr>
          <td>目標是內部活文件 / 內部活集合</td>
          <td>改語意標題引用 / 角色命名</td>
      </tr>
  </tbody>
</table>
<p>掃描器內建兩個自動豁免：被 <code>「」</code> 包住的命中視為反例引用、直接跳過；「第」開頭的序數（第三階段）不屬 REF2 的集合命名。既有內容的命中屬歷史基線、依「changed-set scoped lint」原則處理 — 新增與修改的檔案要判讀、存量警告分開回報。</p>
<h3 id="511-否定起手候選掃描pos-negation-lead警告層">5.11 否定起手候選掃描（POS-negation-lead、警告層）</h3>
<p><code>mdtools lint</code> 對 <code>content/**</code> 跑否定起手掃描、來源是 <a href="/blog/report/lead-with-the-point-cross-language/" data-link-title="重點優先陳述是跨語言的資訊結構原則、不是中文句型問題" data-link-desc="正向陳述優先的本質是資訊結構效率：讀者拿到核心概念的認知步驟越少越好。「不是 X、而是 Y」表達能力差、是因為它讓讀者先處理一個被否定的錯誤理解 X 才拿到正確的 Y、重點後置多繞一步。這個缺陷跨語言成立——英文 not X but Y、日文 X ではなく Y 同樣高頻、換語言不打破（證偽過的反例假設）。判別線是「核心概念在不在最前」、統一了 #94（重點先行合法）與 #149（重點後置違規）、且可操作。LLM 系統性放水的根因是高頻偏置（把語料高頻句型評為表達好、高頻不等於資訊結構優、跨語言）。主解是強制執行重點位置判準、#165 的異源視角降為補充。">#166 重點優先陳述是跨語言的資訊結構原則</a>（搭配 <a href="/blog/report/register-violation-needs-cross-style-eyes/" data-link-title="register 違規：偵測可機械化、判定要靠文體異源的眼睛" data-link-desc="寫作規範的違規分兩類：形式違規（emoji / 編號 / broken link）可完全機械判定、該進工具鏈；register / 品味違規（概念前置 / 否定起手 / 喊話 / 誇飾）的判定有不可消除的品味核心。「不是 X、而是 Y」的陷阱是偵測可機械化（grep 抓得到句型）偽裝成判定可機械化、誘導無限投入更精緻的判定方法（grep → 概念位置 → 行為測試）、但判定始終在品味側、始終放水。更深一層：產出這類違規的 LLM 跟審查它的 LLM 共享文體直覺、同源自審對 register 違規有結構上限、加再多輪次都跨不過。結構解是引入文體異源的視角（人類冷讀 / reader-simulation / 對抗文體 reviewer）、並接受 100% 自動 catch 不可能。">#165 register 違規要文體異源</a>）：</p>
<ul>
<li><strong>POS-negation-lead</strong>：正文中「不是 X、而是 Y」「不是 X — 是 Y」「與其 X、不如 Y」的重點後置候選。核心概念（Y）被擠到「而是 / 不如」之後、讀者要先處理一個被否定的 X 才拿到重點。這是資訊結構效率問題、跨語言成立（英文「not X but Y」、日文「X ではなく Y」）、不是中文特有句型 — 偵測可機械化、判定不可。pattern 涵蓋的連接詞（而是 / 「— 是」/ 不如）枚舉不完、判準是「核心概念在不在句首」而非哪個連接詞 — 漏掉的變體只是讓候選 silent 到有人讀到（規則第一版就漏了「不是 X — 是 Y」、靠人發現才補）。</li>
</ul>
<p>判定用「重點位置」、<strong>命中是候選、不是判決</strong>：</p>
<table>
  <thead>
      <tr>
          <th>命中情境</th>
          <th>判定</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>核心概念第一次正面出現在句首（「有深度、不是非黑即白的二元」）</td>
          <td>合規、重點先行</td>
      </tr>
      <tr>
          <td>明示反例 / 對照段落內的否定（見 <a href="/blog/report/positive-rewrite-preserves-contrast/" data-link-title="正向改寫要保留對照論據、不能空降結論" data-link-desc="把「X、不是 Y」改成正向陳述時、若直接刪除「不是 Y」會讓結論失去推理依據、變成空降斷言。對照論據（Y 是讀者直覺會想到的替代方案）跟結論（X）是同一個推理單元、抽掉一半就不成立。正向改寫的合法做法是補解釋、改成對照表、或保留 Y 當對比錨點 — 直接拿掉是 worst。">#94</a>）</td>
          <td>合規、否定是對照本體</td>
      </tr>
      <tr>
          <td>核心概念被擠到「而是」之後（「不是二元、而是有深度」）</td>
          <td>改正向、把核心概念移到句首</td>
      </tr>
  </tbody>
</table>
<p>掃描器豁免兩類命中：被 <code>「」</code> 包住的引用（反例引用 / 句型佔位符）、以及 backtick 行內程式碼內的 pattern（grep regex / 技術識別碼）。講這個句型的 meta 卡（#165 / #166）與本規範段落會大量自我觸發 — 屬候選人判定豁免、跟 REF1 / REF2 對 #155 / #156 一致。既有內容的命中（全 <code>content/</code> 約數百個）屬歷史基線、依「changed-set scoped lint」原則處理 — 偵測把候選池曝光、判定漸進進行、存量警告分開回報。</p>
<h2 id="6-front-matter-schemamdtools-lint">6. Front matter schema（<code>mdtools lint</code>）</h2>
<p>Hugo 依賴 YAML front matter 提供 title / date / weight 等欄位給 render pipeline。缺欄位會讓列表頁、排序、SEO 壞掉，但 Hugo 本身不會失敗（靜默接受不完整資料），所以必須由 linter 守住。</p>
<h3 id="61-通用層contentmd">6.1 通用層（<code>content/**/*.md</code>）</h3>
<p>所有內容文章必須有：</p>
<ul>
<li><code>title</code>：字串，不可空。</li>
<li><code>date</code>：<code>YYYY-MM-DD</code> 格式（ISO 8601 date）。</li>
</ul>
<p><strong>Hugo <code>_index.md</code> section 頁面例外</strong>：這類檔案是 Hugo 的 section 列表 landing page，不是內容文章，沒有語意上的「日期」。只要求 <code>title</code>，不強制 <code>date</code>。</p>
<h3 id="62-推薦層警告不阻擋">6.2 推薦層（警告，不阻擋）</h3>
<p>推薦填寫（<code>mdtools lint</code> warn level）：</p>
<ul>
<li><code>description</code>：字串，建議 30–150 字，影響 SEO 與列表頁預覽。</li>
<li><code>tags</code>：陣列，至少 1 個標籤。</li>
</ul>
<p>推薦層是歷史內容的緩衝區，不是新增內容的放行條件。新增文章必須同時填寫 <code>description</code> 與 <code>tags</code>；修改既有文章時，若同一檔案缺少推薦欄位，應在同次變更補齊，避免每次驗證都被舊 warning 淹沒。</p>
<p>驗證時先跑 changed-set scoped lint 判斷本次變更品質，再視需要跑 full lint 觀察整體基線。回報 full lint 結果時，要把歷史 warning、已知 warning 與本次新增問題分開描述。</p>
<h3 id="63-卡片嚴格層contentbackendknowledge-cards">6.3 卡片嚴格層（<code>content/backend/knowledge-cards/**</code>）</h3>
<p>知識卡片額外要求（對應 <code>.codex/briefs/knowledge-cards.md</code> K2）：</p>
<ul>
<li><code>title</code>、<code>date</code>、<code>description</code> 必填。</li>
<li><code>weight</code>：整數，決定在 <code>_index.md</code> 主題表格中的排序位置。</li>
</ul>
<h3 id="64-禁止欄位">6.4 禁止欄位</h3>
<p>以下欄位存在時 <code>mdtools lint</code> 警告（避免語義混淆）：</p>
<ul>
<li><code>author</code>：本專案為單作者 blog，統一於 Hugo 設定。</li>
<li><code>permalink</code>：使用 Hugo 預設路徑規則，避免手動覆蓋。</li>
</ul>
<p>若未來需要鬆綁，在 <code>scripts/mdtools/internal/rules/frontmatter.go</code> 的 <code>DisallowedFields</code> 清單調整。</p>
<h3 id="65-slug-必填跟檔名對齊">6.5 slug 必填、跟檔名對齊</h3>
<p>所有 content 文章 frontmatter 必須有 <code>slug</code> 欄位，值跟檔名（不含 <code>.md</code>）對齊。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w"></span><span class="nt">title</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;視覺手段對齊錯誤層次&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w"></span><span class="nt">slug</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;visual-tool-error-layer-alignment&#34;</span><span class="w">   </span><span class="c"># 跟檔名對齊</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w"></span><span class="nt">date</span><span class="p">:</span><span class="w"> </span><span class="ld">2026-04-28</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w"></span><span class="nn">---</span></span></span></code></pre></div><p><strong>為什麼必填</strong>：</p>
<p>slug 是 URL 的核心識別、跨多個工具共用（Hugo build、mdtools lint、跨檔 markdown link、search index）。若不顯式定義，slug 散落在三處推導鏈：</p>
<table>
  <thead>
      <tr>
          <th>來源</th>
          <th>推導值</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Hugo 預設（從 title 用 urlize）</td>
          <td>runtime 推導、隨 hugo 版本變化</td>
      </tr>
      <tr>
          <td>mdtools 字面比對</td>
          <td>檔名 stem</td>
      </tr>
      <tr>
          <td>跨檔連結時的引用</td>
          <td>寫作者手動算 / 複製</td>
      </tr>
  </tbody>
</table>
<p>三個推導鏈不一致時 = silent broken link（mdtools pass 但 hugo build 後 404、或反過來）。把 slug 升成 frontmatter 顯式 fact、所有工具基於同一 source、消除 derivation 鏈。</p>
<p>詳細論述見 <a href="/blog/report/url-slug-must-be-explicit-fact/" data-link-title="URL slug 必須顯式定義為 fact：跨工具 identifier 用單一定義源" data-link-desc="URL slug 在 Hugo 預設下從 title 自動推導、在 mdtools lint 下從檔名讀、在跨檔連結時又要寫第三個值 — 一個 identifier 散落在三個推導鏈、典型 SSoT 違反。當多個工具共用一個 identifier、推導不一致 = silent broken link。修法：把 slug 從 derivation（runtime 推導）升級成 fact（frontmatter 顯式定義）、檔名 / 連結都基於這個 fact。本卡是 #44 在 toolchain integration 情境的具體實例、是 #82 字面 vs 行為在 identifier 維度的展現。">report #93 URL slug 必須顯式定義為 fact</a>。</p>
<p><strong>檔名對齊規則</strong>：</p>
<ul>
<li>檔名命名建議：英文小寫、kebab-case 或 snake_case、不含中文（避免 hugo <code>urlize</code> 規則跨版本變動）</li>
<li>slug 值 == 檔名 stem（不含 <code>.md</code>）</li>
<li>修檔名時必須同步修 slug；修 slug 時必須同步 rename 檔案</li>
</ul>
<p><strong>Hugo <code>_index.md</code> 例外</strong>：section 列表頁已有 <code>slug:</code> 欄位指定資料夾路徑、不適用本規則。</p>
<hr>
<h2 id="7-卡片雙向完整性mdtools-cards">7. 卡片雙向完整性（<code>mdtools cards</code>）</h2>
<p>作用範圍：<code>content/**/*.md</code>，重點關注 <code>content/backend/knowledge-cards/</code>。</p>
<table>
  <thead>
      <tr>
          <th>層級</th>
          <th>規則</th>
          <th>實作</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>L1 連結有效性</strong></td>
          <td>所有相對連結 <code>[...](/posts/markdown-writing-spec/path)</code> / <code>[...](/posts/path)</code> 的目標檔案必須存在</td>
          <td>AST 抽 Link node → 解析相對路徑 → stat 檔案</td>
      </tr>
      <tr>
          <td><strong>L2 卡片 orphan 偵測</strong></td>
          <td>每張卡片至少被 <code>content/**</code> 中一篇非卡片正文引用</td>
          <td>建反向索引 → 找無 incoming edge 的卡片</td>
      </tr>
      <tr>
          <td><strong>L4 卡片 K4 結構合規</strong></td>
          <td>卡片首段與「概念位置」段各至少 1 個相鄰卡片連結</td>
          <td>AST 定位段落節點 → 統計子樹 Link 數</td>
      </tr>
  </tbody>
</table>
<p>L3（正文首次出現術語必須連結到卡片）暫不納入，待術語字典（<code>.codex/briefs/knowledge-web-expansion.md</code>）啟動後再開。</p>
<h3 id="為什麼要做跨文件檢查">為什麼要做跨文件檢查</h3>
<p>知識卡片是 blog 的核心知識資產。隨著卡片數量增加：</p>
<ul>
<li><strong>Orphan 卡片</strong>（沒有正文連結進來）會變成知識死角，讀者無法發現。</li>
<li><strong>斷掉的相對連結</strong>（檔案被改名或移動）肉眼難以發現，只有讀者點擊失敗才暴露。</li>
<li><strong>K4 合規</strong>（首段 + 概念位置段要有鄰卡連結）保證卡片間的知識網不會鬆散。</li>
</ul>
<p>這些檢查用 regex 做都卡在「段落歸屬怎麼判斷」。AST 天生知道節點的父子結構，做起來自然。</p>
<hr>
<h2 id="8-執行時機">8. 執行時機</h2>
<h3 id="pre-commit-hookgithookspre-commit">Pre-commit hook（<code>.githooks/pre-commit</code>）</h3>
<ol>
<li><code>mdtools fmt --fix</code> — 自動修格式；改動會 <code>git add</code> 回 staged，避免改完又沒進 commit。</li>
<li><code>mdtools lint</code> — 結構檢查；失敗阻擋 commit。</li>
<li><code>mdtools cards</code> — 完整性檢查；失敗阻擋 commit。</li>
</ol>
<h3 id="pre-push-hookgithookspre-push">Pre-push hook（<code>.githooks/pre-push</code>）</h3>
<p><code>pre-push</code> 的責任是把 CI 同款全量檢查提前到本機。<code>pre-commit</code> 為了速度只處理 staged markdown；<code>pre-push</code> 會跑 <code>make check</code>，也就是 <code>mdtools fmt --check content/</code>、<code>mdtools lint content/</code>、<code>mdtools cards content/</code>，讓整個 <code>content/</code> 的格式與連結 drift 在推送前被攔下。</p>
<p>啟用 hook：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">git config core.hooksPath .githooks
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># 或：make install-hooks</span></span></span></code></pre></div><h3 id="cigithubworkflowsmd-checkyml">CI（<code>.github/workflows/md-check.yml</code>）</h3>
<p>三個子命令都跑 <code>--check</code> / 嚴格模式，任何違規 fail CI。</p>
<hr>
<h2 id="9-寫作者使用指引">9. 寫作者使用指引</h2>
<ul>
<li>寫作時優先遵循本規範。pre-commit / pre-push 報錯時讀訊息修正；<strong>不可用 <code>git commit --no-verify</code> 或跳過 hook 的方式繞過檢查</strong>。</li>
<li>新增案例平行章節（例如多個「工具評測」「事件時序」）時不需登記到任何白名單 — siblings_only 自動判讀。</li>
<li>新增 URL 時優先採用裸 URL 轉換段的分級形式；若顯示文字含 TLD 字樣，確認 domain 與 href 完全一致。</li>
<li>新增卡片時確認首段與「概念位置」段各有至少一個相鄰卡片連結（L4 要求）；確認 front matter 含 <code>title</code> / <code>date</code> / <code>description</code> / <code>weight</code>（卡片嚴格層）。</li>
<li>程式碼區塊養成習慣先寫語言標示再填內容；純文字輸出用 <code>text</code>。</li>
</ul>
<hr>
<h2 id="10-規則擴充流程">10. 規則擴充流程</h2>
<p>新規則進入本文的路徑：</p>
<ol>
<li>先在 <code>scripts/mdtools/internal/rules/</code> 實作為可開關的 rule（預設關）。</li>
<li>在代表性檔案上測試誤判率。</li>
<li>誤判率 &lt; 1% 且有明確教材品質收益時，預設開啟並更新本文。</li>
<li>預設開啟後同步修正既有違規；若違規數量大，可分批 PR。</li>
</ol>
<hr>
<h2 id="11-為什麼自訂而不是用現成-markdownlint">11. 為什麼自訂而不是用現成 markdownlint</h2>
<p><code>markdownlint-cli2</code> 的 MD022 / MD024 / MD026 / MD029 / MD031 / MD032 / MD034 / MD036 / MD040 / MD047 / MD060 這些基礎規則都有（MD013 預設關閉、MD025 本規範嚴於原版），為什麼還要自寫？</p>
<p>關鍵差在<strong>卡片雙向完整性</strong>、<strong>反釣魚校驗</strong>、<strong>Front matter schema</strong> 這三類檢查，屬於跨文件 / AST 層 / 業務邏輯層的自訂邏輯，現成 linter 無法表達。這些檢查是 blog 品質的核心訊號，必須跟基礎格式檢查放在同一個工具鏈、同一次 AST parse 內處理，避免多個工具重複解析、重複維護。</p>
<p>另外 goldmark 是 Hugo 內建的 markdown parser。用同一個 parser 做 lint 保證「lint 通過 → Hugo render 一致」，杜絕兩套 parser 解讀不同的長尾 bug。</p>
<hr>
<p>本文為 blog 專案 Markdown 寫作規範的單一真實來源。repo 根目錄的 <code>AGENTS.md</code> 引用本文作為排版規範權威，規則與 <code>scripts/mdtools</code> 實作保持同步。</p>
]]></content:encoded></item><item><title>5W1H 自覺決策方法論：系統化決策框架</title><link>https://tarrragon.github.io/blog/record/5w1h-%E8%87%AA%E8%A6%BA%E6%B1%BA%E7%AD%96%E6%96%B9%E6%B3%95%E8%AB%96%E7%B3%BB%E7%B5%B1%E5%8C%96%E6%B1%BA%E7%AD%96%E6%A1%86%E6%9E%B6/</link><pubDate>Wed, 04 Mar 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/5w1h-%E8%87%AA%E8%A6%BA%E6%B1%BA%E7%AD%96%E6%96%B9%E6%B3%95%E8%AB%96%E7%B3%BB%E7%B5%B1%E5%8C%96%E6%B1%BA%E7%AD%96%E6%A1%86%E6%9E%B6/</guid><description>&lt;p>和 AI 協作開發的早期，我們遇到一個讓人沮喪的問題：相同的功能一再被重複實作。某個模組已經有 &lt;code>BookValidator&lt;/code>，但另一個任務裡 AI 又建了一個新驗證類別，因為它沒先問「這功能是否已存在？」更糟的是，遇到棘手問題時，我們（包括 AI）會下意識找「簡單的方法」迴避難題，用臨時解法掩蓋設計缺陷。&lt;/p>
&lt;p>這才建立了 5W1H 自覺決策方法論：在建立任何任務之前，必須強制回答六個問題。不是「可以回答」，而是「必須回答」，任何一個缺失都阻止任務建立。&lt;/p></description><content:encoded><![CDATA[<p>和 AI 協作開發的早期，我們遇到一個讓人沮喪的問題：相同的功能一再被重複實作。某個模組已經有 <code>BookValidator</code>，但另一個任務裡 AI 又建了一個新驗證類別，因為它沒先問「這功能是否已存在？」更糟的是，遇到棘手問題時，我們（包括 AI）會下意識找「簡單的方法」迴避難題，用臨時解法掩蓋設計缺陷。</p>
<p>這才建立了 5W1H 自覺決策方法論：在建立任何任務之前，必須強制回答六個問題。不是「可以回答」，而是「必須回答」，任何一個缺失都阻止任務建立。</p>
<h2 id="為什麼直覺判斷不夠用">為什麼直覺判斷不夠用</h2>
<p>直覺判斷的門檻太低。任何模糊的想法都可能直接變成任務被付諸實作，等發現重複了或架構位置錯了，才知道白花了時間。</p>
<p>5W1H 框架在任務建立的那一刻設置強制思考關卡。只有通過六個問題，任務才能被建立。</p>
<h2 id="六個問題六個層次的自覺">六個問題，六個層次的自覺</h2>
<h3 id="who責任歸屬防止重複實作">Who：責任歸屬，防止重複實作</h3>
<p>Who 問的不只是「誰來做」，更重要的是「有沒有人已經做過了」。在我們的協作模式中，Who 必須明確標記執行者和分派者兩個角色，格式如：「<code>parsley-flutter-developer</code>（執行者）由 <code>rosemary-project-manager</code>（分派者）分派」。</p>
<p>這個格式強制一個邊界：主線程不應自己寫程式碼，職責是設計任務、分派任務、驗收結果。如果任務標記為主線程執行但類型是程式碼實作，系統直接阻止。</p>
<p>執行 Who 前要先做四步檢查：搜尋現有 Domain 是否有相同功能，確認既有 Service 是否已實作，查看測試覆蓋是否涵蓋，最後才決定是重用還是新建。Domain 中已有相同功能就禁止新建，責任不明就禁止執行。這兩條規則消滅了大部分重複實作。</p>
<h3 id="what功能定義單一職責的守門員">What：功能定義，單一職責的守門員</h3>
<p>What 要求明確說出具體的輸入輸出、業務行為和異常處理。合格的 What 必須能一句話描述完且無法再拆分。</p>
<p>「書籍處理——包含驗證、儲存、查詢等」不合格。「驗證輸入字串是否符合 ISBN-10 或 ISBN-13 格式，輸入 <code>String isbn</code>，輸出 <code>ValidationResult</code>，空字串拋出 <code>ValidationError</code>」才是。</p>
<p>職責不清就禁止執行，與既有功能重疊就整合，包含多個不相關職責就拆分。</p>
<h3 id="when觸發時機副作用要完整識別">When：觸發時機，副作用要完整識別</h3>
<p>When 要求明確說出觸發事件，不是「書籍資料需要驗證的時候」，而是「使用者完成 ISBN 條碼掃描，觸發 <code>ISBNScannedEvent</code>，副作用包含觸發書籍資訊查詢和更新 UI 狀態，整合到 <code>ScanTask</code> 聚合根的事件系統」。</p>
<p>副作用未識別是 When 的紅線。連鎖反應沒有提前想清楚，後來就是難以追蹤的 bug。</p>
<h3 id="where架構位置確保層級正確">Where：架構位置，確保層級正確</h3>
<p>Where 決定功能在 Clean Architecture 中的位置。不是「在需要的地方執行」，而是「Domain Layer，<code>BookValidator</code> 位於 <code>Book Aggregate</code>，由 <code>AddBookUseCase</code> 呼叫，呼叫路徑是 UI → UseCase → Domain → Validator」。</p>
<p>位置錯誤就重新定位，不能為了方便就地執行。跨層級混亂就重新架構，不妥協。</p>
<h3 id="why動機驗證識別逃避性開發">Why：動機驗證，識別逃避性開發</h3>
<p>Why 是六個問題中最重要的，因為它直接對抗最常見的開發陷阱——逃避。</p>
<p>合格的 Why 必須有需求文件編號，能說明具體的使用者價值和場景。「系統需要更多驗證」不合格，它沒有需求依據，而且可能只是為了逃避更難的問題。</p>
<p>遇到這些語言會立即阻止：「先做簡單的」（逃避複雜問題）、「順便加個功能」（缺乏需求）、「優化一下效能」（可能在迴避功能問題）。</p>
<h3 id="how實作策略任務類型的強制標記">How：實作策略，任務類型的強制標記</h3>
<p>How 要求明確實作策略，並強制標記任務類型，格式是 <code>[Task Type: XXX]</code>。六種類型：Implementation（程式碼實作，必須由執行代理人執行）、Dispatch（任務分派，主線程執行）、Review（驗收，主線程執行）、Documentation、Analysis、Planning。</p>
<p>這個標記讓 Who 和 How 的合規性可以被自動檢查：How 標記為 Implementation 但執行者是主線程，系統直接阻止；Dispatch 類型的任務執行者是代理人，同樣阻止。</p>
<p>「先建立基本功能之後再加測試」是直接違規，任何臨時解法都禁止。</p>
<h2 id="who-和-how-的聯動檢查">Who 和 How 的聯動檢查</h2>
<p>正確組合：Implementation 搭配執行代理人，Dispatch/Review 搭配主線程。違規組合：Implementation 搭配主線程（主線程不應寫程式碼），Dispatch 搭配代理人，缺少執行者/分派者標記。</p>
<p>這個機制解決了一個真實痛點：主線程在時間壓力下直接修改程式碼，繞過代理人執行的設計。有了 How 的任務類型標記和 Who 的格式要求，這種行為在任務建立階段就被攔截。</p>
<h2 id="逃避語言清單">逃避語言清單</h2>
<p>框架持續更新一份逃避語言清單，分五類：</p>
<ul>
<li>品質妥協：「太複雜」「先將就」「暫時性修正」「症狀緩解」、&ldquo;workaround&rdquo;、&ldquo;hack&rdquo;、&ldquo;quick fix&rdquo;</li>
<li>簡化妥協：「更簡單的方法」「簡化處理」——通常意味著在迴避真正的設計問題</li>
<li>擱置問題：「架構問題先不管」「技術債務之後處理」——發現了卻不解決，最危險</li>
<li>測試妥協：「簡化測試」「基本測試即可」</li>
<li>模糊詞彙：沒有具體指標的「優化」「自動」</li>
</ul>
<p>偵測到任何逃避語言，系統立即阻止並要求修正。</p>
<h2 id="hook-系統讓規則有牙齒">Hook 系統：讓規則有牙齒</h2>
<p>框架的執行依賴 Hook 系統。TodoWrite 工具使用前，Hook 檢查 5W1H 完整性；任何一個 W 或 H 缺失，操作立即阻止；發現逃避語言，進入修復模式。</p>
<p>修復機制：阻止操作 → 明確說明缺失哪個項目 → 要求補充 → 再次驗證。</p>
<h2 id="這個框架改變了什麼">這個框架改變了什麼</h2>
<p>最明顯的感受是對話品質變了。以前任務可能只有「實作書籍驗證功能」；現在必須帶著完整的 Who（誰來做、是否已存在）、What（具體輸入輸出）、When（何時觸發、有哪些副作用）、Where（架構層）、Why（需求來源）、How（任務類型和策略）才能被建立。</p>
<p>很多模糊的想法在回答六個問題的過程中就自然消失了——它們根本過不了關。</p>]]></content:encoded></item><item><title>Clean Architecture 實作指引</title><link>https://tarrragon.github.io/blog/record/clean-architecture-%E5%AF%A6%E4%BD%9C%E6%8C%87%E5%BC%95/</link><pubDate>Wed, 04 Mar 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/clean-architecture-%E5%AF%A6%E4%BD%9C%E6%8C%87%E5%BC%95/</guid><description>&lt;p>定義敏捷開發方法論時，我們需要一個明確的判斷基準：哪些任務屬於同一層？哪些必須依序完成？哪些可以並行？&lt;/p>
&lt;p>我們選擇了 Clean Architecture。它不只是架構模式，更是一套「責任分層」語言，讓我們和 AI 代理人都能用同一套詞彙討論誰該負責哪個部分、什麼時候可以動手。&lt;/p></description><content:encoded><![CDATA[<p>定義敏捷開發方法論時，我們需要一個明確的判斷基準：哪些任務屬於同一層？哪些必須依序完成？哪些可以並行？</p>
<p>我們選擇了 Clean Architecture。它不只是架構模式，更是一套「責任分層」語言，讓我們和 AI 代理人都能用同一套詞彙討論誰該負責哪個部分、什麼時候可以動手。</p>
<h2 id="核心原則依賴只能由外向內">核心原則：依賴只能由外向內</h2>
<p>Clean Architecture 最核心的一句話是「依賴只能由外向內」。內層不知道外層的存在，外層依賴內層定義的介面。業務邏輯因此得以獨立，換資料庫、換 UI 框架都不需要動到核心規則。</p>
<p>架構由內到外分為四層：</p>
<p><strong>Entities（核心業務規則）</strong> 封裝核心業務概念，例如書籍、訂單、使用者。定義實體屬性和業務不變量的驗證，不依賴任何框架或資料庫。</p>
<p><strong>Use Cases（應用業務邏輯）</strong> 協調 Entities 之間的互動，定義系統功能。同時定義 Input Port、Output Port、Repository Port，只依賴 Entities 和這些自己定義的介面。</p>
<p><strong>Interface Adapters（介面轉接層）</strong> 橋接業務邏輯和外部技術。Controller 接收外部請求並轉換為 Use Case 輸入，Presenter 格式化 Use Case 輸出，Repository 實作在這一層開始成形。</p>
<p><strong>Frameworks &amp; Drivers（框架與外部系統）</strong> 包含所有技術細節——資料庫、UI 框架、第三方服務。可以隨時替換，完全不影響內層。</p>
<h2 id="依賴反轉原則dip讓這一切成立">依賴反轉原則（DIP）讓這一切成立</h2>
<p>Use Case 需要存取資料，但不能直接依賴 SQLite。正確做法是：在 Use Cases 層定義 Repository Port（抽象介面），Use Case 只依賴這個介面。真正的資料庫實作在最外層，它去實作這個介面。</p>
<p>依賴方向因此被反轉：資料庫實作依賴 Use Case 定義的介面，而非 Use Case 依賴資料庫。最後在 Composition Root 把具體實作注入進去，這就是依賴注入的本質。</p>
<h2 id="設計從內到外實作從外到內">設計從內到外，實作從外到內</h2>
<p>這是我們在 AI 協作中最重要的一個認知。</p>
<h3 id="設計階段由內到外">設計階段由內到外</h3>
<ol>
<li>設計 Entities：識別業務實體、定義 Value Objects（ISBN、Title 這類有驗證規則的值物件），在建構子中驗證業務不變量。</li>
<li>設計 Use Cases：定義 Input Port、Output Port、Repository Port。確定業務邏輯需要什麼，但不決定如何實作。</li>
<li>設計 Interface Adapters：Controller 如何轉換外部請求、Presenter 如何格式化輸出。</li>
<li>設計 Frameworks：選擇資料庫方案，實作 Repository。到這步才碰具體技術。</li>
</ol>
<h4 id="實作階段由外到內">實作階段由外到內</h4>
<ol>
<li>先定義所有 Ports。在寫任何實作之前，確立 Use Case 介面和 Repository 介面，這是系統的骨架。</li>
<li>外層用 Mock 介面開發和測試。Controller 在 Use Case 還沒真正實作前就能測試，因為它依賴的是抽象介面。</li>
<li>補完內層實作：Interactor 實作業務邏輯，Repository 存取真正的資料庫。</li>
<li>Composition Root 組裝依賴注入，系統就能真正執行。</li>
</ol>
<p>設計階段確保業務邏輯不被技術細節污染，實作階段讓每一層都能獨立開發和測試。</p>
<h2 id="驗證架構是否正確">驗證架構是否正確</h2>
<p>每個 Phase 完成後，我們對照以下幾個方向確認。</p>
<h3 id="依賴方向">依賴方向</h3>
<ul>
<li>Entities 沒有任何外層的 import</li>
<li>Use Cases 只 import Entities 和自己定義的介面</li>
<li>Frameworks 層實作的是 Interface Adapters 定義的介面</li>
</ul>
<h4 id="介面契約">介面契約</h4>
<ul>
<li>Repository Port 定義在 Use Cases 層（不是 Frameworks 層）</li>
<li>Repository 回傳的是 Entity 而不是資料庫 DTO</li>
<li>Input/Output Port 沒有洩漏框架的型別（例如 HTTP Request、SQLite Row）</li>
</ul>
<h5 id="業務邏輯位置">業務邏輯位置</h5>
<ul>
<li>業務不變量的驗證在 Entity 建構子</li>
<li>應用層邏輯在 Use Case Interactor</li>
<li>Controller 只負責轉換和呼叫，不包含業務判斷</li>
</ul>
<h6 id="interface-driven-development">Interface-Driven Development</h6>
<ul>
<li>所有 Ports 在設計階段就定義完成</li>
<li>外層真的用 Mock 介面在開發測試</li>
<li>組裝注入在 Composition Root 統一處理</li>
</ul>
<h2 id="為什麼這對-ai-協作特別有價值">為什麼這對 AI 協作特別有價值</h2>
<p>傳統開發中，架構邊界的維護依賴工程師的經驗，容易在壓力下妥協。AI 代理人則非常適合執行有明確規則的框架——規則夠清楚，它就能持續一致地遵守。</p>
<p>分層邊界就是這樣的規則。告訴代理人「這個 class 屬於 Use Cases 層，所以不能 import 任何 Framework 層的東西」，代理人就能機械性地驗證和維護這個邊界。</p>
<p>這套語言也讓任務拆分有了清楚的依據。「這個功能需要修改 Entity 和 Use Case，但不涉及 Repository 實作」是一個清楚的任務描述，對應到具體的修改範圍，不會模糊。</p>
<p>從實際經驗來看，引入 Clean Architecture 之後，AI 的實作結果更容易預測，測試覆蓋率更容易維持，架構審查也更有效率。代價是設計階段需要更多前置思考，但這個投資通常值得。</p>]]></content:encoded></item><item><title>Code Smell 品質閘門檢測方法論</title><link>https://tarrragon.github.io/blog/record/code-smell-%E5%93%81%E8%B3%AA%E9%96%98%E9%96%80%E6%AA%A2%E6%B8%AC%E6%96%B9%E6%B3%95%E8%AB%96/</link><pubDate>Wed, 04 Mar 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/code-smell-%E5%93%81%E8%B3%AA%E9%96%98%E9%96%80%E6%AA%A2%E6%B8%AC%E6%96%B9%E6%B3%95%E8%AB%96/</guid><description>&lt;p>Ticket 設計完成後才發現職責不清、範圍過大——這時程式碼都已經寫了，修正成本才是真正的問題所在。&lt;/p>
&lt;p>我們在專案中建立了一套設計階段品質閘門：&lt;strong>在 Ticket 進入 Phase 2 之前強制執行 C1/C2/C3 三項檢測。&lt;/strong> 問題在文字階段就抓出來，修正只需幾分鐘；等到程式碼都寫好了，才是幾個小時起跳的代價。&lt;/p></description><content:encoded><![CDATA[<p>Ticket 設計完成後才發現職責不清、範圍過大——這時程式碼都已經寫了，修正成本才是真正的問題所在。</p>
<p>我們在專案中建立了一套設計階段品質閘門：<strong>在 Ticket 進入 Phase 2 之前強制執行 C1/C2/C3 三項檢測。</strong> 問題在文字階段就抓出來，修正只需幾分鐘；等到程式碼都寫好了，才是幾個小時起跳的代價。</p>
<h2 id="三層檢測標準">三層檢測標準</h2>
<p><strong>C1 God Ticket</strong>：超過 10 個檔案、跨越超過 2 個架構層、或預估超過 16 小時，判定過大，必須拆分。</p>
<p><strong>C2 Incomplete Ticket</strong>：驗收條件少於 3 個可量化項目、缺少測試規劃、未定義工作日誌名稱、無參考文件連結——任一缺失，Ticket 不完整。</p>
<p><strong>C3 Ambiguous Responsibility</strong>：標題缺少層級標示（如 <code>[Layer 5]</code>）、目標含複合職責（出現「和」或「或」）、步驟用「相關檔案」而非具體路徑、驗收條件跨層——任一模糊，需重新定義。</p>
<h2 id="檢測順序的設計邏輯">檢測順序的設計邏輯</h2>
<p>固定執行順序：<strong>C1 → C3 → C2</strong>。</p>
<p>C1 最先，因為一旦判定 God Ticket，拆分後產生多個新 Ticket，C2/C3 的分析全部作廢要重來。C3 排第二，職責不清的 Ticket，補充驗收條件也會是模糊的。C2 最後，在範圍合理、職責明確的基礎上，確認必要元素齊全。</p>
<h2 id="執行流程">執行流程</h2>
<h3 id="step-1c1-god-ticket-檢測">Step 1：C1 God Ticket 檢測</h3>
<p>從 Ticket 的步驟章節提取所有檔案路徑，計算檔案數量。接著使用檔案路徑分析法判斷每個檔案所屬的架構層：</p>
<ul>
<li><code>lib/presentation/widgets/</code>、<code>lib/presentation/pages/</code> 屬於 Layer 1（UI 層）</li>
<li><code>lib/presentation/controllers/</code>、<code>lib/presentation/view_models/</code> 屬於 Layer 2（行為層）</li>
<li><code>lib/application/use_cases/</code>、<code>lib/application/services/</code> 屬於 Layer 3（UseCase 層）</li>
<li><code>lib/domain/repositories/</code>（介面）、<code>lib/domain/events/</code> 屬於 Layer 4（Domain 介面層）</li>
<li><code>lib/domain/entities/</code>、<code>lib/domain/value_objects/</code>、<code>lib/infrastructure/</code> 屬於 Layer 5（Domain 實作層）</li>
</ul>
<p>層級跨度 = 最高層 - 最低層。如果有檔案分佈在 Layer 1 到 Layer 5，層級跨度就是 4，遠超過 2 的上限。</p>
<p>預估工時根據步驟數量與複雜度係數評估：步驟數 × 平均每步時間 × 複雜度係數（1.0 到 2.0）。</p>
<p>檢測失敗時，依序嘗試三種拆分策略：</p>
<ol>
<li>按層級拆分（優先）：將跨層的 Ticket 按架構層拆成多個</li>
<li>按職責拆分（次要）：將工時過長的 Ticket 按職責邊界拆分</li>
<li>按功能模組拆分（最終）：將職責過多的 Ticket 按功能模組拆分</li>
</ol>
<p>拆分後的每個子 Ticket 都需要重新執行 C1 檢測。</p>
<h3 id="step-2c3-ambiguous-responsibility-檢測">Step 2：C3 Ambiguous Responsibility 檢測</h3>
<p>確認四個要素：</p>
<ul>
<li><strong>層級標示</strong>：標題包含 <code>[Layer X]</code> 標籤</li>
<li><strong>職責描述</strong>：目標章節無「和」或「或」的複合描述</li>
<li><strong>檔案範圍</strong>：步驟列出具體路徑，而非「相關檔案」</li>
<li><strong>驗收限定</strong>：驗收條件不出現跨層項目</li>
</ul>
<p>任一不符，依序修正：明確層級 → 重寫職責 → 列出具體檔案 → 限定驗收範圍。</p>
<h3 id="step-3c2-incomplete-ticket-檢測">Step 3：C2 Incomplete Ticket 檢測</h3>
<p>確認四個必要元素都存在：</p>
<ul>
<li><strong>驗收條件</strong>：3 個以上可量化、可驗證的項目（「程式碼品質提升」這種描述不算）</li>
<li><strong>測試規劃</strong>：明確的測試檔案路徑和對應的測試項目</li>
<li><strong>工作日誌</strong>：定義好工作日誌的檔案名稱（而非「待填寫」）</li>
<li><strong>參考文件</strong>：至少 1 個有效的文件連結</li>
</ul>
<p>缺少哪個就補哪個，沒有例外。</p>
<h3 id="step-4提交審查">Step 4：提交審查</h3>
<p>所有 Ticket 通過 C1/C2/C3 之後，生成品質閘門檢測報告，提交 PM 審查。PM 批准後才能進入 Phase 2。</p>
<h2 id="阻斷機制與執行時機">阻斷機制與執行時機</h2>
<p>任何 Ticket 未通過 C1/C2/C3，<strong>禁止進入 Phase 2</strong>。不是建議，是硬性規定。問題立即修正，所有過程寫入工作日誌。</p>
<p>執行時機有三個：設計完成後立即檢測、PM 分派前確認報告、PM 審查時以報告作依據。三個時機缺一不可——缺了任何一個，閘門就會有漏網之魚。</p>
<h2 id="實際效益">實際效益</h2>
<p>最直接的改變是：開發者接到 Ticket 時就已經有明確範圍、清楚職責、具體驗收標準，不需要反覆確認。</p>
<p>更重要的是，因為修正成本真的很低，團隊才會認真對待每一次品質閘門，而不是當作走形式。一旦修正成本高，就會開始討價還價、局部修正、累積技術債——品質閘門也就形同虛設。</p>]]></content:encoded></item><item><title>Ticket 生命週期管理方法論</title><link>https://tarrragon.github.io/blog/record/ticket-%E7%94%9F%E5%91%BD%E9%80%B1%E6%9C%9F%E7%AE%A1%E7%90%86%E6%96%B9%E6%B3%95%E8%AB%96/</link><pubDate>Wed, 04 Mar 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/ticket-%E7%94%9F%E5%91%BD%E9%80%B1%E6%9C%9F%E7%AE%A1%E7%90%86%E6%96%B9%E6%B3%95%E8%AB%96/</guid><description>&lt;p>某個深夜追查 Bug，打開 Ticket 系統看到的是：「修復書籍搜尋問題」、「要加那個功能」、「上次說的那個」。沒有驗收條件，沒有步驟，只有模糊標題。Reviewer 不知道該查什麼，開發者不確定自己算不算完成，PM 無法判斷能不能關掉。&lt;/p>
&lt;p>這讓我們認真思考：Ticket 不只是一張便利貼，它是任務的完整生命週期。&lt;/p></description><content:encoded><![CDATA[<p>某個深夜追查 Bug，打開 Ticket 系統看到的是：「修復書籍搜尋問題」、「要加那個功能」、「上次說的那個」。沒有驗收條件，沒有步驟，只有模糊標題。Reviewer 不知道該查什麼，開發者不確定自己算不算完成，PM 無法判斷能不能關掉。</p>
<p>這讓我們認真思考：Ticket 不只是一張便利貼，它是任務的完整生命週期。</p>
<h2 id="四個狀態一條主線">四個狀態，一條主線</h2>
<p><strong>待執行（Pending）</strong>：Ticket 準備好但還沒人開始。進入條件是標題格式正確、五個核心欄位填寫完整、前置依賴都已完成。</p>
<p><strong>進行中（In Progress）</strong>：核心紀律是即時記錄，遇到問題就寫進日誌，不要等到最後才回憶。我們設的建議上限是 8 小時——超過通常代表 Ticket 太大，需要拆分。</p>
<p><strong>Review 中（In Review）</strong>：第三方驗證。重點聚焦在驗收條件：逐項打勾，要麼通過要麼不通過。建議 1-2 小時內完成，避免卡住流程。</p>
<p><strong>已完成（Completed）</strong>：不可逆的終態。進入條件：所有驗收條件打勾、Review 通過、測試 100% 通過、靜態分析 0 錯誤、工作日誌更新。缺一不可。</p>
<h2 id="ticket-標題動詞加目標">Ticket 標題：動詞加目標</h2>
<p>標準格式是「動詞 + 目標」。動詞選擇反映任務類型：「定義」用於介面或規範，「撰寫」用於測試文件，「實作」用於具體功能，「修復」用於 Bug，「重構」用於改善結構。</p>
<p>對比：「做 Repository」和「實作 SQLiteBookRepository」——後者讓人在領取前就知道工作規模。</p>
<h2 id="五個核心欄位">五個核心欄位</h2>
<p>每張 Ticket 必須有這五個欄位：</p>
<p><strong>背景</strong>說明為什麼需要這個任務，讓三個月後的自己也能理解來龍去脈，不用去問當初的人。</p>
<p><strong>目標</strong>一句話說清楚要達成什麼，建議不超過 30 字。可驗證的目標：「實作 SQLiteBookRepository，符合 IBookRepository 介面，通過所有單元測試」。不可驗證的：「優化資料儲存」。</p>
<p><strong>步驟</strong>列 3-5 個具體動作，每個指明操作對象和預期結果。目的是讓人一領取就知道從哪裡開始。</p>
<p><strong>驗收條件</strong>是任務的契約，必須客觀可勾選。不是「程式碼品質良好」，而是「dart analyze 0 錯誤」；不是「測試通過」，而是「覆蓋率 100%，通過率 100%」。</p>
<p><strong>參考文件</strong>連結設計文件、需求規格、相關 Ticket，讓執行者有完整上下文。</p>
<h2 id="執行的七個步驟">執行的七個步驟</h2>
<p><strong>領取</strong>前先確認依賴都完成，工作量合理再標記進行中。</p>
<p><strong>閱讀</strong>背景和目標，把參考文件看完再動手。</p>
<p><strong>執行</strong>時遇到問題即時記進日誌。</p>
<p><strong>自檢</strong>時逐項勾選驗收條件，全過才進下一步。</p>
<p><strong>提交</strong>時標記為 Review，附上自我檢查結果。</p>
<p><strong>處理 Review 結果</strong>，通過則關閉，未通過根據反饋修正再提交。</p>
<p><strong>記錄經驗</strong>——這一步最容易省略，也最有長期價值。Ticket 完成時回答：學到了什麼？有什麼可改進的地方？是否需要建立 error-pattern 防止重複？知識沉澱進系統，才不會只留在某個人腦子裡。</p>
<h2 id="阻塞狀態的積極處理">阻塞狀態的積極處理</h2>
<p>Blocked 最容易被誤用：標記了就等著。我們的原則是阻塞不超過 24 小時，必須採取行動。</p>
<p>行動方式取決於原因：缺前置條件就派發前置任務；技術問題不清楚就建調查任務；需要決策就建評估任務。把「被動等待」轉成「積極解除」，每個阻塞都變成一個有人負責的子任務。</p>
<h2 id="結語">結語</h2>
<p>從「修復那個問題」到「修復書籍搜尋在 ISBN 格式不一致時回傳空結果的問題」，這個距離比看起來近。只需要一點紀律，和對「完成」這件事更認真的態度。</p>]]></content:encoded></item><item><title>Ticket 設計派工方法論</title><link>https://tarrragon.github.io/blog/record/ticket-%E8%A8%AD%E8%A8%88%E6%B4%BE%E5%B7%A5%E6%96%B9%E6%B3%95%E8%AB%96/</link><pubDate>Wed, 04 Mar 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/ticket-%E8%A8%AD%E8%A8%88%E6%B4%BE%E5%B7%A5%E6%96%B9%E6%B3%95%E8%AB%96/</guid><description>&lt;p>開發 book_overview_app 某個版本時，工作日誌膨脹到 6000 行，查一個設計決策要上下滾半天，不同人同時修改同一份文件也頻繁衝突。這不是個別案例，大型功能開發幾乎必然會遇到。&lt;/p>
&lt;p>我們的解法是建立一套 Ticket 設計派工方法論。這篇文章記錄實踐方式和核心概念，以及後來加入的積極派發原則。&lt;/p></description><content:encoded><![CDATA[<p>開發 book_overview_app 某個版本時，工作日誌膨脹到 6000 行，查一個設計決策要上下滾半天，不同人同時修改同一份文件也頻繁衝突。這不是個別案例，大型功能開發幾乎必然會遇到。</p>
<p>我們的解法是建立一套 Ticket 設計派工方法論。這篇文章記錄實踐方式和核心概念，以及後來加入的積極派發原則。</p>
<h2 id="為什麼需要-ticket">為什麼需要 Ticket？</h2>
<p>工作日誌已經記錄了一切，為什麼還需要 Ticket？</p>
<p>兩者定位不同。工作日誌是「記錄層」，給未來的開發者看，記錄決策脈絡。Ticket 是「執行層」，給正在做事的人看，代表一個可以獨立完成、驗收、追蹤的最小任務單位。</p>
<p>一個 Ticket 有幾個核心特徵：獨立性（可以獨立執行和驗收）、原子性（不可再分割）、可驗證性（有明確的完成標準），以及單一職責（一個動詞加上一個目標）。</p>
<h2 id="單一職責是拆分-ticket-的唯一標準">單一職責是拆分 Ticket 的唯一標準</h2>
<p>方法論早期我們用量化指標判斷是否需要拆分：職責數量、檔案數量、測試案例數、行數。直觀，但實際用起來有問題：一個 100 行的 Ticket 可能職責非常清楚，完全不需要拆；一個 30 行的如果混了兩個不相關的目標，就應該拆。數字不能反映語義。</p>
<p>後來改為四個語義檢查：</p>
<p><strong>語義檢查</strong>：能用「動詞加單一目標」描述嗎？描述時出現「並且」或「以及」，通常需要拆。</p>
<p><strong>修改原因檢查</strong>：只有一個原因會觸發修改嗎？兩個不同的業務規則各自可能觸發修改，就是兩個 Ticket。</p>
<p><strong>驗收一致性</strong>：所有驗收條件都指向同一個目標嗎？</p>
<p><strong>依賴獨立性</strong>：拆分後不會產生循環依賴嗎？</p>
<p>這四個問題比任何量化指標都更接近本質。</p>
<p>驗收條件的標準也要說清楚：「功能正常」不夠，「所有單元測試通過且 dart analyze 輸出 0 個問題」才算。</p>
<h2 id="ticket-與-clean-architecture-的對應">Ticket 與 Clean Architecture 的對應</h2>
<p>我們的專案用 Clean Architecture，Ticket 設計也遵循同樣的分層邏輯。</p>
<p>Interface 定義優先於具體實作。每個功能模組先建 Interface 定義的 Ticket，再建具體實作的 Ticket。這樣外層可以依賴 Interface 先行開發，不被內層實作進度卡住。</p>
<p>分層拆分讓每個 Ticket 待在自己的架構層，Domain、Application、Infrastructure、Presentation 層的 Ticket 不互相混合，也更容易並行執行。</p>
<h2 id="即時-review在問題發生當下就解決">即時 Review：在問題發生當下就解決</h2>
<p>傳統 code review 在整個版本完成後進行，此時修正成本很高，問題可能已經影響了許多後續實作。</p>
<p>我們改為每個 Ticket 完成時立即觸發 Review。因為範圍只有單一 Ticket 的相關程式碼，Review 者不需要理解整個版本的脈絡，反饋在數小時內完成，修正成本極低。</p>
<p>Review 涵蓋四個面向：功能正確性（功能實現、驗收條件、邊界情況、錯誤處理）、架構合規性（Clean Architecture、依賴方向、Interface-Driven、架構債務）、測試通過率（單元測試、整合測試、覆蓋率、跳過測試）、文件同步性（Ticket 日誌、設計決策、API 文件、README），共 16 項。</p>
<h2 id="三層文件結構讓-6000-行問題消失">三層文件結構：讓 6000 行問題消失</h2>
<p>解決臃腫工作日誌的方式是拆層：</p>
<ul>
<li><strong>主版本日誌</strong>：500–1000 行，說明版本目標，提供所有 Ticket 的索引</li>
<li><strong>Ticket 工作日誌</strong>：每個 Ticket 一份，100–200 行，記錄執行過程、Review 結果和完成標記</li>
<li><strong>設計決策日誌</strong>：300–500 行，記錄設計決策演進，包含廢棄決策的原因</li>
</ul>
<p>原本 6000 行的文件拆分後變成一份 800 行主日誌加上 12 個 Ticket 日誌加上一份 400 行設計決策日誌，總量降到約 2000 行，資訊查找時間減少大約 80%。</p>
<h2 id="積極派發發現問題就建-ticket">積極派發：發現問題就建 Ticket</h2>
<p>這是後來加入的原則，也是這套方法論最核心的轉變。</p>
<p>執行一個 Ticket 時，經常會發現超出原本範圍的情況。以前的做法是「繼續在這個 Ticket 裡處理」或「先記著等有空再說」。前者讓 Ticket 失去單一職責，後者讓問題消失在記憶裡。</p>
<p>積極派發的做法：只要發現超出範圍的新情況，立刻評估性質，建立對應的新 Ticket。阻塞了當前 Ticket 就建子 Ticket 標記依賴；獨立於當前 Ticket 就建新 Ticket 讓兩者並行；是未來的改進就建技術債務 Ticket 記錄起來。</p>
<p>好處是：每個決策都有獨立記錄，保持可追溯性；任務粒度更細，更容易估算；學習點不會淹沒在大 Ticket 裡；一次只專注一件事。</p>
<p>要注意的反模式是「為派發而派發」。積極派發不是讓每件事都變成獨立 Ticket，判斷標準始終是單一職責：情況是否真的超出原 Ticket 範圍？</p>
<hr>
<p>這套方法論從單一大文件演進到精簡主文加引用網絡，量化指標被單一職責取代，是因為語義比數字更能反映 Ticket 是否設計得好。積極派發的加入，是因為 Ticket 可以成為持續改善的工具，不只是任務追蹤。</p>
<p>如果你的工作日誌越來越長、協作衝突越來越多，從三個問題開始：Ticket 能用「動詞加單一目標」描述嗎？驗收條件是客觀可檢查的嗎？跟其他 Ticket 的依賴關係清楚嗎？</p>]]></content:encoded></item><item><title>用 Claude Code GitHub Actions 自動除錯 CI 建置失敗</title><link>https://tarrragon.github.io/blog/posts/%E7%94%A8-claude-code-github-actions-%E8%87%AA%E5%8B%95%E9%99%A4%E9%8C%AF-ci-%E5%BB%BA%E7%BD%AE%E5%A4%B1%E6%95%97/</link><pubDate>Wed, 04 Mar 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/posts/%E7%94%A8-claude-code-github-actions-%E8%87%AA%E5%8B%95%E9%99%A4%E9%8C%AF-ci-%E5%BB%BA%E7%BD%AE%E5%A4%B1%E6%95%97/</guid><description>&lt;h2 id="這是什麼">這是什麼&lt;/h2>
&lt;p>&lt;a href="https://github.com/anthropics/claude-code-action">Claude Code GitHub Actions&lt;/a> 讓 Claude 直接參與你的 GitHub 工作流程，主要功能：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>互動式助手&lt;/strong> — 在 PR/Issue 留言 &lt;code>@claude&lt;/code>，Claude 會分析程式碼並回覆&lt;/li>
&lt;li>&lt;strong>自動 Code Review&lt;/strong> — PR 開啟時自動審查變更&lt;/li>
&lt;li>&lt;strong>CI 除錯修復&lt;/strong> — build 失敗時自動分析錯誤並修復&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<h2 id="這是什麼">這是什麼</h2>
<p><a href="https://github.com/anthropics/claude-code-action">Claude Code GitHub Actions</a> 讓 Claude 直接參與你的 GitHub 工作流程，主要功能：</p>
<ul>
<li><strong>互動式助手</strong> — 在 PR/Issue 留言 <code>@claude</code>，Claude 會分析程式碼並回覆</li>
<li><strong>自動 Code Review</strong> — PR 開啟時自動審查變更</li>
<li><strong>CI 除錯修復</strong> — build 失敗時自動分析錯誤並修復</li>
</ul>
<p>完整功能說明參考 <a href="https://code.claude.com/docs/en/github-actions">官方文件</a>。</p>
<h2 id="設定方式">設定方式</h2>
<h3 id="install-github-app推薦"><code>/install-github-app</code>（推薦）</h3>
<p>在 Claude Code 終端執行 <code>/install-github-app</code>，它會引導你完成所有設定。</p>
<p>流程中的關鍵步驟：</p>
<ol>
<li><strong>選擇 repo</strong> — 指定要安裝的 GitHub repository</li>
<li><strong>安裝 Claude GitHub App</strong> — 自動安裝到指定 repo，授予 Contents、Issues、Pull requests 的 Read &amp; Write 權限</li>
<li><strong>選擇認證方式</strong> — 選擇 <strong>long-life token</strong> 會產生 OAuth token，自動寫入 GitHub Secrets 為 <code>CLAUDE_CODE_OAUTH_TOKEN</code></li>
<li><strong>建立 workflow 檔案</strong> — 自動建立並 push 兩個 workflow：
<ul>
<li><code>claude.yml</code> — <code>@claude</code> 互動回覆</li>
<li><code>claude-code-review.yml</code> — PR 自動 code review</li>
</ul>
</li>
</ol>
<p>完成後不需要額外設定。</p>
<h3 id="手動設定使用-anthropic-api-key">手動設定（使用 Anthropic API Key）</h3>
<p>如果不想用 <code>/install-github-app</code>，可以手動操作：</p>
<ol>
<li>前往 <a href="https://github.com/apps/claude">github.com/apps/claude</a> 安裝 App 到你的 repo</li>
<li>到 repo 的 <strong>Settings → Secrets and variables → Actions</strong>，新增 <code>ANTHROPIC_API_KEY</code></li>
<li>手動建立 workflow 檔案到 <code>.github/workflows/</code></li>
</ol>
<p>兩種認證方式的差異：</p>
<table>
  <thead>
      <tr>
          <th>認證方式</th>
          <th>Secret 名稱</th>
          <th>適用對象</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>OAuth Token</td>
          <td><code>CLAUDE_CODE_OAUTH_TOKEN</code></td>
          <td>Pro/Max 用戶，<code>/install-github-app</code> 自動設定</td>
      </tr>
      <tr>
          <td>API Key</td>
          <td><code>ANTHROPIC_API_KEY</code></td>
          <td>直接使用 Anthropic API，需手動到 <a href="https://console.anthropic.com">console.anthropic.com</a> 取得</td>
      </tr>
  </tbody>
</table>
<h2 id="加入-ci-自動除錯">加入 CI 自動除錯</h2>
<p><code>/install-github-app</code> 建立的 workflow 只處理 <code>@claude</code> 互動和 code review。如果你想在 <strong>build 失敗時自動觸發 Claude 修復</strong>，需要修改既有的 deploy workflow。</p>
<p>首先，補上 Claude 需要的權限（原本可能只有 <code>contents: read</code>）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="nt">permissions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">  </span><span class="nt">contents</span><span class="p">:</span><span class="w"> </span><span class="l">write       </span><span class="w"> </span><span class="c"># Claude 需要寫入修復後的檔案</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">  </span><span class="nt">pull-requests</span><span class="p">:</span><span class="w"> </span><span class="l">write  </span><span class="w"> </span><span class="c"># Claude 可能需要建立 PR</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">  </span><span class="nt">issues</span><span class="p">:</span><span class="w"> </span><span class="l">write         </span><span class="w"> </span><span class="c"># Claude 回報結果</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">  </span><span class="nt">pages</span><span class="p">:</span><span class="w"> </span><span class="l">write          </span><span class="w"> </span><span class="c"># 原本的 deploy 權限</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w">  </span><span class="nt">id-token</span><span class="p">:</span><span class="w"> </span><span class="l">write       </span><span class="w"> </span><span class="c"># 原本的 deploy 權限</span></span></span></code></pre></div><p>然後在 build 步驟加入 Claude 除錯邏輯：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># 在原本的 build step 加上 continue-on-error 和 id</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">  </span><span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l">hugo-build</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">  </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">hugo --minify 2&gt;&amp;1 | tee hugo-build-output.txt</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">  </span><span class="nt">continue-on-error</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w"></span><span class="c"># Build 失敗時觸發 Claude 除錯</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Claude Debug on Build Failure</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">  </span><span class="nt">if</span><span class="p">:</span><span class="w"> </span><span class="l">steps.hugo-build.outcome == &#39;failure&#39;</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">  </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">anthropics/claude-code-action@v1</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">  </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span><span class="c"># 依你的認證方式擇一</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">    </span><span class="nt">claude_code_oauth_token</span><span class="p">:</span><span class="w"> </span><span class="l">${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">    </span><span class="c"># anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">    </span><span class="nt">prompt</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="sd">      Hugo build failed. Here is the error output:
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="sd">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="sd">      $(cat hugo-build-output.txt)
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="sd">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="sd">      Please analyze the error, find the problematic file(s),
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="sd">      fix the YAML front matter or content issue, and commit the fix.</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">    </span><span class="nt">claude_args</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;--max-turns 10&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w"></span><span class="c"># 修復後重新 build 驗證</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Retry build after fix</span><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">  </span><span class="nt">if</span><span class="p">:</span><span class="w"> </span><span class="l">steps.hugo-build.outcome == &#39;failure&#39;</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">  </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">hugo --minify</span></span></span></code></pre></div><p>核心設計：</p>
<ol>
<li><code>continue-on-error: true</code> — build 失敗不中斷流程，讓後續 Claude 步驟有機會執行</li>
<li><code>if: steps.hugo-build.outcome == 'failure'</code> — 只在失敗時觸發，正常 build 不消耗 API 額度</li>
<li>修復後重新 <code>hugo --minify</code> 驗證是否成功</li>
</ol>
<h2 id="計費方式">計費方式</h2>
<p>計費取決於你使用哪種認證方式：</p>
<table>
  <thead>
      <tr>
          <th>認證方式</th>
          <th>計費來源</th>
          <th>說明</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>OAuth Token</td>
          <td><strong>訂閱額度</strong>（Pro/Max）</td>
          <td>跟 claude.ai 網頁、Claude Code CLI、Claude Desktop <strong>共用同一個額度池</strong></td>
      </tr>
      <tr>
          <td>API Key</td>
          <td><strong>獨立 API 計費</strong></td>
          <td>按 token 用量付費，與訂閱額度完全分開</td>
      </tr>
  </tbody>
</table>
<p>OAuth token 的額度是共用的，GitHub Actions 跑多了會擠壓你日常在 claude.ai 和 CLI 的使用額度。如果 CI 觸發頻繁，建議改用 API Key 避免互相影響。</p>
<p>詳細的費率可參考 <a href="https://www.anthropic.com/pricing">Claude 定價頁面</a>。</p>
<h3 id="降低成本的設定">降低成本的設定</h3>
<table>
  <thead>
      <tr>
          <th>設定</th>
          <th>說明</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>--max-turns 10</code></td>
          <td>限制迭代次數，避免無限循環</td>
      </tr>
      <tr>
          <td>只在 <code>failure</code> 時觸發</td>
          <td>正常 build 不消耗 API 額度</td>
      </tr>
      <tr>
          <td><code>@claude</code> 觸發詞</td>
          <td>互動模式只在明確呼叫時才啟動</td>
      </tr>
  </tbody>
</table>
<h2 id="搭配-claudemd">搭配 CLAUDE.md</h2>
<p>在 repo 根目錄建立 <code>CLAUDE.md</code>，Claude 會自動讀取作為上下文，提升修復準確度。</p>
<h2 id="參考資料">參考資料</h2>
<ul>
<li><a href="https://code.claude.com/docs/en/github-actions">Claude Code GitHub Actions 官方文件</a></li>
<li><a href="https://github.com/anthropics/claude-code-action">claude-code-action GitHub Repo</a></li>
<li><a href="https://github.com/anthropics/claude-code-action/blob/main/docs/setup.md">Setup Guide</a></li>
</ul>]]></content:encoded></item><item><title>用 Hook 系統把開發規範變成自動執行的基礎設施</title><link>https://tarrragon.github.io/blog/record/%E7%94%A8-hook-%E7%B3%BB%E7%B5%B1%E6%8A%8A%E9%96%8B%E7%99%BC%E8%A6%8F%E7%AF%84%E8%AE%8A%E6%88%90%E8%87%AA%E5%8B%95%E5%9F%B7%E8%A1%8C%E7%9A%84%E5%9F%BA%E7%A4%8E%E8%A8%AD%E6%96%BD/</link><pubDate>Wed, 04 Mar 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/%E7%94%A8-hook-%E7%B3%BB%E7%B5%B1%E6%8A%8A%E9%96%8B%E7%99%BC%E8%A6%8F%E7%AF%84%E8%AE%8A%E6%88%90%E8%87%AA%E5%8B%95%E5%9F%B7%E8%A1%8C%E7%9A%84%E5%9F%BA%E7%A4%8E%E8%A8%AD%E6%96%BD/</guid><description>&lt;h3 id="從一個社群文章開始">從一個社群文章開始&lt;/h3>
&lt;p>社群裡有人分享了幾篇關於 Claude Code Hook 的文章，看完第一個反應是：「我低估了 hook 能做的事情。」&lt;/p>
&lt;p>我知道 hook 的存在，但一直把它當成「跑幾個簡單檢查」的輔助工具。那幾篇文章讓我意識到，hook 可以是完整的品質控制基礎設施——在每個關鍵時機介入，執行那些「應該要做但忘記做」的檢查。&lt;/p></description><content:encoded><![CDATA[<h3 id="從一個社群文章開始">從一個社群文章開始</h3>
<p>社群裡有人分享了幾篇關於 Claude Code Hook 的文章，看完第一個反應是：「我低估了 hook 能做的事情。」</p>
<p>我知道 hook 的存在，但一直把它當成「跑幾個簡單檢查」的輔助工具。那幾篇文章讓我意識到，hook 可以是完整的品質控制基礎設施——在每個關鍵時機介入，執行那些「應該要做但忘記做」的檢查。</p>
<h3 id="hook-的執行時機">Hook 的執行時機</h3>
<p>Claude Code Hook 有五個觸發點：<code>SessionStart</code>、<code>UserPromptSubmit</code>、<code>PreToolUse</code>、<code>PostToolUse</code>、<code>Stop</code>，涵蓋整個開發互動的生命週期。</p>
<p>有了這個框架，問題就變成：哪些規範應該在哪個時機點執行？</p>
<h3 id="我們建立了哪些-hook">我們建立了哪些 Hook</h3>
<h4 id="session-啟動檢查">Session 啟動檢查</h4>
<p>每次 session 開始時自動確認：git 遠端是否有需要同步的變更、開發環境依賴是否完整、工作日誌狀態。這些檢查不阻止啟動，只是讓開發者一開始就有完整的情境。</p>
<h5 id="任務逃避偵測">任務逃避偵測</h5>
<p>這是我們設計中比較有趣的一個 hook。它在每次 prompt 提交時執行，掃描內容裡是否出現「太複雜先跳過」、「暫時不處理」這類詞彙，同時也會檢查行為模式：程式碼變更了但測試沒有對應變更、技術債務累積超過閾值等。一旦偵測到逃避行為，會建立一個 block 標記檔案，後續所有工具呼叫都會被阻止，直到問題被正視。</p>
<h6 id="程式異味即時偵測">程式異味即時偵測</h6>
<p>每次檔案編輯後（<code>PostToolUse</code>），我們會即時掃描變更的程式碼。函數超過 30 行、巢狀超過 4 層、參數超過 5 個、依賴數超過 10 個，這些都會觸發記錄並建議重構。這個 hook 採用非阻塞設計——發現問題時記錄，但不中斷開發流程。</p>
<h6 id="版本推進建議">版本推進建議</h6>
<p>在 Claude 完成回應時（<code>Stop</code>），hook 會分析當前的工作狀態：有沒有未提交的變更、工作日誌有沒有標記完成、TodoList 系列是否達成。根據這些狀態，自動建議接下來應該做小版本推進還是繼續開發。</p>
<h6 id="文件同步提醒">文件同步提醒</h6>
<p>程式碼變更後，hook 根據檔案類型判斷哪些文件需要同步更新。API 異動對應 API 文件，架構異動對應架構文件，這類規則很難靠人記，但 hook 記得。</p>
<h3 id="一個反覆遇到的事故">一個反覆遇到的事故</h3>
<p>hook 開發過程中出過同一個事故不只一次。</p>
<p>Claude Code hook 系統的設計是：任何寫入 <code>stderr</code> 的輸出都會被視為 hook error 顯示給使用者。Python 的 <code>logging</code> 模組預設輸出到 stderr，所以即使 hook 正常執行，只要有 logging 輸出，UI 上就會冒出 hook error 警告。</p>
<p>這個問題反覆出現，最後系統性地修復，確立了一條規則：hook 禁止寫入 stderr，所有輸出必須走 stdout。現在這條規則寫進方法論，新 hook 都有對應的驗證：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">grep -r <span class="s2">&#34;sys\.stderr&#34;</span> .claude/hooks/ --include<span class="o">=</span><span class="s2">&#34;*.py&#34;</span></span></span></code></pre></div><p>這個指令應該永遠返回空結果。</p>
<h3 id="模組化的演進">模組化的演進</h3>
<p>剛開始每個 hook 腳本都是獨立的，讀取輸入、處理邏輯、輸出結果各自實作。問題很明顯：讀取 hook 輸入、輸出決策結果這些通用邏輯，在每個腳本裡都重複了一遍。</p>
<p>後來重構引入了共用模組。<code>.claude/lib/</code> 底下現在有幾個核心模組：<code>hook_io.py</code> 負責標準化 I/O，<code>hook_logging.py</code> 負責日誌，<code>config_loader.py</code> 載入配置，<code>git_utils.py</code> 封裝 git 操作。</p>
<p>帶來兩個好處：腳本結構變得簡潔，開發者只需要專注判斷邏輯；共用模組可以寫獨立的單元測試，以前 hook 的正確性很難驗證，現在有了。</p>
<h3 id="幾個設計原則">幾個設計原則</h3>
<p><strong>非阻塞優先</strong>。大部分品質檢查不該阻止開發流程，而是記錄、追蹤、提示。只有真正關鍵的違規——任務逃避、阻止狀態——才完全阻斷操作。</p>
<p><strong>漸進式強制</strong>。從警告到記錄到追蹤到阻止，給開發者理解和修正的機會，不是一刀切拒絕。</p>
<p><strong>可觀測性</strong>。hook 系統自己也需要被監控。有一個 performance monitor hook 專門追蹤其他 hook 的執行時間，超過 5 秒視為需要立即優化。</p>
<p><strong>配置外部化</strong>。品質規則的閾值、代理人分派規則，全部放在 YAML 配置檔，不硬編碼在腳本裡。需要調整只改配置。</p>
<h3 id="現在的感受">現在的感受</h3>
<p>把規範寫進文件和寫進 hook，是完全不同的感受。</p>
<p>寫進文件的規範靠人記。寫進 hook 的規範，在每個關鍵時機自動執行，開發者甚至可以不知道它存在——但規範確實在發生效用。品質基線從依賴個人紀律，變成由系統保證。</p>]]></content:encoded></item><item><title>即時 Review 機制：Ticket 完成就 Review，不累積</title><link>https://tarrragon.github.io/blog/record/%E5%8D%B3%E6%99%82-review-%E6%A9%9F%E5%88%B6ticket-%E5%AE%8C%E6%88%90%E5%B0%B1-review%E4%B8%8D%E7%B4%AF%E7%A9%8D/</link><pubDate>Wed, 04 Mar 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/%E5%8D%B3%E6%99%82-review-%E6%A9%9F%E5%88%B6ticket-%E5%AE%8C%E6%88%90%E5%B0%B1-review%E4%B8%8D%E7%B4%AF%E7%A9%8D/</guid><description>&lt;p>以前我們也是等一批 Ticket 做完，才坐下來一起 review。&lt;/p>
&lt;p>結果就是：Review 的時候發現第一個 Ticket 的架構方向有問題，接下來五個都是建在那個錯誤上面。這時候修正成本已經是即時發現的六到十倍，開發者剛花了三天卻要整個打掉，士氣很難不崩。&lt;/p>
&lt;p>後來我們設計了「即時 Review 機制」，核心就一句話：&lt;strong>Ticket 完成就 Review，不累積。&lt;/strong>&lt;/p></description><content:encoded><![CDATA[<p>以前我們也是等一批 Ticket 做完，才坐下來一起 review。</p>
<p>結果就是：Review 的時候發現第一個 Ticket 的架構方向有問題，接下來五個都是建在那個錯誤上面。這時候修正成本已經是即時發現的六到十倍，開發者剛花了三天卻要整個打掉，士氣很難不崩。</p>
<p>後來我們設計了「即時 Review 機制」，核心就一句話：<strong>Ticket 完成就 Review，不累積。</strong></p>
<h2 id="三個原則">三個原則</h2>
<p><strong>即時觸發</strong>。Ticket 完成的當下就觸發，不是等 Wave 結束，不是等今天收工。錯誤的架構決策會在下一個 Ticket 開始前就污染後續設計，等不起。</p>
<p><strong>30 分鐘完成</strong>。這個時間限制是設計訊號：如果一個 Ticket 的 Review 超過一小時，代表那個 Ticket 太大，應該在規劃時就拆。</p>
<p><strong>標準清單</strong>。16 項固定檢查項，分功能正確性、架構合規性、測試通過率、文件同步性四類。不管誰 Review、什麼時間點，標準都一樣。</p>
<h2 id="怎麼跑">怎麼跑</h2>
<p><strong>觸發</strong>：完成實作、驗收條件全滿足、測試 100% 通過、靜態分析零錯誤，狀態標為「Review 中」。</p>
<p><strong>執行</strong>：30 分鐘內跑完 16 項。功能正確性（10 分鐘）、架構合規性（8 分鐘）、測試通過率（5 分鐘）、文件同步性（2 分鐘），最後 5 分鐘記錄結果。</p>
<p><strong>偏差糾正</strong>：發現問題就照固定流程走——暫停、記錄根因、建修正 Ticket、執行、再 Review。流程結構化是關鍵，「發現問題不知怎麼辦」是問題被忽略的主因。</p>
<h2 id="問題分三級">問題分三級</h2>
<p><strong>P0（阻塞）</strong>：功能錯誤、架構偏差、測試失敗。必須在 Review 通過前修正。</p>
<p><strong>P1（重要）</strong>：邊界處理缺失、文件不完整。建 Ticket 追蹤，當前 Ticket 可先通過。</p>
<p><strong>P2（建議）</strong>：命名改善、程式風格。記錄即可。</p>
<p>分級的目的：Review 保持快速，重要問題不被跳過。</p>
<h2 id="超時是訊號">超時是訊號</h2>
<p>Ticket 完成後 2 小時內開始 Review，目標 30 分鐘，最長 1 小時。超時代表 Ticket 範圍太大，不是 Reviewer 的問題。這個標準幫助我們持續校正 Ticket 的粒度。</p>
<h2 id="最明顯的變化">最明顯的變化</h2>
<p>導入前，「Review」這件事是模糊的——知道應該做，但時機不固定、標準不一、出了問題也不清楚怎麼處理。</p>
<p>導入後，偏差被控制在單一 Ticket 的範圍內，沒機會擴散。另一個意外收穫是，16 項清單讓我們對「品質」有了共同語言，Review 結果可以比較、可以追蹤，不再靠個人感覺。</p>
<hr>
<p>即時 Review 的目的是把修正成本壓到最低。問題在最小範圍被抓到，代價才會真的小。</p>]]></content:encoded></item><item><title>敏捷重構方法論 - Agent 分工協作模式</title><link>https://tarrragon.github.io/blog/record/%E6%95%8F%E6%8D%B7%E9%87%8D%E6%A7%8B%E6%96%B9%E6%B3%95%E8%AB%96-agent-%E5%88%86%E5%B7%A5%E5%8D%94%E4%BD%9C%E6%A8%A1%E5%BC%8F/</link><pubDate>Wed, 04 Mar 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/%E6%95%8F%E6%8D%B7%E9%87%8D%E6%A7%8B%E6%96%B9%E6%B3%95%E8%AB%96-agent-%E5%88%86%E5%B7%A5%E5%8D%94%E4%BD%9C%E6%A8%A1%E5%BC%8F/</guid><description>&lt;p>開發 Book Overview App 時遇到一個核心問題：AI 的上下文有限，讓代理人自行決定工作流程的話，主線程 compact 後就會失憶，任務交接也無法保證正確。&lt;/p>
&lt;p>需要一套方法讓主線程始終掌握全局，同時讓各代理人在清楚的規格下專注執行。這就是這套方法論的起點。&lt;/p></description><content:encoded><![CDATA[<p>開發 Book Overview App 時遇到一個核心問題：AI 的上下文有限，讓代理人自行決定工作流程的話，主線程 compact 後就會失憶，任務交接也無法保證正確。</p>
<p>需要一套方法讓主線程始終掌握全局，同時讓各代理人在清楚的規格下專注執行。這就是這套方法論的起點。</p>
<h2 id="三大核心原則">三大核心原則</h2>
<p><strong>主線程只分派、不寫程式碼。</strong> 主線程的價值在於全局視野和決策，一旦它開始親自動手，統籌能力就消失了。</p>
<p><strong>任務符合 Atomic Ticket 原則：一個 Action 加一個 Target。</strong>「實作 BookEnrichmentProcessor」是好的任務；「實作資料處理」太模糊。任務夠原子，才能精確評估、精確驗收。</p>
<p><strong>100% 測試通過率，沒有例外。</strong> 我們試過接受「暫時跳過」，結果累積的債務比預期高出很多倍。現在的規則是：任何檢查失敗就等於階段未完成。</p>
<h2 id="agent-角色定義">Agent 角色定義</h2>
<p>主線程（rosemary-project-manager）負責分派任務、監控進度，處理代理人回報的升級請求。禁止親自閱讀或修改程式碼，不繞過子代理人直接操作。</p>
<p>TDD 四個階段各有專責代理人：Phase 1 由 lavender-interface-designer 做功能設計，Phase 2 由 sage-test-architect 做測試設計，Phase 3a 由 pepper-test-implementer 制定語言無關的實作策略，Phase 3b 由 parsley-flutter-developer 執行 Flutter 程式碼，Phase 4 由 cinnamon-refactor-owl 做重構評估。</p>
<h2 id="五重文件系統">五重文件系統</h2>
<p>文件混亂是任務失敗的隱性原因之一。代理人不知道去哪找資訊，或者基於過時文件做了錯誤假設。</p>
<p>每份文件只回答一個問題：CHANGELOG 回答「這個版本做了什麼？」、todolist.yaml 回答「還有哪些問題要處理？」、worklog 回答「這個版本要達成什麼目標？」、ticket 回答「這個任務怎麼執行的？」、error-patterns 回答「之前遇過類似問題嗎？」</p>
<p>細節下沉原則確保 worklog 只記錄大方向，具體細節都沉到 ticket 層級。</p>
<h2 id="任務分派前的準備度檢查">任務分派前的準備度檢查</h2>
<p>這是從失敗中學到的最重要教訓：<strong>分派任務前，先確認代理人有足夠資訊執行。</strong></p>
<p>四個面向：API 規格和設計文件是否具體到類別定義？測試規格是否存在？完成標準是否可測量？潛在風險和依賴關係是否梳理清楚？</p>
<p>任何一個回答為否，就先建立對應文件再分派。我們早期跳過這步驟，代理人執行到一半才發現關鍵資訊缺失，修復成本遠高於事前準備。</p>
<h2 id="階段完成的強制驗證">階段完成的強制驗證</h2>
<p>每個開發階段完成後，必須通過五項強制檢查：</p>
<ul>
<li><code>flutter analyze</code> 零 error</li>
<li>無「Target of URI doesn&rsquo;t exist」，100% package 格式導入</li>
<li>測試 100% 通過，覆蓋率不得下降</li>
<li>無功能重複的服務實作</li>
<li>檔案位置符合 Clean Architecture，依賴方向正確</li>
</ul>
<p>任何一項失敗就等於階段未完成。不允許「暫時跳過」。</p>
<h2 id="技術債務的責任分工">技術債務的責任分工</h2>
<p>Phase 4 的 cinnamon-refactor-owl 必須在重構評估中識別所有技術債務，用標準格式記錄到工作日誌，執行 <code>/tech-debt-capture</code> 建立 Ticket，確認 Ticket 成功建立後才能完成 Phase 4。</p>
<p>這個設計解決一個常見問題：技術債務往往被口頭記錄，然後就消失了。現在強制要求轉化為可追蹤的 Ticket。</p>
<h2 id="資訊流動不是固定計畫">資訊流動，不是固定計畫</h2>
<p>v0.11.0 實戰中最深刻的學習：<strong>敏捷開發是持續發現、快速調整的動態過程。</strong></p>
<p>Phase 2 測試設計讓我們提前識別了 80% 的實作問題。sage-test-architect 在設計測試時發現 BookEnrichmentData 放在錯誤的架構層，同時發現 Domain 服務層完全缺失，需要 28 個 TDD 新建測試。</p>
<p>這個發現改變了整個計畫，原估 4-6 天調整為 6-7 天。我們面對兩個選擇：維持原時程但降低測試覆蓋率，或延長時程確保品質。</p>
<p>選了後者，理由是 Domain 層是核心基礎不能妥協。最終結果：71 個測試 100% 通過，時程只多了 1 天。</p>
<p>這個案例確立了一個原則：Phase 間的資訊必須雙向流動。Phase 2 發現的問題不只影響 Phase 3，也必須回饋給 Phase 1 更新設計文件。「Phase 1 完成就凍結」是錯誤模式，每個 Phase 的產出都是基礎版本，隨著後續發現持續更新。</p>
<h2 id="架構衝突的零容忍">架構衝突的零容忍</h2>
<p>發現 BookEnrichmentData 位置錯誤後，主線程立即停止 Phase 3 啟動，指派緊急遷移任務：移動檔案、更新所有 import 引用、執行測試確認無錯誤、提交變更。實際花了 0.4 小時完成。</p>
<p>如果在 Phase 3 已基於錯誤架構實作多個任務後才修正，修復成本至少高出十倍。</p>
<h2 id="動態文件更新的觸發點">動態文件更新的觸發點</h2>
<p>三種情況必須立即觸發文件更新：</p>
<p><strong>架構變更時</strong>——類別位置調整、介面簽名變更，必須同步更新所有引用該類別的任務和設計文件。</p>
<p><strong>測試設計發現時</strong>——識別新測試用例、發現缺失的邊界條件，更新對應實作任務的驗收標準。</p>
<p><strong>實作階段發現時</strong>——技術方案調整、新增輔助類別，更新後續相關任務的參考實作。</p>
<h2 id="核心洞察">核心洞察</h2>
<p>AI 協作開發的核心挑戰是：<strong>如何讓多個 AI 代理人在有限的上下文中，基於共享的知識庫，做出一致且高品質的決策。</strong></p>
<p>五重文件系統、Atomic Ticket、準備度檢查、強制驗證閘門——這些都是為了解決「資訊在 AI 協作中如何可靠流動」這個根本問題。</p>
<p>這套方法論仍在持續演進。每個版本都帶來新的學習，新的 error-pattern，新的流程調整。</p>]]></content:encoded></item><item><title>程式碼自然語言化撰寫方法論</title><link>https://tarrragon.github.io/blog/record/%E7%A8%8B%E5%BC%8F%E7%A2%BC%E8%87%AA%E7%84%B6%E8%AA%9E%E8%A8%80%E5%8C%96%E6%92%B0%E5%AF%AB%E6%96%B9%E6%B3%95%E8%AB%96/</link><pubDate>Wed, 04 Mar 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/%E7%A8%8B%E5%BC%8F%E7%A2%BC%E8%87%AA%E7%84%B6%E8%AA%9E%E8%A8%80%E5%8C%96%E6%92%B0%E5%AF%AB%E6%96%B9%E6%B3%95%E8%AB%96/</guid><description>&lt;p>程式碼不是寫給電腦看的，是寫給人類讀的。電腦只管執行，人類才要維護。&lt;/p></description><content:encoded><![CDATA[<p>程式碼不是寫給電腦看的，是寫給人類讀的。電腦只管執行，人類才要維護。</p>
<h2 id="認知負擔一切的出發點">認知負擔：一切的出發點</h2>
<p>人類工作記憶有限，大約一次只能處理七個項目（Miller&rsquo;s Law）。看到縮寫要在腦中展開、看到模糊詞要猜測含義、看到長函式要分段記憶——這些都是認知負擔。</p>
<p>自然語言化的目標很簡單：讓程式碼像讀文章一樣自然，把讀者的認知資源留給理解業務邏輯，而不是解碼程式碼本身。</p>
<hr>
<h2 id="第一原則命名要能直接讀懂">第一原則：命名要能直接讀懂</h2>
<h3 id="函式命名">函式命名</h3>





<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="kt">void</span> <span class="n">process</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="p">{}</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="kt">void</span> <span class="n">handle</span><span class="p">(</span><span class="n">item</span><span class="p">)</span> <span class="p">{}</span>
</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"><span class="c1">// 正確：一眼看懂
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="n">calculateBookReadingProgress</span><span class="p">(</span><span class="n">Book</span> <span class="n">book</span><span class="p">)</span> <span class="p">{}</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="kt">void</span> <span class="n">validateUserRegistrationData</span><span class="p">(</span><span class="n">User</span> <span class="n">user</span><span class="p">)</span> <span class="p">{}</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="kt">void</span> <span class="n">enrichBookMetadataFromExternalSource</span><span class="p">(</span><span class="n">Book</span> <span class="n">book</span><span class="p">)</span> <span class="p">{}</span></span></span></code></pre></div><h3 id="變數命名">變數命名</h3>





<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="kd">var</span> <span class="n">usr</span> <span class="o">=</span> <span class="n">getCurrentUser</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">data</span> <span class="o">=</span> <span class="n">loadUserData</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="n">data</span> <span class="o">=</span> <span class="n">processBookData</span><span class="p">();</span> <span class="c1">// 100行後同一變數換了意思
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1">// 正確：明確且專用
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"></span><span class="kd">final</span> <span class="n">authenticatedUser</span> <span class="o">=</span> <span class="n">getCurrentUser</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="kd">final</span> <span class="n">userProfileData</span> <span class="o">=</span> <span class="n">loadUserData</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="kd">final</span> <span class="n">enrichedBookMetadata</span> <span class="o">=</span> <span class="n">processBookData</span><span class="p">();</span></span></span></code></pre></div><h3 id="類別命名">類別命名</h3>





<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="kd">class</span> <span class="nc">Manager</span> <span class="p">{}</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="kd">class</span> <span class="nc">Handler</span> <span class="p">{}</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="kd">class</span> <span class="nc">BookDAO</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="c1">// 正確：業務職責一目了然
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookMetadataEnrichmentService</span> <span class="p">{}</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="kd">class</span> <span class="nc">UserRegistrationValidator</span> <span class="p">{}</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="kd">class</span> <span class="nc">LibraryBookSearchEngine</span> <span class="p">{}</span></span></span></code></pre></div><h3 id="布林命名">布林命名</h3>
<p>布林變數應該能讀成問句，在 if 裡就能自然被理解：</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">bool</span> <span class="n">isValid</span><span class="p">;</span>      <span class="c1">// &#34;Is valid?&#34;
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span><span class="kt">bool</span> <span class="n">hasPermission</span><span class="p">;</span> <span class="c1">// &#34;Has permission?&#34;
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"></span><span class="kt">bool</span> <span class="n">canEdit</span><span class="p">;</span>       <span class="c1">// &#34;Can edit?&#34;
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1">// 不好：語意不清
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1"></span><span class="kt">bool</span> <span class="n">permission</span><span class="p">;</span></span></span></code></pre></div><h3 id="常見命名反模式">常見命名反模式</h3>
<ul>
<li><strong>匈牙利命名法</strong>：<code>strName</code>, <code>intCount</code> — 型別系統自己會提供型別資訊，名稱不用重複</li>
<li><strong>無意義前綴</strong>：<code>theUser</code>, <code>aBook</code> — 沒帶來任何資訊，直接刪掉</li>
<li><strong>過度縮寫</strong>：<code>usrMgr</code> — 迫使讀者展開，<code>userManager</code> 更自然</li>
<li><strong>數字後綴</strong>：<code>user1</code>, <code>user2</code> — 改成 <code>primaryUser</code>, <code>secondaryUser</code> 才說明關係</li>
</ul>
<hr>
<h2 id="第二原則函式控制在五到十行">第二原則：函式控制在五到十行</h2>
<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="c1">// 錯誤：15行，三種職責混在一起
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="n">Book</span> <span class="n">processBook</span><span class="p">(</span><span class="kt">String</span> <span class="n">isbn</span><span class="p">)</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="n">isbn</span><span class="p">.</span><span class="n">length</span> <span class="o">!=</span> <span class="m">13</span><span class="p">)</span> <span class="k">throw</span> <span class="n">ArgumentError</span><span class="p">(</span><span class="s1">&#39;Invalid ISBN&#39;</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">isValidISBNChecksum</span><span class="p">(</span><span class="n">isbn</span><span class="p">))</span> <span class="k">throw</span> <span class="n">ArgumentError</span><span class="p">(</span><span class="s1">&#39;ISBN checksum failed&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="kd">final</span> <span class="n">apiResponse</span> <span class="o">=</span> <span class="n">httpClient</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="s1">&#39;/books/</span><span class="si">$</span><span class="n">isbn</span><span class="s1">&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">apiResponse</span><span class="p">.</span><span class="n">statusCode</span> <span class="o">!=</span> <span class="m">200</span><span class="p">)</span> <span class="k">throw</span> <span class="n">Exception</span><span class="p">(</span><span class="s1">&#39;API failed&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="kd">final</span> <span class="n">bookData</span> <span class="o">=</span> <span class="n">json</span><span class="p">.</span><span class="n">decode</span><span class="p">(</span><span class="n">apiResponse</span><span class="p">.</span><span class="n">body</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="k">return</span> <span class="n">Book</span><span class="p">.</span><span class="n">create</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="nl">id:</span> <span class="n">BookId</span><span class="p">(</span><span class="n">generateUniqueId</span><span class="p">()),</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="nl">title:</span> <span class="n">BookTitle</span><span class="p">(</span><span class="n">bookData</span><span class="p">[</span><span class="s1">&#39;title&#39;</span><span class="p">]),</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="k">source</span><span class="o">:</span> <span class="n">BookSource</span><span class="p">.</span><span class="n">external</span><span class="p">(),</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1">// 正確：拆成三個單一職責函式
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1"></span><span class="n">Book</span> <span class="n">createBookFromISBN</span><span class="p">(</span><span class="kt">String</span> <span class="n">isbn</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">  <span class="n">validateISBNFormat</span><span class="p">(</span><span class="n">isbn</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">  <span class="kd">final</span> <span class="n">bookData</span> <span class="o">=</span> <span class="n">fetchBookDataFromExternalAPI</span><span class="p">(</span><span class="n">isbn</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">  <span class="k">return</span> <span class="n">buildBookFromExternalData</span><span class="p">(</span><span class="n">bookData</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="kt">void</span> <span class="n">validateISBNFormat</span><span class="p">(</span><span class="kt">String</span> <span class="n">isbn</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">isbn</span><span class="p">.</span><span class="n">length</span> <span class="o">!=</span> <span class="m">13</span><span class="p">)</span> <span class="k">throw</span> <span class="n">ArgumentError</span><span class="p">(</span><span class="s1">&#39;ISBN must be 13 digits&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">isValidISBNChecksum</span><span class="p">(</span><span class="n">isbn</span><span class="p">))</span> <span class="k">throw</span> <span class="n">ArgumentError</span><span class="p">(</span><span class="s1">&#39;ISBN checksum validation failed&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="n">Map</span><span class="o">&lt;</span><span class="kt">String</span><span class="p">,</span> <span class="kt">dynamic</span><span class="o">&gt;</span> <span class="n">fetchBookDataFromExternalAPI</span><span class="p">(</span><span class="kt">String</span> <span class="n">isbn</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">  <span class="kd">final</span> <span class="n">apiResponse</span> <span class="o">=</span> <span class="n">httpClient</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="s1">&#39;/books/</span><span class="si">$</span><span class="n">isbn</span><span class="s1">&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">apiResponse</span><span class="p">.</span><span class="n">statusCode</span> <span class="o">!=</span> <span class="m">200</span><span class="p">)</span> <span class="k">throw</span> <span class="n">Exception</span><span class="p">(</span><span class="s1">&#39;Failed to fetch book data&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">  <span class="k">return</span> <span class="n">json</span><span class="p">.</span><span class="n">decode</span><span class="p">(</span><span class="n">apiResponse</span><span class="p">.</span><span class="n">body</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="n">Book</span> <span class="n">buildBookFromExternalData</span><span class="p">(</span><span class="n">Map</span><span class="o">&lt;</span><span class="kt">String</span><span class="p">,</span> <span class="kt">dynamic</span><span class="o">&gt;</span> <span class="n">bookData</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">  <span class="k">return</span> <span class="n">Book</span><span class="p">.</span><span class="n">create</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">    <span class="nl">id:</span> <span class="n">BookId</span><span class="p">(</span><span class="n">generateUniqueId</span><span class="p">()),</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">    <span class="nl">title:</span> <span class="n">BookTitle</span><span class="p">(</span><span class="n">bookData</span><span class="p">[</span><span class="s1">&#39;title&#39;</span><span class="p">]),</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">    <span class="k">source</span><span class="o">:</span> <span class="n">BookSource</span><span class="p">.</span><span class="n">external</span><span class="p">(),</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">  <span class="p">);</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><hr>
<h2 id="第三原則一個變數只做一件事">第三原則：一個變數只做一件事</h2>
<p>同一個變數在不同地方承載不同意義，是我見過最難追蹤的 bug 來源之一。</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="kd">var</span> <span class="n">result</span> <span class="o">=</span> <span class="n">validateUser</span><span class="p">(</span><span class="n">userData</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="n">result</span><span class="p">.</span><span class="n">isValid</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="n">result</span> <span class="o">=</span> <span class="n">processPayment</span><span class="p">(</span><span class="n">paymentData</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">result</span><span class="p">.</span><span class="n">success</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="n">result</span> <span class="o">=</span> <span class="n">updateDatabase</span><span class="p">(</span><span class="n">result</span><span class="p">.</span><span class="n">data</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1">// 正確：每個變數都有自己的名字
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"></span><span class="kd">final</span> <span class="n">userValidationResult</span> <span class="o">=</span> <span class="n">validateUser</span><span class="p">(</span><span class="n">userData</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="n">userValidationResult</span><span class="p">.</span><span class="n">isValid</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">  <span class="kd">final</span> <span class="n">paymentProcessingResult</span> <span class="o">=</span> <span class="n">processPayment</span><span class="p">(</span><span class="n">paymentData</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">paymentProcessingResult</span><span class="p">.</span><span class="n">success</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="kd">final</span> <span class="n">databaseUpdateResult</span> <span class="o">=</span> <span class="n">updateDatabase</span><span class="p">(</span><span class="n">paymentProcessingResult</span><span class="p">.</span><span class="n">data</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><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="c1">// 錯誤：books 在100行間一直換狀態
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="n">processLibraryBooks</span><span class="p">()</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">books</span> <span class="o">=</span> <span class="n">getAllBooks</span><span class="p">();</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="n">books</span> <span class="o">=</span> <span class="n">filterAvailableBooks</span><span class="p">(</span><span class="n">books</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="n">books</span> <span class="o">=</span> <span class="n">sortBooksByTitle</span><span class="p">(</span><span class="n">books</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="p">}</span>
</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"><span class="c1">// 正確：每個階段的狀態都有名字
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="n">processLibraryBooks</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="kd">final</span> <span class="n">allLibraryBooks</span> <span class="o">=</span> <span class="n">getAllBooks</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">  <span class="kd">final</span> <span class="n">availableBooks</span> <span class="o">=</span> <span class="n">filterAvailableBooks</span><span class="p">(</span><span class="n">allLibraryBooks</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="kd">final</span> <span class="n">sortedAvailableBooks</span> <span class="o">=</span> <span class="n">sortBooksByTitle</span><span class="p">(</span><span class="n">availableBooks</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><hr>
<h2 id="第四原則用事件驅動表達業務流程">第四原則：用事件驅動表達業務流程</h2>
<p>複雜的業務流程往往會寫成一個大函式，裡面塞滿 if/else。問題不在於 if/else 本身，而是把不同職責的邏輯混在一起。</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">// 錯誤：驗證、API呼叫、結果處理全混在一起
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="n">submitForm</span><span class="p">(</span><span class="n">FormData</span> <span class="n">formData</span><span class="p">)</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="n">formData</span><span class="p">.</span><span class="n">name</span><span class="p">.</span><span class="n">isEmpty</span><span class="p">)</span> <span class="p">{</span> <span class="n">showErrorMessage</span><span class="p">(</span><span class="s1">&#39;姓名不能為空&#39;</span><span class="p">);</span> <span class="k">return</span><span class="p">;</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="n">formData</span><span class="p">.</span><span class="n">email</span><span class="p">.</span><span class="n">isEmpty</span><span class="p">)</span> <span class="p">{</span> <span class="n">showErrorMessage</span><span class="p">(</span><span class="s1">&#39;Email不能為空&#39;</span><span class="p">);</span> <span class="k">return</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">isValidEmail</span><span class="p">(</span><span class="n">formData</span><span class="p">.</span><span class="n">email</span><span class="p">))</span> <span class="p">{</span> <span class="n">showErrorMessage</span><span class="p">(</span><span class="s1">&#39;Email格式不正確&#39;</span><span class="p">);</span> <span class="k">return</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  <span class="kd">final</span> <span class="n">apiResult</span> <span class="o">=</span> <span class="n">submitToAPI</span><span class="p">(</span><span class="n">formData</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">apiResult</span><span class="p">.</span><span class="n">success</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="n">showSuccessMessage</span><span class="p">(</span><span class="s1">&#39;提交成功&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="n">navigateToSuccessPage</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="n">clearForm</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="n">showErrorMessage</span><span class="p">(</span><span class="s1">&#39;提交失敗：&#39;</span> <span class="o">+</span> <span class="n">apiResult</span><span class="p">.</span><span class="n">error</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="n">highlightErrorFields</span><span class="p">(</span><span class="n">apiResult</span><span class="p">.</span><span class="n">errorFields</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c1">// 正確：每個事件有自己的函式
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="n">submitUserRegistrationForm</span><span class="p">(</span><span class="n">UserRegistrationFormData</span> <span class="n">formData</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">  <span class="kd">final</span> <span class="n">validationResult</span> <span class="o">=</span> <span class="n">validateUserRegistrationData</span><span class="p">(</span><span class="n">formData</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">validationResult</span><span class="p">.</span><span class="n">isValid</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="n">handleSuccessfulValidation</span><span class="p">(</span><span class="n">formData</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">    <span class="n">handleValidationFailure</span><span class="p">(</span><span class="n">validationResult</span><span class="p">.</span><span class="n">errors</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="n">ValidationResult</span> <span class="n">validateUserRegistrationData</span><span class="p">(</span><span class="n">UserRegistrationFormData</span> <span class="n">formData</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">  <span class="kd">final</span> <span class="n">errors</span> <span class="o">=</span> <span class="o">&lt;</span><span class="n">ValidationError</span><span class="o">&gt;</span><span class="p">[];</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">isValidUserName</span><span class="p">(</span><span class="n">formData</span><span class="p">.</span><span class="n">name</span><span class="p">))</span> <span class="n">errors</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">ValidationError</span><span class="p">.</span><span class="n">invalidUserName</span><span class="p">());</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">isValidUserEmail</span><span class="p">(</span><span class="n">formData</span><span class="p">.</span><span class="n">email</span><span class="p">))</span> <span class="n">errors</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">ValidationError</span><span class="p">.</span><span class="n">invalidEmail</span><span class="p">());</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">  <span class="k">return</span> <span class="n">ValidationResult</span><span class="p">.</span><span class="n">fromErrors</span><span class="p">(</span><span class="n">errors</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="kt">void</span> <span class="n">handleSuccessfulValidation</span><span class="p">(</span><span class="n">UserRegistrationFormData</span> <span class="n">formData</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">  <span class="n">submitUserRegistrationToAPI</span><span class="p">(</span><span class="n">formData</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">    <span class="p">.</span><span class="n">then</span><span class="p">(</span><span class="n">handleSuccessfulAPIResponse</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">    <span class="p">.</span><span class="n">catchError</span><span class="p">(</span><span class="n">handleAPIFailure</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">
</span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="kt">void</span> <span class="n">handleValidationFailure</span><span class="p">(</span><span class="n">List</span><span class="o">&lt;</span><span class="n">ValidationError</span><span class="o">&gt;</span> <span class="n">errors</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">  <span class="n">displayValidationErrors</span><span class="p">(</span><span class="n">errors</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">42</span><span class="cl">  <span class="n">highlightInvalidFormFields</span><span class="p">(</span><span class="n">errors</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><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="c1">// 錯誤：一個函式根據狀態做完全不同的事
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="n">updateBookStatus</span><span class="p">(</span><span class="n">Book</span> <span class="n">book</span><span class="p">,</span> <span class="kt">String</span> <span class="n">newStatus</span><span class="p">)</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="n">newStatus</span> <span class="o">==</span> <span class="s1">&#39;available&#39;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="n">book</span><span class="p">.</span><span class="n">status</span> <span class="o">=</span> <span class="n">BookStatus</span><span class="p">.</span><span class="n">available</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="n">book</span><span class="p">.</span><span class="n">borrower</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="n">updateSearchIndex</span><span class="p">(</span><span class="n">book</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="n">notifyWaitingUsers</span><span class="p">(</span><span class="n">book</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">newStatus</span> <span class="o">==</span> <span class="s1">&#39;borrowed&#39;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="n">book</span><span class="p">.</span><span class="n">status</span> <span class="o">=</span> <span class="n">BookStatus</span><span class="p">.</span><span class="n">borrowed</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="n">book</span><span class="p">.</span><span class="n">borrowDate</span> <span class="o">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">now</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="n">sendBorrowConfirmation</span><span class="p">(</span><span class="n">book</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">newStatus</span> <span class="o">==</span> <span class="s1">&#39;maintenance&#39;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="n">book</span><span class="p">.</span><span class="n">status</span> <span class="o">=</span> <span class="n">BookStatus</span><span class="p">.</span><span class="n">maintenance</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="n">removeFromSearchIndex</span><span class="p">(</span><span class="n">book</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="n">notifyMaintenanceTeam</span><span class="p">(</span><span class="n">book</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="c1">// 正確：每個事件獨立
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="n">handleBookReturnEvent</span><span class="p">(</span><span class="n">Book</span> <span class="n">book</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">  <span class="n">executeBookReturn</span><span class="p">(</span><span class="n">book</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">  <span class="n">notifyBookBecameAvailable</span><span class="p">(</span><span class="n">book</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="kt">void</span> <span class="n">handleBookBorrowEvent</span><span class="p">(</span><span class="n">Book</span> <span class="n">book</span><span class="p">,</span> <span class="n">User</span> <span class="n">borrower</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">  <span class="n">executeBookBorrow</span><span class="p">(</span><span class="n">book</span><span class="p">,</span> <span class="n">borrower</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">  <span class="n">confirmBorrowingToUser</span><span class="p">(</span><span class="n">book</span><span class="p">,</span> <span class="n">borrower</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="kt">void</span> <span class="n">handleBookMaintenanceEvent</span><span class="p">(</span><span class="n">Book</span> <span class="n">book</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">  <span class="n">markBookForMaintenance</span><span class="p">(</span><span class="n">book</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">  <span class="n">notifyMaintenanceRequired</span><span class="p">(</span><span class="n">book</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><hr>
<h2 id="第五原則可讀性優於簡潔性">第五原則：可讀性優於簡潔性</h2>
<p>程式碼的價值排序：正確性 &gt; 可讀性 &gt; 可維護性 &gt; 簡潔性。</p>
<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="c1">// 錯誤：為了少寫幾行犧牲可讀性
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="n">books</span><span class="p">.</span><span class="n">where</span><span class="p">((</span><span class="n">b</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="n">b</span><span class="p">.</span><span class="n">s</span> <span class="o">==</span> <span class="s1">&#39;a&#39;</span> <span class="o">&amp;&amp;</span> <span class="n">b</span><span class="p">.</span><span class="n">p</span> <span class="o">&gt;</span> <span class="m">100</span><span class="p">).</span><span class="n">map</span><span class="p">((</span><span class="n">b</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="n">b</span><span class="p">.</span><span class="n">t</span><span class="p">).</span><span class="n">toList</span><span class="p">();</span>
</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 class="c1">// 正確：每一步都說清楚在做什麼
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span><span class="kd">final</span> <span class="n">availableBooksWithMoreThan100Pages</span> <span class="o">=</span> <span class="n">allBooks</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="p">.</span><span class="n">where</span><span class="p">((</span><span class="n">book</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="n">book</span><span class="p">.</span><span class="n">status</span> <span class="o">==</span> <span class="n">BookStatus</span><span class="p">.</span><span class="n">available</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="p">.</span><span class="n">where</span><span class="p">((</span><span class="n">book</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="n">book</span><span class="p">.</span><span class="n">pageCount</span> <span class="o">&gt;</span> <span class="m">100</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="p">.</span><span class="n">toList</span><span class="p">();</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="kd">final</span> <span class="n">bookTitlesForDisplay</span> <span class="o">=</span> <span class="n">availableBooksWithMoreThan100Pages</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="p">.</span><span class="n">map</span><span class="p">((</span><span class="n">book</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="n">book</span><span class="p">.</span><span class="n">title</span><span class="p">.</span><span class="n">value</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="p">.</span><span class="n">toList</span><span class="p">();</span></span></span></code></pre></div><hr>
<h2 id="如何驗證程式碼品質">如何驗證程式碼品質</h2>
<p><strong>陌生人測試</strong>：讓不熟悉這段程式碼的工程師讀。5分鐘內能理解主要邏輯算合格，需要解釋才能理解就要重寫。</p>
<p><strong>自然語言測試</strong>：把程式碼翻譯成中文說出來。翻譯流暢自然算合格，說不清楚就改命名。</p>
<p><strong>六個月後測試</strong>：假設半年後的自己要修改這段程式碼，能快速找到位置算合格，不敢動怕壞掉就要重新設計。</p>
<hr>
<p>每一行程式碼都是一句話，每個函式都是一個段落。好的程式碼是對未來維護者的體貼——不只是風格偏好，而是降低維護成本的工程決策。</p>]]></content:encoded></item><item><title>程式碼註解撰寫方法論</title><link>https://tarrragon.github.io/blog/record/%E7%A8%8B%E5%BC%8F%E7%A2%BC%E8%A8%BB%E8%A7%A3%E6%92%B0%E5%AF%AB%E6%96%B9%E6%B3%95%E8%AB%96/</link><pubDate>Wed, 04 Mar 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/%E7%A8%8B%E5%BC%8F%E7%A2%BC%E8%A8%BB%E8%A7%A3%E6%92%B0%E5%AF%AB%E6%96%B9%E6%B3%95%E8%AB%96/</guid><description>&lt;p>接手一段六個月前的程式碼，看到 &lt;code>processBook()&lt;/code>，旁邊的註解寫著「處理書籍相關邏輯」。完全沒有幫助——只是重述函式名稱，沒說背後有什麼業務限制、改了會影響哪裡、當初為什麼這樣設計。&lt;/p>
&lt;p>這讓我們重新思考：註解到底是為了誰而寫的？&lt;/p></description><content:encoded><![CDATA[<p>接手一段六個月前的程式碼，看到 <code>processBook()</code>，旁邊的註解寫著「處理書籍相關邏輯」。完全沒有幫助——只是重述函式名稱，沒說背後有什麼業務限制、改了會影響哪裡、當初為什麼這樣設計。</p>
<p>這讓我們重新思考：註解到底是為了誰而寫的？</p>
<h2 id="註解的本質">註解的本質</h2>
<p>程式碼註解不是程式的解釋員。它的存在是為了保護原始設計意圖，提供無法從程式碼本身推斷出來的訊息。</p>
<p><strong>不應該做的</strong>：解釋程式在做什麼（好的程式碼應該自己說話）、描述函式使用方法（那是文件的工作）、充當 TODO 清單。</p>
<p><strong>應該做的</strong>：作為需求保護器，防止維護時破壞原始需求；記錄設計意圖，保存業務邏輯的考量；提供維護指引，明確標示約束條件；建立程式碼與需求規格的連結。</p>
<h2 id="第一原則程式碼本身必須自說明">第一原則：程式碼本身必須自說明</h2>
<p>如果程式碼需要靠註解才能被理解，首先應該改善的是程式碼，不是補更多解釋。</p>
<p><code>process(Book book)</code> 需要一行「檢查書籍狀態並更新進度」的註解，但如果直接命名為 <code>updateReadingProgressWhenStatusChanges(Book book)</code>，解釋性的註解就不需要了。此時真正值得寫下來的，是這個函式背後的業務需求和約束條件：</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">updateReadingProgressWhenStatusChanges</span><span class="p">(</span><span class="n">Book</span> <span class="n">book</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">  <span class="c1">// 需求：UC-005 閱讀進度管理
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"></span>  <span class="c1">// 當使用者標記書籍為「閱讀中」時，自動設定進度為 0%
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"></span>  <span class="c1">// 當使用者標記為「已完成」時，自動設定進度為 100%
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"></span>  <span class="c1">// 約束：不可覆蓋使用者手動設定的進度值
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><p>變數也一樣：<code>final data = book.getInfo()</code> 毫無意義，<code>final enrichedBookMetadata = book.getMetadataWithEnrichment()</code> 就完全自說明了。</p>
<p>驗證標準：移除所有註解後，如果仍能理解程式邏輯，程式碼達標。需要猜測變數含義就重新命名，無法確定函式目的就拆分函式。</p>
<h2 id="第二原則註解記錄的是需求脈絡">第二原則：註解記錄的是需求脈絡</h2>
<p>程式碼自說明，不代表不需要註解——需要的是不同種類的註解。</p>
<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="c1">/// 需求：UC-003.2 書籍分類管理
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1">/// 使用者可以為書籍設定多個標籤進行分類
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1">/// 約束：標籤名稱不可重複，最多 10 個標籤
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="n">addTagToBook</span><span class="p">(</span><span class="n">BookId</span> <span class="n">bookId</span><span class="p">,</span> <span class="n">Tag</span> <span class="n">tag</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">  <span class="c1">// 實作...
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><p>這條註解告訴維護者：這個函式的存在依據是 UC-003.2，不可以被打破的規則是標籤不可重複且最多十個。</p>
<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="c1">/// 需求：BR-001 書籍狀態轉換規則
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1">/// 書籍狀態變更順序：初始 → 資訊補充中 → 資訊補充完成 → 可用
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1">/// 約束：不可跳過中間狀態，不可逆向轉換
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1">/// 例外：管理員可以直接設定為任何狀態
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"></span><span class="n">BookStatus</span> <span class="n">transitionBookStatus</span><span class="p">(</span><span class="n">BookStatus</span> <span class="n">current</span><span class="p">,</span> <span class="n">BookStatus</span> <span class="n">target</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">  <span class="c1">// 實作...
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><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="c1">/// 需求：NFR-002 效能需求
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1">/// 書庫載入時間不可超過 2 秒
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1">/// 設計決策：採用懶載入 + 分頁載入策略
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1">/// 影響：首次載入只載入 20 本書，滾動時動態載入
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"></span><span class="n">List</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">&gt;</span> <span class="n">loadLibraryWithPagination</span><span class="p">(</span><span class="kt">int</span> <span class="n">page</span><span class="p">,</span> <span class="kt">int</span> <span class="n">pageSize</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">  <span class="c1">// 實作...
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><p>一個完整的業務邏輯註解必須包含：需求來源（UC 或 BR 編號）、業務描述、約束條件、以及修改此邏輯會影響哪些功能。</p>
<h2 id="第三原則維護指引必須明確">第三原則：維護指引必須明確</h2>
<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="c1">/// 需求：UC-001.3 書籍唯一性檢查
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1">/// 同一書庫內不可有相同 ISBN 的書籍
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1">/// 警告：此邏輯關聯到資料一致性，修改前必須檢查：
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1">/// - 書籍匯入流程 (ImportBookService)
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1">/// - 書籍合併功能 (BookMergeService)
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1">/// - 資料庫索引設計 (book_isbn_unique_index)
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"></span><span class="kt">bool</span> <span class="n">isDuplicateBook</span><span class="p">(</span><span class="kt">String</span> <span class="n">isbn</span><span class="p">,</span> <span class="n">LibraryId</span> <span class="n">libraryId</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">  <span class="c1">// 實作...
</span></span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><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="c1">/// 需求：UC-004 書籍搜尋功能
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1">/// 支援書名、作者、標籤的模糊搜尋
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1">/// 擴展指引：新增搜尋條件時必須：
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1">/// 1. 更新 SearchCriteria 值物件
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1">/// 2. 修改索引策略以維持效能
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1">/// 3. 更新搜尋測試案例涵蓋新條件
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"></span><span class="n">List</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">&gt;</span> <span class="n">searchBooks</span><span class="p">(</span><span class="n">SearchCriteria</span> <span class="n">criteria</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">  <span class="c1">// 實作...
</span></span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><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="c1">/// 需求：UC-006 借閱管理
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1">/// 計算書籍歸還到期日
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1">/// 相依性警告：此邏輯與以下模組緊密耦合
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1">/// - LoanReminderService（提醒計算）
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1">/// - OverdueBookDetector（逾期偵測）
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1">/// - LibraryStatistics（統計計算）
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1">/// 修改歸還期限計算會影響上述所有模組
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span><span class="n">DateTime</span> <span class="n">calculateDueDate</span><span class="p">(</span><span class="n">DateTime</span> <span class="n">loanDate</span><span class="p">,</span> <span class="kt">int</span> <span class="n">loanPeriodDays</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="c1">// 實作...
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><h2 id="第四原則結構一致的標準格式">第四原則：結構一致的標準格式</h2>
<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="c1">/// 需求：[需求編號] [簡短描述]
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1">/// [詳細業務描述，說明使用者需求]
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1">/// 約束：[限制條件和邊界規則]
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1">/// [維護指引：修改須知、相依性警告、擴展要求]
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"></span><span class="p">[</span><span class="err">函式簽名</span><span class="p">]</span></span></span></code></pre></div><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="c1">/// 需求：UC-007.1 閱讀統計分析
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1">/// 計算使用者的閱讀速度和預估剩餘時間
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1">/// 約束：只統計狀態為「閱讀中」的書籍，頁數必須大於 0
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1">/// 計算邏輯：(已讀頁數 / 實際閱讀時間) = 閱讀速度（頁/小時）
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1">/// 維護指引：修改計算公式會影響：
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1">/// - 閱讀目標設定功能
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1">/// - 個人化推薦系統
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1">/// - 學習分析報表
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1">/// 相依模組：ReadingProgressTracker, BookMetadata, UserPreferences
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"></span><span class="n">ReadingSpeed</span> <span class="n">calculateReadingSpeed</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">  <span class="n">ReadingProgress</span> <span class="n">progress</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="n">BookMetadata</span> <span class="n">metadata</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">  <span class="n">Duration</span> <span class="n">actualReadingTime</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">  <span class="c1">// 實作...
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><h2 id="禁止的註解模式">禁止的註解模式</h2>
<p>最常見的錯誤是重述程式碼行為。「設定書籍標題」對應的程式碼是 <code>book.setTitle(newTitle)</code>，完全多餘。「使用 Map 快速查找避免 O(n) 複雜度」也一樣，有經驗的開發者看程式碼就知道。</p>
<p>UI 層特別容易出現這類問題。以 Widget 選取回饋設計為例，錯誤做法是列出技術細節：</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">/// BookListItem - 書庫列表項目 Widget
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1">///
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1">/// 視覺設計：
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1">/// - 陰影刻痕變化（凸起→凹陷）
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1">/// - AnimatedContainer 200ms 過渡動畫
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1">///
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1">/// 觸覺回饋：
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1">/// - 選擇時：HapticFeedback.selectionClick()
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1">/// - 取消選擇：HapticFeedback.lightImpact()
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookListItem</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="c1">// ...
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><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="c1">/// 【需求來源】UC-05: 雙模式書庫展示切換 - 書籍選擇互動
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1">/// 【規格文件】docs/ui_design_specification.md#book-selection-feedback
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1">/// 【設計決策】採用方案C-1基礎版 - 極簡視覺回饋設計
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1">/// 【為什麼選擇陰影刻痕變化】
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1">/// - 不影響文字可讀性：避免背景色干擾閱讀體驗
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1">/// - 符合無障礙設計：不依賴顏色作為唯一視覺提示
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1">/// 【為什麼選擇差異化觸覺回饋】
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1">/// - 選中 vs 取消必須有不同的觸覺回饋類型
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1">/// - selectionClick 提供明確的「確認」感受
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1">/// - lightImpact 提供輕微的「狀態變更」提示
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1">/// 【修改約束】
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1">/// - 觸覺回饋時機不可調換（與使用者預期一致）
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1">/// - 陰影變化動畫時長需保持 &lt; 250ms（符合 Material Design 規範）
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1">/// 【維護警告】
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1">/// - 此 Widget 被 3 個書庫頁面使用
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1">/// - 修改視覺回饋會影響整體使用者體驗一致性
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookListItem</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">  <span class="c1">// ...
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><p>模糊描述也不可取——「處理書籍相關邏輯」等於沒有描述。過時的 TODO 必須清除，否則會讓人以為某個功能還沒實作。</p>
<h2 id="第五原則事件驅動架構的特殊需求">第五原則：事件驅動架構的特殊需求</h2>
<p>在 UseCase 或 Domain 層，函式名稱包含 <code>handle*</code>、<code>on*</code>、<code>process*</code>、<code>emit*</code>、<code>dispatch*</code>，或回傳類型為 <code>Future&lt;&gt;</code>、<code>Stream&lt;&gt;</code> 的函式，都需要標示其在事件流中的角色：</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">/// 【需求來源】UC-01: Chrome Extension 匯入書籍資料
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1">/// 【規格文件】docs/app-requirements-spec.md#chrome-extension-import
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1">/// 【設計方案】方案C-1基礎版 (v0.12.7 Phase 1)
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1">/// 【工作日誌】docs/work-logs/v0.12.7.md - 方案研究和設計決策
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1">/// 【事件類型】BookAdded 事件處理
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1">/// 【修改約束】修改時需確保事件流完整性，避免影響上游訂閱者
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1">/// 【維護警告】此函式被 3 個 UseCase 依賴，修改前需檢查影響範圍
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span><span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">handleBookAdded</span><span class="p">(</span><span class="n">BookAddedEvent</span> <span class="n">event</span><span class="p">)</span> <span class="kd">async</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="c1">// 實作...
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><p>以 <code>_</code> 開頭的私有輔助函式（<code>_isValid*</code>、<code>_format*</code>、<code>_convert*</code>、<code>_validate*</code> 等）不包含業務邏輯，豁免詳細業務註解。</p>
<h2 id="第六原則widget-獨立性的明確標示">第六原則：Widget 獨立性的明確標示</h2>
<p>非私有命名（不以 <code>_</code> 開頭）、繼承自 <code>StatefulWidget</code>、<code>ConsumerWidget</code>、<code>StreamBuilder</code> 或 <code>FutureBuilder</code> 的 Widget，具備獨立狀態，需要明確記錄需求來源和修改約束：</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">/// 【需求來源】UC-05: 雙模式書庫展示切換
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1">/// 【規格文件】docs/ui_design_specification.md#book-list-item
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1">/// 【設計方案】方案C-1基礎版 - 陰影刻痕變化 + 觸覺回饋
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1">/// 【工作日誌】docs/work-logs/v0.12.7.md - UI 互動設計
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1">/// 【Widget 類型】獨立狀態管理 Widget
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1">/// 【修改約束】此 Widget 具備獨立狀態，下層刷新不觸發上層重建
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1">/// 【維護警告】修改前需確認子 Widget 依賴關係
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookListItem</span> <span class="kd">extends</span> <span class="n">StatefulWidget</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="c1">// 實作...
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><p>私有的 <code>StatelessWidget</code>（如 <code>_BookTitleText</code>、<code>_ProgressBar</code>）和純展示型組件，只展示上層傳遞的資料，豁免詳細業務邏輯註解。</p>
<h2 id="第七原則工作日誌與規格文件的追溯鏈">第七原則：工作日誌與規格文件的追溯鏈</h2>
<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="o">///</span> <span class="err">【工作日誌】</span><span class="n">docs</span><span class="o">/</span><span class="n">work</span><span class="o">-</span><span class="n">logs</span><span class="o">/</span><span class="n">v0</span><span class="p">.</span><span class="m">12.7</span><span class="p">.</span><span class="n">md</span> <span class="o">-</span> <span class="err">方案</span><span class="n">C</span><span class="o">-</span><span class="m">1</span><span class="err">基礎版設計</span></span></span></code></pre></div><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="c1">/// 【規格文件】docs/app-requirements-spec.md#section-name
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span><span class="o">///</span> <span class="err">【規格文件】</span><span class="n">docs</span><span class="o">/</span><span class="n">event</span><span class="o">-</span><span class="n">driven</span><span class="o">-</span><span class="n">architecture</span><span class="o">-</span><span class="n">design</span><span class="p">.</span><span class="n">md</span><span class="err">#</span><span class="n">event</span><span class="o">-</span><span class="n">flow</span></span></span></code></pre></div><p>這讓程式碼成為整個需求、設計、實作文件體系的一部分，而不是孤立的存在。</p>
<h2 id="品質驗證兩個測試">品質驗證：兩個測試</h2>
<p><strong>可執行性測試</strong>：維護者看到這條註解後，能理解業務需求嗎？修改約束明確嗎？需求來源可以追溯嗎？</p>
<p><strong>必要性測試</strong>：移除這條註解後，是否會遺失業務脈絡？如果移除後仍能理解程式邏輯，就要檢查它是否只是在重述程式碼。如果內容過時，直接刪除，不要保留會誤導維護者的假資訊。</p>
<h2 id="結論">結論</h2>
<p>好的程式碼是自說明的，但好的業務系統還需要一個跨越時間的溝通機制，讓六個月後接手的人能理解每個設計決策背後的原因，不會在不了解背景的情況下破壞原始設計。</p>
<p>這就是我們對程式碼註解的重新定位——需求的守護者。</p>
<p>這是需求保護機制，不是文書工作。</p>]]></content:encoded></item><item><title>層級隔離：讓每張 Ticket 只做一件層級的事</title><link>https://tarrragon.github.io/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/</link><pubDate>Wed, 04 Mar 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/</guid><description>&lt;p>架構圖貼出來，層級畫得漂漂亮亮，但 PR 送進來還是一次動了 UI、Controller、UseCase 和 Entity 四層。&lt;/p></description><content:encoded><![CDATA[<p>架構圖貼出來，層級畫得漂漂亮亮，但 PR 送進來還是一次動了 UI、Controller、UseCase 和 Entity 四層。</p>
<h2 id="問題不在架構在派工">問題不在架構，在派工</h2>
<p>Clean Architecture 告訴你「怎麼組織程式碼」，但沒告訴你「怎麼拆 Ticket」。每次 Code Review 都像翻地層，從 Widget 翻到 Entity，不知道從哪開始看；Domain 沒穩定，UI 那層就沒辦法測，整個流程互相等待。</p>
<p>這個銜接點，需要一套專門處理 Ticket 拆法的方法論。</p>
<h2 id="核心原則一張-ticket一個層級">核心原則：一張 Ticket，一個層級</h2>
<blockquote>
<p>一個 Ticket 只應該修改單一架構層級的程式碼，變更的原因單一且明確。</p></blockquote>
<p>SRP 說一個類別只有一個改變的原因，我們把它升一層：一張 Ticket 也只有一個改變的原因。</p>
<p>聽起來嚴苛，但實際跑起來好處很直接：Code Review 只需要理解一層的邏輯、測試不需要拉起整個系統、PR 影響範圍可預測，壞掉的時候更容易定位。</p>
<h2 id="我們怎麼定義五層">我們怎麼定義「五層」</h2>
<p>傳統 Clean Architecture 四層中，Interface Adapters 同時處理「事件邏輯」和「資料轉換」，職責太雜，我們把它細分成五層：</p>
<p><strong>Layer 1 — UI/Presentation</strong>：純視覺呈現，Widget 長什麼樣。變更原因只有一個：設計稿改了。</p>
<p><strong>Layer 2 — Application/Behavior</strong>：事件處理和 UI 邏輯。按鈕點擊怎麼處理、Loading 狀態怎麼切換、Domain Entity 怎麼轉成 ViewModel。Flutter 對應 Controller 和 ViewModel。</p>
<p><strong>Layer 3 — UseCase</strong>：業務流程編排。協調多個 Repository 和 Domain Service，把業務步驟串起來。不管 UI 怎麼顯示，也不管資料庫怎麼存。</p>
<p><strong>Layer 4 — Domain Events/Interfaces</strong>：定義契約。Repository 抽象介面、Domain Event 結構、跨層 DTO。只定義，不實作。</p>
<p><strong>Layer 5 — Domain Implementation</strong>：核心業務邏輯。Entity、Value Object、Domain Service、業務規則驗證。整個系統最穩定的部分。</p>
<p>Infrastructure 層（資料庫、外部 API、EventBus）不納入層級隔離，它的變更驅動是技術決策，不是業務需求，Ticket 設計上本來就獨立對待。</p>
<h2 id="從外而內而不是從內而外">從外而內，而不是從內而外</h2>
<p>許多教材說「先設計 Domain 再往外做」，但實際開發時，我們發現從外而內更能控制風險。</p>
<p>原因很簡單：Layer 1 UI 壞掉只影響視覺，Layer 5 Domain 邏輯壞掉影響整個系統的業務規則。從影響最小的地方開始，需求偏差時調整成本低；一開始就動 Domain，到了 UI 才發現需求理解有誤，代價就大得多。</p>
<p>實作順序是 Layer 1 → Layer 2 → Layer 3 → Layer 4 → Layer 5，每層完成後立即驗證。</p>
<p>有幾個例外：架構遷移要先定義 Layer 4 介面契約（Interface-First），讓外層修改有穩定依據；安全性修復從 Layer 5 往外；Bug Fix 從問題根源那層開始。</p>
<h2 id="ticket-拆分的量化標準">Ticket 拆分的量化標準</h2>
<p>幾個判斷指標：修改檔案數 1 到 3 個（最多 5 個）、預估開發時間 2 到 8 小時（超過一天就拆）、修改層級嚴格限制 1 層、新增程式碼測試覆蓋率 100%。</p>
<p>數字可以商議，但有標準就不用靠直覺判斷「感覺差不多」。</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">Ticket：實作書籍收藏功能
</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">- lib/ui/pages/book_detail_page.dart       (Layer 1)
</span></span><span class="line"><span class="ln">5</span><span class="cl">- lib/application/controllers/book_detail_controller.dart  (Layer 2)
</span></span><span class="line"><span class="ln">6</span><span class="cl">- lib/usecases/add_book_to_favorite_usecase.dart  (Layer 3)
</span></span><span class="line"><span class="ln">7</span><span class="cl">- lib/domain/entities/favorite.dart        (Layer 5)</span></span></code></pre></div><p>這張 Ticket 跨了四層，PR 送出來沒人知道從哪開始審，測試也很難設計。正確做法是拆成四張各自獨立的 Ticket，按依賴順序執行。</p>
<h2 id="如何判斷一段程式碼屬於哪一層">如何判斷一段程式碼屬於哪一層</h2>
<p>最常模糊的是 Layer 2 和 Layer 3 之間的邊界。判斷流程：</p>
<ol>
<li>在渲染 UI 元素？→ Layer 1</li>
<li>在處理 UI 事件、控制 UI 狀態、或把 Domain 資料轉成 UI 格式？→ Layer 2（把 Domain Exception 轉成 ErrorViewModel 也是這層的事）</li>
<li>在協調多個 Domain Service 或 Repository、編排業務步驟？→ Layer 3</li>
<li>在定義介面契約或事件結構？→ Layer 4</li>
<li>在實作業務規則或定義 Entity？→ Layer 5</li>
<li>以上都不是 → Infrastructure 層</li>
</ol>
<h2 id="這套方法論的定位">這套方法論的定位</h2>
<p>這是 Clean Architecture 的「派工指南」。Clean Architecture 告訴你程式碼怎麼組織，層級隔離告訴你 Ticket 怎麼拆、按什麼順序做。</p>
<p>它和 Atomic Ticket 方法論也不衝突：Atomic Ticket 強調職責維度（一個 Action 加一個 Target），層級隔離強調層級維度（一個 Ticket 只動一層），兩個維度同時符合才是最完整的 Ticket 設計。</p>
<p>緊急 Hotfix、原型開發、一次性腳本不需要強行套用。但在正常功能開發和重構中，跑起來之後的感覺是：每次把一個大需求拆成按層排好的 Ticket 序列，就等於把架構邊界重新確認了一遍。</p>]]></content:encoded></item><item><title>MVVM ViewModel 開發方法論</title><link>https://tarrragon.github.io/blog/record/mvvm-viewmodel-%E9%96%8B%E7%99%BC%E6%96%B9%E6%B3%95%E8%AB%96/</link><pubDate>Mon, 13 Oct 2025 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/mvvm-viewmodel-%E9%96%8B%E7%99%BC%E6%96%B9%E6%B3%95%E8%AB%96/</guid><description>&lt;h2 id="前言">前言&lt;/h2>
&lt;p>為了提升AI開發前端的穩定性，我想依賴MVVM確實定義前端的狀態跟模型，以及責任分層，這樣可以降低除錯的複雜度&lt;/p>
&lt;h2 id="核心概念">核心概念&lt;/h2>
&lt;h3 id="viewmodel-定位">ViewModel 定位&lt;/h3>
&lt;p>&lt;strong>ViewModel 是 MVVM 架構的核心層&lt;/strong>，負責：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Domain → UI 轉換&lt;/strong>：將 Domain 模型轉為 UI 需要的格式&lt;/li>
&lt;li>&lt;strong>UI 狀態管理&lt;/strong>：管理 Widget 狀態和互動邏輯&lt;/li>
&lt;li>&lt;strong>Provider 定義&lt;/strong>：定義 Riverpod Provider 供 Widget 使用&lt;/li>
&lt;li>&lt;strong>UI 專用計算邏輯&lt;/strong>：提供顏色、圖標、格式化文字等 UI 屬性&lt;/li>
&lt;/ol>
&lt;h3 id="mvvm-分層原則">MVVM 分層原則&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">┌─────────────────────────────────────────┐
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">│ Presentation Layer (UI 層) │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">├─────────────────────────────────────────┤
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">│ Widget (Page/Extensions) │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">│ - 純 UI 組裝，無業務邏輯 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">│ - 使用 ViewModel Provider 取得資料 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">│ - 顯示 ViewModel 提供的 UI 屬性 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">├─────────────────────────────────────────┤
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">│ ViewModel │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">│ - Domain → UI 轉換 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">│ - UI 狀態管理 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">│ - UI 專用計算邏輯 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">│ - Provider 定義 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">├─────────────────────────────────────────┤
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">│ Mapper │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">│ - Domain 模型 → ViewModel 轉換邏輯 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">├─────────────────────────────────────────┤
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">│ Domain Layer (領域層) │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">19&lt;/span>&lt;span class="cl">│ - 業務邏輯 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">20&lt;/span>&lt;span class="cl">│ - Domain 模型 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">21&lt;/span>&lt;span class="cl">│ - Domain 服務 │
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">22&lt;/span>&lt;span class="cl">└─────────────────────────────────────────┘&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="viewmodel-命名規範">ViewModel 命名規範&lt;/h2>
&lt;h3 id="命名格式">命名格式&lt;/h3>
&lt;p>&lt;strong>格式&lt;/strong>：&lt;code>[Feature]ViewModel&lt;/code>&lt;/p>
&lt;p>&lt;strong>範例&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>&lt;code>EnrichmentProgressViewModel&lt;/code> - 補充進度顯示&lt;/li>
&lt;li>&lt;code>ChromeExtensionImportViewModel&lt;/code> - Chrome Extension 匯入&lt;/li>
&lt;li>&lt;code>LibraryDisplayViewModel&lt;/code> - 書庫展示&lt;/li>
&lt;li>&lt;code>AdvancedSearchViewModel&lt;/code> - 進階搜尋&lt;/li>
&lt;/ul>
&lt;h3 id="檔案位置">檔案位置&lt;/h3>
&lt;p>&lt;strong>標準路徑&lt;/strong>：&lt;code>lib/presentation/[feature]/[feature]_viewmodel.dart&lt;/code>&lt;/p>
&lt;p>&lt;strong>範例&lt;/strong>：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">lib/presentation/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">├── import/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">│ └── chrome_extension_import_viewmodel.dart
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">├── library/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">│ ├── library_viewmodel.dart
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">│ └── library_display_page.dart
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">└── search/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl"> └── advanced_search_viewmodel.dart&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="viewmodel-職責定義">ViewModel 職責定義&lt;/h2>
&lt;h3 id="包含的職責">包含的職責&lt;/h3>
&lt;h4 id="1-domain--ui-轉換">1. Domain → UI 轉換&lt;/h4>
&lt;p>將 Domain 模型轉換為 UI 需要的格式：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-dart" data-lang="dart">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1">/// Domain 來源
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">final&lt;/span> &lt;span class="n">EnrichmentProgress&lt;/span> &lt;span class="n">domainProgress&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1">/// UI 專用欄位（計算屬性）
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">String&lt;/span> &lt;span class="kd">get&lt;/span> &lt;span class="n">displayStatus&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="n">_mapStatus&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="n">IconData&lt;/span> &lt;span class="kd">get&lt;/span> &lt;span class="n">statusIcon&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="n">_mapIcon&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="n">Color&lt;/span> &lt;span class="kd">get&lt;/span> &lt;span class="n">progressColor&lt;/span> &lt;span class="o">=&amp;gt;&lt;/span> &lt;span class="n">_mapColor&lt;/span>&lt;span class="p">();&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="2-ui-狀態管理">2. UI 狀態管理&lt;/h4>
&lt;p>管理 Widget 需要的狀態：&lt;/p></description><content:encoded><![CDATA[<h2 id="前言">前言</h2>
<p>為了提升AI開發前端的穩定性，我想依賴MVVM確實定義前端的狀態跟模型，以及責任分層，這樣可以降低除錯的複雜度</p>
<h2 id="核心概念">核心概念</h2>
<h3 id="viewmodel-定位">ViewModel 定位</h3>
<p><strong>ViewModel 是 MVVM 架構的核心層</strong>，負責：</p>
<ol>
<li><strong>Domain → UI 轉換</strong>：將 Domain 模型轉為 UI 需要的格式</li>
<li><strong>UI 狀態管理</strong>：管理 Widget 狀態和互動邏輯</li>
<li><strong>Provider 定義</strong>：定義 Riverpod Provider 供 Widget 使用</li>
<li><strong>UI 專用計算邏輯</strong>：提供顏色、圖標、格式化文字等 UI 屬性</li>
</ol>
<h3 id="mvvm-分層原則">MVVM 分層原則</h3>





<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><span class="line"><span class="ln"> 2</span><span class="cl">│ Presentation Layer (UI 層)              │
</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">│ Widget (Page/Extensions)                │
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">│ - 純 UI 組裝，無業務邏輯                 │
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">│ - 使用 ViewModel Provider 取得資料       │
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">│ - 顯示 ViewModel 提供的 UI 屬性          │
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">├─────────────────────────────────────────┤
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">│ ViewModel                               │
</span></span><span class="line"><span class="ln">10</span><span class="cl">│ - Domain → UI 轉換                       │
</span></span><span class="line"><span class="ln">11</span><span class="cl">│ - UI 狀態管理                            │
</span></span><span class="line"><span class="ln">12</span><span class="cl">│ - UI 專用計算邏輯                        │
</span></span><span class="line"><span class="ln">13</span><span class="cl">│ - Provider 定義                          │
</span></span><span class="line"><span class="ln">14</span><span class="cl">├─────────────────────────────────────────┤
</span></span><span class="line"><span class="ln">15</span><span class="cl">│ Mapper                                  │
</span></span><span class="line"><span class="ln">16</span><span class="cl">│ - Domain 模型 → ViewModel 轉換邏輯       │
</span></span><span class="line"><span class="ln">17</span><span class="cl">├─────────────────────────────────────────┤
</span></span><span class="line"><span class="ln">18</span><span class="cl">│ Domain Layer (領域層)                    │
</span></span><span class="line"><span class="ln">19</span><span class="cl">│ - 業務邏輯                               │
</span></span><span class="line"><span class="ln">20</span><span class="cl">│ - Domain 模型                            │
</span></span><span class="line"><span class="ln">21</span><span class="cl">│ - Domain 服務                            │
</span></span><span class="line"><span class="ln">22</span><span class="cl">└─────────────────────────────────────────┘</span></span></code></pre></div><hr>
<h2 id="viewmodel-命名規範">ViewModel 命名規範</h2>
<h3 id="命名格式">命名格式</h3>
<p><strong>格式</strong>：<code>[Feature]ViewModel</code></p>
<p><strong>範例</strong>：</p>
<ul>
<li><code>EnrichmentProgressViewModel</code> - 補充進度顯示</li>
<li><code>ChromeExtensionImportViewModel</code> - Chrome Extension 匯入</li>
<li><code>LibraryDisplayViewModel</code> - 書庫展示</li>
<li><code>AdvancedSearchViewModel</code> - 進階搜尋</li>
</ul>
<h3 id="檔案位置">檔案位置</h3>
<p><strong>標準路徑</strong>：<code>lib/presentation/[feature]/[feature]_viewmodel.dart</code></p>
<p><strong>範例</strong>：</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">lib/presentation/
</span></span><span class="line"><span class="ln">2</span><span class="cl">├── import/
</span></span><span class="line"><span class="ln">3</span><span class="cl">│   └── chrome_extension_import_viewmodel.dart
</span></span><span class="line"><span class="ln">4</span><span class="cl">├── library/
</span></span><span class="line"><span class="ln">5</span><span class="cl">│   ├── library_viewmodel.dart
</span></span><span class="line"><span class="ln">6</span><span class="cl">│   └── library_display_page.dart
</span></span><span class="line"><span class="ln">7</span><span class="cl">└── search/
</span></span><span class="line"><span class="ln">8</span><span class="cl">    └── advanced_search_viewmodel.dart</span></span></code></pre></div><hr>
<h2 id="viewmodel-職責定義">ViewModel 職責定義</h2>
<h3 id="包含的職責">包含的職責</h3>
<h4 id="1-domain--ui-轉換">1. Domain → UI 轉換</h4>
<p>將 Domain 模型轉換為 UI 需要的格式：</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">/// Domain 來源
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span><span class="kd">final</span> <span class="n">EnrichmentProgress</span> <span class="n">domainProgress</span><span class="p">;</span>
</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 class="c1">/// UI 專用欄位（計算屬性）
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"></span><span class="kt">String</span> <span class="kd">get</span> <span class="n">displayStatus</span> <span class="o">=&gt;</span> <span class="n">_mapStatus</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="n">IconData</span> <span class="kd">get</span> <span class="n">statusIcon</span> <span class="o">=&gt;</span> <span class="n">_mapIcon</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="n">Color</span> <span class="kd">get</span> <span class="n">progressColor</span> <span class="o">=&gt;</span> <span class="n">_mapColor</span><span class="p">();</span></span></span></code></pre></div><h4 id="2-ui-狀態管理">2. UI 狀態管理</h4>
<p>管理 Widget 需要的狀態：</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="kd">class</span> <span class="nc">EnrichmentProgressViewModel</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">  <span class="c1">// 狀態欄位
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"></span>  <span class="kd">final</span> <span class="n">EnrichmentProgress</span> <span class="n">domainProgress</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">  <span class="kd">final</span> <span class="n">List</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">&gt;</span> <span class="n">failedBooks</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="c1">// UI 控制狀態
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"></span>  <span class="kt">bool</span> <span class="kd">get</span> <span class="n">showFailedBooks</span> <span class="o">=&gt;</span> <span class="n">failedBooks</span><span class="p">.</span><span class="n">isNotEmpty</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">  <span class="kt">bool</span> <span class="kd">get</span> <span class="n">canRetry</span> <span class="o">=&gt;</span> <span class="n">domainProgress</span><span class="p">.</span><span class="n">isComplete</span> <span class="o">&amp;&amp;</span> <span class="n">failedBooks</span><span class="p">.</span><span class="n">isNotEmpty</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h4 id="3-provider-定義">3. Provider 定義</h4>
<p>定義 Riverpod Provider 供 Widget 使用：</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="kd">final</span> <span class="n">enrichmentProgressViewModelProvider</span> <span class="o">=</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">  <span class="n">StreamProvider</span><span class="p">.</span><span class="n">family</span><span class="o">&lt;</span><span class="n">EnrichmentProgressViewModel</span><span class="p">,</span> <span class="kt">String</span><span class="o">&gt;</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">    <span class="p">(</span><span class="n">ref</span><span class="p">,</span> <span class="n">operationId</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">      <span class="c1">// Provider 邏輯
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"></span>    <span class="p">},</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">  <span class="p">);</span></span></span></code></pre></div><h4 id="4-ui-專用計算邏輯">4. UI 專用計算邏輯</h4>
<p>提供 UI 需要的格式化資料：</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="kt">String</span> <span class="kd">get</span> <span class="n">summaryText</span> <span class="o">=&gt;</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="s1">&#39;已處理 </span><span class="si">${</span><span class="n">domainProgress</span><span class="p">.</span><span class="n">processedBooks</span><span class="si">}</span><span class="s1">/</span><span class="si">${</span><span class="n">domainProgress</span><span class="p">.</span><span class="n">totalBooks</span><span class="si">}</span><span class="s1"> 本&#39;</span><span class="p">;</span>
</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"><span class="c1">/// 進度顏色（根據狀態決定）
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span><span class="n">Color</span> <span class="kd">get</span> <span class="n">progressColor</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">domainProgress</span><span class="p">.</span><span class="n">failedEnrichments</span> <span class="o">&gt;</span> <span class="m">0</span><span class="p">)</span> <span class="k">return</span> <span class="n">Colors</span><span class="p">.</span><span class="n">orange</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">domainProgress</span><span class="p">.</span><span class="n">isComplete</span><span class="p">)</span> <span class="k">return</span> <span class="n">Colors</span><span class="p">.</span><span class="n">green</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="k">return</span> <span class="n">Colors</span><span class="p">.</span><span class="n">blue</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h3 id="不包含的內容">不包含的內容</h3>
<h4 id="1-widget-程式碼">1. Widget 程式碼</h4>





<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">// 反例：ViewModel 中包含 Widget
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">EnrichmentProgressViewModel</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="n">Widget</span> <span class="n">buildProgressBar</span><span class="p">()</span> <span class="p">{</span>  <span class="c1">// 違規
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"></span>    <span class="k">return</span> <span class="n">LinearProgressIndicator</span><span class="p">(...);</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="p">}</span>
</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"><span class="c1">// 正例：Widget 在 Extension 中
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"></span><span class="n">extension</span> <span class="n">EnrichmentProgressWidgets</span> <span class="n">on</span> <span class="n">Widget</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="n">Widget</span> <span class="n">enrichmentProgressBar</span><span class="p">(</span><span class="n">EnrichmentProgressViewModel</span> <span class="n">vm</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="k">return</span> <span class="n">LinearProgressIndicator</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">      <span class="nl">value:</span> <span class="n">vm</span><span class="p">.</span><span class="n">domainProgress</span><span class="p">.</span><span class="n">percentageComplete</span> <span class="o">/</span> <span class="m">100</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">      <span class="nl">color:</span> <span class="n">vm</span><span class="p">.</span><span class="n">progressColor</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h4 id="2-直接依賴-flutter-框架除-changenotifier">2. 直接依賴 Flutter 框架（除 ChangeNotifier）</h4>





<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">// 反例：依賴 Flutter Material
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="k">import</span> <span class="s1">&#39;package:flutter/material.dart&#39;</span><span class="p">;</span>
</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 class="kd">class</span> <span class="nc">MyViewModel</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="n">BuildContext</span><span class="o">?</span> <span class="n">context</span><span class="p">;</span>  <span class="c1">// 違規
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span><span class="p">}</span>
</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"><span class="c1">// 正例：使用 Dart 原生類型
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">MyViewModel</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="n">Color</span> <span class="n">progressColor</span><span class="p">;</span>  <span class="c1">// 可以使用 Color（來自 dart:ui）
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"></span>  <span class="n">IconData</span> <span class="n">statusIcon</span><span class="p">;</span>  <span class="c1">// 可以使用 IconData
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><h4 id="3-業務邏輯">3. 業務邏輯</h4>





<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">// 反例：在 ViewModel 中執行業務邏輯
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">EnrichmentProgressViewModel</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">enrichBook</span><span class="p">(</span><span class="n">Book</span> <span class="n">book</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="c1">// 呼叫 API、驗證資料、儲存到資料庫
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span>    <span class="c1">// 這些是 Domain 層的職責
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1">// 正例：業務邏輯在 Domain Service
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">IBookInfoEnrichmentService</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="n">EnrichedBookInfo</span><span class="o">&gt;</span> <span class="n">enrichBookInfo</span><span class="p">(</span><span class="n">Book</span> <span class="n">book</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1">// ViewModel 只負責狀態管理
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">EnrichmentProgressViewModel</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="kd">final</span> <span class="n">EnrichmentProgress</span> <span class="n">domainProgress</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">  <span class="c1">// 不包含業務邏輯
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><hr>
<h2 id="viewmodel-結構範本">ViewModel 結構範本</h2>
<h3 id="基本結構">基本結構</h3>





<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">/// UI 層專用的 [Feature] 顯示模型
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1">///
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1">/// 職責：
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1">/// - 將 Domain 模型轉換為 UI 需要的格式
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1">/// - 提供 UI 專用的計算屬性
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1">/// - 管理 UI 狀態
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1">///
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1">/// 需求：[需求編號]
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="err">[</span><span class="nc">Feature</span><span class="p">]</span><span class="n">ViewModel</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="c1">// =============================================================================
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"></span>  <span class="c1">// Domain 來源（不可變）
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"></span>  <span class="c1">// =============================================================================
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">  <span class="c1">/// Domain 模型來源
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"></span>  <span class="kd">final</span> <span class="p">[</span><span class="n">DomainModel</span><span class="p">]</span> <span class="n">domainModel</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl">  <span class="c1">/// 額外的 Domain 資料（如失敗清單）
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"></span>  <span class="kd">final</span> <span class="n">List</span><span class="o">&lt;</span><span class="p">[</span><span class="n">Entity</span><span class="p">]</span><span class="o">&gt;</span> <span class="n">additionalData</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl">  <span class="c1">// =============================================================================
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="c1"></span>  <span class="c1">// UI 專用欄位（計算屬性）
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="c1"></span>  <span class="c1">// =============================================================================
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">  <span class="c1">/// 狀態顯示文字
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="c1"></span>  <span class="kt">String</span> <span class="kd">get</span> <span class="n">displayStatus</span> <span class="o">=&gt;</span> <span class="n">_mapStatus</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl">  <span class="c1">/// 狀態圖標
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="c1"></span>  <span class="n">IconData</span> <span class="kd">get</span> <span class="n">statusIcon</span> <span class="o">=&gt;</span> <span class="n">_mapIcon</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">
</span></span><span class="line"><span class="ln">30</span><span class="cl">  <span class="c1">/// 進度顏色
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="c1"></span>  <span class="n">Color</span> <span class="kd">get</span> <span class="n">progressColor</span> <span class="o">=&gt;</span> <span class="n">_mapColor</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">
</span></span><span class="line"><span class="ln">33</span><span class="cl">  <span class="c1">/// 摘要文字
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="c1"></span>  <span class="kt">String</span> <span class="kd">get</span> <span class="n">summaryText</span> <span class="o">=&gt;</span> <span class="n">_formatSummary</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">
</span></span><span class="line"><span class="ln">36</span><span class="cl">  <span class="c1">// =============================================================================
</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="c1"></span>  <span class="c1">// 建構子
</span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="c1"></span>  <span class="c1">// =============================================================================
</span></span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">40</span><span class="cl">  <span class="kd">const</span> <span class="p">[</span><span class="n">Feature</span><span class="p">]</span><span class="n">ViewModel</span><span class="p">({</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">    <span class="kd">required</span> <span class="k">this</span><span class="p">.</span><span class="n">domainModel</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">42</span><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="n">additionalData</span> <span class="o">=</span> <span class="kd">const</span> <span class="p">[],</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="ln">44</span><span class="cl">
</span></span><span class="line"><span class="ln">45</span><span class="cl">  <span class="c1">// =============================================================================
</span></span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="c1"></span>  <span class="c1">// Domain → UI 轉換方法（私有）
</span></span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="c1"></span>  <span class="c1">// =============================================================================
</span></span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">49</span><span class="cl">  <span class="c1">/// 對應狀態到顯示文字
</span></span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="c1"></span>  <span class="kt">String</span> <span class="n">_mapStatus</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">51</span><span class="cl">    <span class="c1">// 轉換邏輯
</span></span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln">53</span><span class="cl">
</span></span><span class="line"><span class="ln">54</span><span class="cl">  <span class="c1">/// 對應狀態到圖標
</span></span></span><span class="line"><span class="ln">55</span><span class="cl"><span class="c1"></span>  <span class="n">IconData</span> <span class="n">_mapIcon</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">56</span><span class="cl">    <span class="c1">// 轉換邏輯
</span></span></span><span class="line"><span class="ln">57</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln">58</span><span class="cl">
</span></span><span class="line"><span class="ln">59</span><span class="cl">  <span class="c1">/// 對應狀態到顏色
</span></span></span><span class="line"><span class="ln">60</span><span class="cl"><span class="c1"></span>  <span class="n">Color</span> <span class="n">_mapColor</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">61</span><span class="cl">    <span class="c1">// 轉換邏輯
</span></span></span><span class="line"><span class="ln">62</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln">63</span><span class="cl">
</span></span><span class="line"><span class="ln">64</span><span class="cl">  <span class="c1">/// 格式化摘要文字
</span></span></span><span class="line"><span class="ln">65</span><span class="cl"><span class="c1"></span>  <span class="kt">String</span> <span class="n">_formatSummary</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">66</span><span class="cl">    <span class="c1">// 格式化邏輯
</span></span></span><span class="line"><span class="ln">67</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln">68</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h3 id="完整範例enrichmentprogressviewmodel">完整範例：EnrichmentProgressViewModel</h3>





<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="k">import</span> <span class="s1">&#39;package:flutter/material.dart&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">  2</span><span class="cl"><span class="k">import</span> <span class="s1">&#39;package:book_overview_app/domains/import/value_objects/enrichment_progress.dart&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">  3</span><span class="cl"><span class="k">import</span> <span class="s1">&#39;package:book_overview_app/domains/library/entities/book.dart&#39;</span><span class="p">;</span>
</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"><span class="c1">/// UI 層專用的補充進度顯示模型
</span></span></span><span class="line"><span class="ln">  6</span><span class="cl"><span class="c1">///
</span></span></span><span class="line"><span class="ln">  7</span><span class="cl"><span class="c1">/// 職責：
</span></span></span><span class="line"><span class="ln">  8</span><span class="cl"><span class="c1">/// - 將 EnrichmentProgress Domain 模型轉為 UI 格式
</span></span></span><span class="line"><span class="ln">  9</span><span class="cl"><span class="c1">/// - 提供進度顏色、圖標、文字等 UI 屬性
</span></span></span><span class="line"><span class="ln"> 10</span><span class="cl"><span class="c1">/// - 管理失敗書籍清單的顯示
</span></span></span><span class="line"><span class="ln"> 11</span><span class="cl"><span class="c1">///
</span></span></span><span class="line"><span class="ln"> 12</span><span class="cl"><span class="c1">/// 需求：UC-01.Enrichment.Progress
</span></span></span><span class="line"><span class="ln"> 13</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">EnrichmentProgressViewModel</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 14</span><span class="cl">  <span class="c1">// =============================================================================
</span></span></span><span class="line"><span class="ln"> 15</span><span class="cl"><span class="c1"></span>  <span class="c1">// Domain 來源
</span></span></span><span class="line"><span class="ln"> 16</span><span class="cl"><span class="c1"></span>  <span class="c1">// =============================================================================
</span></span></span><span class="line"><span class="ln"> 17</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 18</span><span class="cl">  <span class="c1">/// Domain 進度模型
</span></span></span><span class="line"><span class="ln"> 19</span><span class="cl"><span class="c1"></span>  <span class="kd">final</span> <span class="n">EnrichmentProgress</span> <span class="n">domainProgress</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 20</span><span class="cl">
</span></span><span class="line"><span class="ln"> 21</span><span class="cl">  <span class="c1">/// 失敗補充的書籍清單
</span></span></span><span class="line"><span class="ln"> 22</span><span class="cl"><span class="c1"></span>  <span class="kd">final</span> <span class="n">List</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">&gt;</span> <span class="n">failedBooks</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 23</span><span class="cl">
</span></span><span class="line"><span class="ln"> 24</span><span class="cl">  <span class="c1">// =============================================================================
</span></span></span><span class="line"><span class="ln"> 25</span><span class="cl"><span class="c1"></span>  <span class="c1">// UI 專用欄位（計算屬性）
</span></span></span><span class="line"><span class="ln"> 26</span><span class="cl"><span class="c1"></span>  <span class="c1">// =============================================================================
</span></span></span><span class="line"><span class="ln"> 27</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 28</span><span class="cl">  <span class="c1">/// 狀態顯示文字
</span></span></span><span class="line"><span class="ln"> 29</span><span class="cl"><span class="c1"></span>  <span class="c1">///
</span></span></span><span class="line"><span class="ln"> 30</span><span class="cl"><span class="c1"></span>  <span class="c1">/// 對應規則：
</span></span></span><span class="line"><span class="ln"> 31</span><span class="cl"><span class="c1"></span>  <span class="c1">/// - processedBooks == 0 → &#34;準備中&#34;
</span></span></span><span class="line"><span class="ln"> 32</span><span class="cl"><span class="c1"></span>  <span class="c1">/// - processedBooks &gt; 0 &amp;&amp; !isComplete → &#34;補充中&#34;
</span></span></span><span class="line"><span class="ln"> 33</span><span class="cl"><span class="c1"></span>  <span class="c1">/// - isComplete → &#34;已完成&#34;
</span></span></span><span class="line"><span class="ln"> 34</span><span class="cl"><span class="c1"></span>  <span class="kt">String</span> <span class="kd">get</span> <span class="n">displayStatus</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 35</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">domainProgress</span><span class="p">.</span><span class="n">isComplete</span><span class="p">)</span> <span class="k">return</span> <span class="s1">&#39;已完成&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 36</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">domainProgress</span><span class="p">.</span><span class="n">processedBooks</span> <span class="o">==</span> <span class="m">0</span><span class="p">)</span> <span class="k">return</span> <span class="s1">&#39;準備中&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 37</span><span class="cl">    <span class="k">return</span> <span class="s1">&#39;補充中&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 38</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 39</span><span class="cl">
</span></span><span class="line"><span class="ln"> 40</span><span class="cl">  <span class="c1">/// 狀態圖標
</span></span></span><span class="line"><span class="ln"> 41</span><span class="cl"><span class="c1"></span>  <span class="c1">///
</span></span></span><span class="line"><span class="ln"> 42</span><span class="cl"><span class="c1"></span>  <span class="c1">/// 對應規則：
</span></span></span><span class="line"><span class="ln"> 43</span><span class="cl"><span class="c1"></span>  <span class="c1">/// - 準備中 → Icons.pending
</span></span></span><span class="line"><span class="ln"> 44</span><span class="cl"><span class="c1"></span>  <span class="c1">/// - 補充中 → Icons.sync
</span></span></span><span class="line"><span class="ln"> 45</span><span class="cl"><span class="c1"></span>  <span class="c1">/// - 已完成 → Icons.check_circle
</span></span></span><span class="line"><span class="ln"> 46</span><span class="cl"><span class="c1"></span>  <span class="n">IconData</span> <span class="kd">get</span> <span class="n">statusIcon</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 47</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">domainProgress</span><span class="p">.</span><span class="n">isComplete</span><span class="p">)</span> <span class="k">return</span> <span class="n">Icons</span><span class="p">.</span><span class="n">check_circle</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 48</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">domainProgress</span><span class="p">.</span><span class="n">processedBooks</span> <span class="o">==</span> <span class="m">0</span><span class="p">)</span> <span class="k">return</span> <span class="n">Icons</span><span class="p">.</span><span class="n">pending</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 49</span><span class="cl">    <span class="k">return</span> <span class="n">Icons</span><span class="p">.</span><span class="kd">sync</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 50</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 51</span><span class="cl">
</span></span><span class="line"><span class="ln"> 52</span><span class="cl">  <span class="c1">/// 進度顏色
</span></span></span><span class="line"><span class="ln"> 53</span><span class="cl"><span class="c1"></span>  <span class="c1">///
</span></span></span><span class="line"><span class="ln"> 54</span><span class="cl"><span class="c1"></span>  <span class="c1">/// 對應規則：
</span></span></span><span class="line"><span class="ln"> 55</span><span class="cl"><span class="c1"></span>  <span class="c1">/// - 有失敗 → 橘色警告
</span></span></span><span class="line"><span class="ln"> 56</span><span class="cl"><span class="c1"></span>  <span class="c1">/// - 已完成 → 綠色成功
</span></span></span><span class="line"><span class="ln"> 57</span><span class="cl"><span class="c1"></span>  <span class="c1">/// - 進行中 → 藍色
</span></span></span><span class="line"><span class="ln"> 58</span><span class="cl"><span class="c1"></span>  <span class="n">Color</span> <span class="kd">get</span> <span class="n">progressColor</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 59</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">domainProgress</span><span class="p">.</span><span class="n">failedEnrichments</span> <span class="o">&gt;</span> <span class="m">0</span><span class="p">)</span> <span class="k">return</span> <span class="n">Colors</span><span class="p">.</span><span class="n">orange</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 60</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">domainProgress</span><span class="p">.</span><span class="n">isComplete</span><span class="p">)</span> <span class="k">return</span> <span class="n">Colors</span><span class="p">.</span><span class="n">green</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 61</span><span class="cl">    <span class="k">return</span> <span class="n">Colors</span><span class="p">.</span><span class="n">blue</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 62</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 63</span><span class="cl">
</span></span><span class="line"><span class="ln"> 64</span><span class="cl">  <span class="c1">/// 摘要文字
</span></span></span><span class="line"><span class="ln"> 65</span><span class="cl"><span class="c1"></span>  <span class="c1">///
</span></span></span><span class="line"><span class="ln"> 66</span><span class="cl"><span class="c1"></span>  <span class="c1">/// 格式：「已處理 X/Y 本（成功 A，失敗 B）」
</span></span></span><span class="line"><span class="ln"> 67</span><span class="cl"><span class="c1"></span>  <span class="kt">String</span> <span class="kd">get</span> <span class="n">summaryText</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 68</span><span class="cl">    <span class="kd">final</span> <span class="n">processed</span> <span class="o">=</span> <span class="n">domainProgress</span><span class="p">.</span><span class="n">processedBooks</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 69</span><span class="cl">    <span class="kd">final</span> <span class="n">total</span> <span class="o">=</span> <span class="n">domainProgress</span><span class="p">.</span><span class="n">totalBooks</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 70</span><span class="cl">    <span class="kd">final</span> <span class="n">success</span> <span class="o">=</span> <span class="n">domainProgress</span><span class="p">.</span><span class="n">successfulEnrichments</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 71</span><span class="cl">    <span class="kd">final</span> <span class="n">failed</span> <span class="o">=</span> <span class="n">domainProgress</span><span class="p">.</span><span class="n">failedEnrichments</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 72</span><span class="cl">
</span></span><span class="line"><span class="ln"> 73</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">failed</span> <span class="o">&gt;</span> <span class="m">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 74</span><span class="cl">      <span class="k">return</span> <span class="s1">&#39;已處理 </span><span class="si">$</span><span class="n">processed</span><span class="s1">/</span><span class="si">$</span><span class="n">total</span><span class="s1"> 本（成功 </span><span class="si">$</span><span class="n">success</span><span class="s1">，失敗 </span><span class="si">$</span><span class="n">failed</span><span class="s1">）&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 75</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 76</span><span class="cl">    <span class="k">return</span> <span class="s1">&#39;已處理 </span><span class="si">$</span><span class="n">processed</span><span class="s1">/</span><span class="si">$</span><span class="n">total</span><span class="s1"> 本&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 77</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 78</span><span class="cl">
</span></span><span class="line"><span class="ln"> 79</span><span class="cl">  <span class="c1">/// 失敗書籍摘要清單
</span></span></span><span class="line"><span class="ln"> 80</span><span class="cl"><span class="c1"></span>  <span class="c1">///
</span></span></span><span class="line"><span class="ln"> 81</span><span class="cl"><span class="c1"></span>  <span class="c1">/// 提供簡化的書籍資訊供 UI 顯示
</span></span></span><span class="line"><span class="ln"> 82</span><span class="cl"><span class="c1"></span>  <span class="n">List</span><span class="o">&lt;</span><span class="n">BookSummary</span><span class="o">&gt;</span> <span class="kd">get</span> <span class="n">failedBooksSummary</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 83</span><span class="cl">    <span class="k">return</span> <span class="n">failedBooks</span><span class="p">.</span><span class="n">map</span><span class="p">((</span><span class="n">book</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="n">BookSummary</span><span class="p">.</span><span class="n">fromBook</span><span class="p">(</span><span class="n">book</span><span class="p">)).</span><span class="n">toList</span><span class="p">();</span>
</span></span><span class="line"><span class="ln"> 84</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 85</span><span class="cl">
</span></span><span class="line"><span class="ln"> 86</span><span class="cl">  <span class="c1">/// 進度百分比（直接使用 Domain 計算）
</span></span></span><span class="line"><span class="ln"> 87</span><span class="cl"><span class="c1"></span>  <span class="kt">double</span> <span class="kd">get</span> <span class="n">progressPercentage</span> <span class="o">=&gt;</span> <span class="n">domainProgress</span><span class="p">.</span><span class="n">percentageComplete</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 88</span><span class="cl">
</span></span><span class="line"><span class="ln"> 89</span><span class="cl">  <span class="c1">/// 當前處理書名（如果有）
</span></span></span><span class="line"><span class="ln"> 90</span><span class="cl"><span class="c1"></span>  <span class="kt">String</span><span class="o">?</span> <span class="kd">get</span> <span class="n">currentBookTitle</span> <span class="o">=&gt;</span> <span class="n">domainProgress</span><span class="p">.</span><span class="n">currentBook</span><span class="o">?</span><span class="p">.</span><span class="n">title</span><span class="p">.</span><span class="n">value</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 91</span><span class="cl">
</span></span><span class="line"><span class="ln"> 92</span><span class="cl">  <span class="c1">/// 是否顯示失敗清單
</span></span></span><span class="line"><span class="ln"> 93</span><span class="cl"><span class="c1"></span>  <span class="kt">bool</span> <span class="kd">get</span> <span class="n">showFailedBooks</span> <span class="o">=&gt;</span> <span class="n">failedBooks</span><span class="p">.</span><span class="n">isNotEmpty</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 94</span><span class="cl">
</span></span><span class="line"><span class="ln"> 95</span><span class="cl">  <span class="c1">/// 是否可以重試
</span></span></span><span class="line"><span class="ln"> 96</span><span class="cl"><span class="c1"></span>  <span class="kt">bool</span> <span class="kd">get</span> <span class="n">canRetry</span> <span class="o">=&gt;</span> <span class="n">domainProgress</span><span class="p">.</span><span class="n">isComplete</span> <span class="o">&amp;&amp;</span> <span class="n">failedBooks</span><span class="p">.</span><span class="n">isNotEmpty</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 97</span><span class="cl">
</span></span><span class="line"><span class="ln"> 98</span><span class="cl">  <span class="c1">// =============================================================================
</span></span></span><span class="line"><span class="ln"> 99</span><span class="cl"><span class="c1"></span>  <span class="c1">// 建構子
</span></span></span><span class="line"><span class="ln">100</span><span class="cl"><span class="c1"></span>  <span class="c1">// =============================================================================
</span></span></span><span class="line"><span class="ln">101</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">102</span><span class="cl">  <span class="kd">const</span> <span class="n">EnrichmentProgressViewModel</span><span class="p">({</span>
</span></span><span class="line"><span class="ln">103</span><span class="cl">    <span class="kd">required</span> <span class="k">this</span><span class="p">.</span><span class="n">domainProgress</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">104</span><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="n">failedBooks</span> <span class="o">=</span> <span class="kd">const</span> <span class="p">[],</span>
</span></span><span class="line"><span class="ln">105</span><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="ln">106</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">107</span><span class="cl">
</span></span><span class="line"><span class="ln">108</span><span class="cl"><span class="c1">/// 書籍摘要（UI 專用簡化資料）
</span></span></span><span class="line"><span class="ln">109</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookSummary</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">110</span><span class="cl">  <span class="kd">final</span> <span class="kt">String</span> <span class="n">id</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">111</span><span class="cl">  <span class="kd">final</span> <span class="kt">String</span> <span class="n">title</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">112</span><span class="cl">  <span class="kd">final</span> <span class="kt">String</span> <span class="n">author</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">113</span><span class="cl">
</span></span><span class="line"><span class="ln">114</span><span class="cl">  <span class="kd">const</span> <span class="n">BookSummary</span><span class="p">({</span>
</span></span><span class="line"><span class="ln">115</span><span class="cl">    <span class="kd">required</span> <span class="k">this</span><span class="p">.</span><span class="n">id</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">116</span><span class="cl">    <span class="kd">required</span> <span class="k">this</span><span class="p">.</span><span class="n">title</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">117</span><span class="cl">    <span class="kd">required</span> <span class="k">this</span><span class="p">.</span><span class="n">author</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">118</span><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="ln">119</span><span class="cl">
</span></span><span class="line"><span class="ln">120</span><span class="cl">  <span class="kd">factory</span> <span class="n">BookSummary</span><span class="p">.</span><span class="n">fromBook</span><span class="p">(</span><span class="n">Book</span> <span class="n">book</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">121</span><span class="cl">    <span class="k">return</span> <span class="n">BookSummary</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">122</span><span class="cl">      <span class="nl">id:</span> <span class="n">book</span><span class="p">.</span><span class="n">id</span><span class="p">.</span><span class="n">value</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">123</span><span class="cl">      <span class="nl">title:</span> <span class="n">book</span><span class="p">.</span><span class="n">title</span><span class="p">.</span><span class="n">value</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">124</span><span class="cl">      <span class="nl">author:</span> <span class="n">book</span><span class="p">.</span><span class="n">author</span><span class="p">.</span><span class="n">value</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">125</span><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="ln">126</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">127</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><hr>
<h2 id="mapper-模式">Mapper 模式</h2>
<h3 id="mapper-職責">Mapper 職責</h3>
<p><strong>Mapper 負責 Domain 模型 → ViewModel 的轉換邏輯</strong>。</p>
<h3 id="mapper-結構">Mapper 結構</h3>





<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">/// Domain [DomainModel] → UI ViewModel 轉換器
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1">///
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1">/// 職責：
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1">/// - 將 Domain 模型轉換為 ViewModel
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1">/// - 整合多個 Domain 資料來源
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1">/// - 處理轉換過程中的資料格式化
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="err">[</span><span class="nc">Feature</span><span class="p">]</span><span class="n">Mapper</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="c1">/// 轉換 Domain 模型為 ViewModel
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"></span>  <span class="kd">static</span> <span class="p">[</span><span class="n">Feature</span><span class="p">]</span><span class="n">ViewModel</span> <span class="n">toViewModel</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="p">[</span><span class="n">DomainModel</span><span class="p">]</span> <span class="n">domainModel</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="c1">// 額外的 Domain 資料來源
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"></span>  <span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="k">return</span> <span class="p">[</span><span class="n">Feature</span><span class="p">]</span><span class="n">ViewModel</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">      <span class="nl">domainModel:</span> <span class="n">domainModel</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">      <span class="c1">// 額外欄位轉換
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1"></span>    <span class="p">);</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl">  <span class="c1">/// 批量轉換
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="c1"></span>  <span class="kd">static</span> <span class="n">List</span><span class="o">&lt;</span><span class="p">[</span><span class="n">Feature</span><span class="p">]</span><span class="n">ViewModel</span><span class="o">&gt;</span> <span class="n">toViewModelList</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="n">List</span><span class="o">&lt;</span><span class="p">[</span><span class="n">DomainModel</span><span class="p">]</span><span class="o">&gt;</span> <span class="n">domainModels</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">  <span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">    <span class="k">return</span> <span class="n">domainModels</span><span class="p">.</span><span class="n">map</span><span class="p">((</span><span class="n">model</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="n">toViewModel</span><span class="p">(</span><span class="n">model</span><span class="p">)).</span><span class="n">toList</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h3 id="完整範例enrichmentprogressmapper">完整範例：EnrichmentProgressMapper</h3>





<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="k">import</span> <span class="s1">&#39;package:book_overview_app/domains/import/value_objects/enrichment_progress.dart&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="k">import</span> <span class="s1">&#39;package:book_overview_app/domains/library/entities/book.dart&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="k">import</span> <span class="s1">&#39;package:book_overview_app/presentation/import/enrichment_progress_viewmodel.dart&#39;</span><span class="p">;</span>
</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"><span class="c1">/// Domain EnrichmentProgress → UI ViewModel 轉換器
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1">///
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1">/// 職責：
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1">/// - 整合 EnrichmentProgress 和失敗書籍清單
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1">/// - 轉換為 UI 層需要的 ViewModel 格式
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">EnrichmentProgressMapper</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">  <span class="c1">/// 轉換 Domain 進度模型為 ViewModel
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"></span>  <span class="c1">///
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"></span>  <span class="c1">/// 參數：
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"></span>  <span class="c1">/// - [progress]: Domain 進度模型
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"></span>  <span class="c1">/// - [failedBooks]: 失敗補充的書籍清單（從 Service 取得）
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1"></span>  <span class="c1">///
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c1"></span>  <span class="c1">/// 回傳：UI 層專用的 ViewModel
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"></span>  <span class="kd">static</span> <span class="n">EnrichmentProgressViewModel</span> <span class="n">toViewModel</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="n">EnrichmentProgress</span> <span class="n">progress</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="n">List</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">&gt;</span> <span class="n">failedBooks</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">  <span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="k">return</span> <span class="n">EnrichmentProgressViewModel</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">      <span class="nl">domainProgress:</span> <span class="n">progress</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">      <span class="nl">failedBooks:</span> <span class="n">failedBooks</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">
</span></span><span class="line"><span class="ln">28</span><span class="cl">  <span class="c1">/// 批量轉換（如果需要顯示多個進度）
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="c1"></span>  <span class="kd">static</span> <span class="n">List</span><span class="o">&lt;</span><span class="n">EnrichmentProgressViewModel</span><span class="o">&gt;</span> <span class="n">toViewModelList</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">    <span class="n">List</span><span class="o">&lt;</span><span class="n">EnrichmentProgress</span><span class="o">&gt;</span> <span class="n">progressList</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">    <span class="n">Map</span><span class="o">&lt;</span><span class="kt">String</span><span class="p">,</span> <span class="n">List</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">&gt;&gt;</span> <span class="n">failedBooksMap</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">  <span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">    <span class="k">return</span> <span class="n">progressList</span><span class="p">.</span><span class="n">map</span><span class="p">((</span><span class="n">progress</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">      <span class="c1">// 假設每個 progress 有唯一 ID
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="c1"></span>      <span class="kd">final</span> <span class="n">failedBooks</span> <span class="o">=</span> <span class="n">failedBooksMap</span><span class="p">[</span><span class="n">progress</span><span class="p">.</span><span class="n">hashCode</span><span class="p">.</span><span class="n">toString</span><span class="p">()]</span> <span class="o">??</span> <span class="p">[];</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">      <span class="k">return</span> <span class="n">toViewModel</span><span class="p">(</span><span class="n">progress</span><span class="p">,</span> <span class="n">failedBooks</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">    <span class="p">}).</span><span class="n">toList</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><hr>
<h2 id="provider-整合模式">Provider 整合模式</h2>
<h3 id="streamprovider-整合">StreamProvider 整合</h3>
<p><strong>當 Domain 資料是 Stream 時使用 StreamProvider</strong>。</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">/// ViewModel StreamProvider 定義
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1">///
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1">/// 職責：
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1">/// - 整合多個 Domain Provider
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1">/// - 使用 Mapper 轉換為 ViewModel
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1">/// - 提供給 Widget 使用
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"></span><span class="kd">final</span> <span class="n">enrichmentProgressViewModelProvider</span> <span class="o">=</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="n">StreamProvider</span><span class="p">.</span><span class="n">family</span><span class="o">&lt;</span><span class="n">EnrichmentProgressViewModel</span><span class="p">,</span> <span class="kt">String</span><span class="o">&gt;</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="p">(</span><span class="n">ref</span><span class="p">,</span> <span class="n">operationId</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">      <span class="c1">// 1. 監聽 Domain Progress Stream
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"></span>      <span class="kd">final</span> <span class="n">domainProgressStream</span> <span class="o">=</span> <span class="n">ref</span><span class="p">.</span><span class="n">watch</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">        <span class="n">enrichmentProgressProvider</span><span class="p">(</span><span class="n">operationId</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">      <span class="p">);</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">      <span class="c1">// 2. 監聽失敗書籍 Stream
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1"></span>      <span class="kd">final</span> <span class="n">failedBooksStream</span> <span class="o">=</span> <span class="n">ref</span><span class="p">.</span><span class="n">watch</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">        <span class="n">failedBooksProvider</span><span class="p">(</span><span class="n">operationId</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">      <span class="p">);</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl">      <span class="c1">// 3. 合併 Stream 並轉換為 ViewModel
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="c1"></span>      <span class="k">return</span> <span class="n">Rx</span><span class="p">.</span><span class="n">combineLatest2</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">        <span class="n">domainProgressStream</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">        <span class="n">failedBooksStream</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">        <span class="p">(</span><span class="n">EnrichmentProgress</span> <span class="n">progress</span><span class="p">,</span> <span class="n">List</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">&gt;</span> <span class="n">failedBooks</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">          <span class="k">return</span> <span class="n">EnrichmentProgressMapper</span><span class="p">.</span><span class="n">toViewModel</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">            <span class="n">progress</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">            <span class="n">failedBooks</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">          <span class="p">);</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">      <span class="p">);</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">  <span class="p">);</span></span></span></code></pre></div><h3 id="stateprovider-整合">StateProvider 整合</h3>
<p><strong>當 ViewModel 需要狀態管理時使用 Notifier</strong>。</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">/// ViewModel State 定義
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">LibraryDisplayState</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">DisplayMode</span> <span class="n">displayMode</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="kd">final</span> <span class="n">List</span><span class="o">&lt;</span><span class="n">LibraryBookModel</span><span class="o">&gt;</span> <span class="n">books</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="kd">final</span> <span class="n">Set</span><span class="o">&lt;</span><span class="kt">String</span><span class="o">&gt;</span> <span class="n">selectedBookIds</span><span class="p">;</span>
</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 class="kd">const</span> <span class="n">LibraryDisplayState</span><span class="p">({</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="n">displayMode</span> <span class="o">=</span> <span class="n">DisplayMode</span><span class="p">.</span><span class="n">simple</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="n">books</span> <span class="o">=</span> <span class="kd">const</span> <span class="p">[],</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="n">selectedBookIds</span> <span class="o">=</span> <span class="kd">const</span> <span class="p">{},</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">  <span class="n">LibraryDisplayState</span> <span class="n">copyWith</span><span class="p">({</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="n">DisplayMode</span><span class="o">?</span> <span class="n">displayMode</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="n">List</span><span class="o">&lt;</span><span class="n">LibraryBookModel</span><span class="o">&gt;?</span> <span class="n">books</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="n">Set</span><span class="o">&lt;</span><span class="kt">String</span><span class="o">&gt;?</span> <span class="n">selectedBookIds</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">  <span class="p">})</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="k">return</span> <span class="n">LibraryDisplayState</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">      <span class="nl">displayMode:</span> <span class="n">displayMode</span> <span class="o">??</span> <span class="k">this</span><span class="p">.</span><span class="n">displayMode</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">      <span class="nl">books:</span> <span class="n">books</span> <span class="o">??</span> <span class="k">this</span><span class="p">.</span><span class="n">books</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">      <span class="nl">selectedBookIds:</span> <span class="n">selectedBookIds</span> <span class="o">??</span> <span class="k">this</span><span class="p">.</span><span class="n">selectedBookIds</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="c1">/// ViewModel Notifier
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">LibraryDisplayViewModel</span> <span class="kd">extends</span> <span class="n">Notifier</span><span class="o">&lt;</span><span class="n">LibraryDisplayState</span><span class="o">&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">  <span class="err">@</span><span class="n">override</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">  <span class="n">LibraryDisplayState</span> <span class="n">build</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">    <span class="k">return</span> <span class="kd">const</span> <span class="n">LibraryDisplayState</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">
</span></span><span class="line"><span class="ln">33</span><span class="cl">  <span class="c1">/// 切換顯示模式
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="c1"></span>  <span class="kt">void</span> <span class="n">toggleDisplayMode</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">    <span class="kd">final</span> <span class="n">newMode</span> <span class="o">=</span> <span class="n">state</span><span class="p">.</span><span class="n">displayMode</span> <span class="o">==</span> <span class="n">DisplayMode</span><span class="p">.</span><span class="n">simple</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">        <span class="o">?</span> <span class="n">DisplayMode</span><span class="p">.</span><span class="n">management</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">        <span class="o">:</span> <span class="n">DisplayMode</span><span class="p">.</span><span class="n">simple</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">    <span class="n">state</span> <span class="o">=</span> <span class="n">state</span><span class="p">.</span><span class="n">copyWith</span><span class="p">(</span><span class="nl">displayMode:</span> <span class="n">newMode</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl">
</span></span><span class="line"><span class="ln">41</span><span class="cl">  <span class="c1">/// 選擇書籍
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="c1"></span>  <span class="kt">void</span> <span class="n">toggleBookSelection</span><span class="p">(</span><span class="kt">String</span> <span class="n">bookId</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl">    <span class="kd">final</span> <span class="n">selectedIds</span> <span class="o">=</span> <span class="n">Set</span><span class="o">&lt;</span><span class="kt">String</span><span class="o">&gt;</span><span class="p">.</span><span class="n">from</span><span class="p">(</span><span class="n">state</span><span class="p">.</span><span class="n">selectedBookIds</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">44</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">selectedIds</span><span class="p">.</span><span class="n">contains</span><span class="p">(</span><span class="n">bookId</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl">      <span class="n">selectedIds</span><span class="p">.</span><span class="n">remove</span><span class="p">(</span><span class="n">bookId</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">46</span><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">47</span><span class="cl">      <span class="n">selectedIds</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">bookId</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl">    <span class="n">state</span> <span class="o">=</span> <span class="n">state</span><span class="p">.</span><span class="n">copyWith</span><span class="p">(</span><span class="nl">selectedBookIds:</span> <span class="n">selectedIds</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">50</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">52</span><span class="cl">
</span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="c1">/// Provider 定義
</span></span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="c1"></span><span class="kd">final</span> <span class="n">libraryDisplayViewModelProvider</span> <span class="o">=</span>
</span></span><span class="line"><span class="ln">55</span><span class="cl">  <span class="n">NotifierProvider</span><span class="o">&lt;</span><span class="n">LibraryDisplayViewModel</span><span class="p">,</span> <span class="n">LibraryDisplayState</span><span class="o">&gt;</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">56</span><span class="cl">    <span class="n">LibraryDisplayViewModel</span><span class="p">.</span><span class="k">new</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">57</span><span class="cl">  <span class="p">);</span></span></span></code></pre></div><hr>
<h2 id="widget-使用方式">Widget 使用方式</h2>
<h3 id="streamprovider-使用">StreamProvider 使用</h3>





<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="kd">class</span> <span class="nc">EnrichmentProgressWidget</span> <span class="kd">extends</span> <span class="n">ConsumerWidget</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="kt">String</span> <span class="n">operationId</span><span class="p">;</span>
</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 class="kd">const</span> <span class="n">EnrichmentProgressWidget</span><span class="p">({</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="kd">required</span> <span class="k">this</span><span class="p">.</span><span class="n">operationId</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="k">super</span><span class="p">.</span><span class="n">key</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="err">@</span><span class="n">override</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="n">Widget</span> <span class="n">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">,</span> <span class="n">WidgetRef</span> <span class="n">ref</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="kd">final</span> <span class="n">viewModelAsync</span> <span class="o">=</span> <span class="n">ref</span><span class="p">.</span><span class="n">watch</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">      <span class="n">enrichmentProgressViewModelProvider</span><span class="p">(</span><span class="n">operationId</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="k">return</span> <span class="n">viewModelAsync</span><span class="p">.</span><span class="n">when</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">      <span class="nl">data:</span> <span class="p">(</span><span class="n">viewModel</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="n">_buildProgressContent</span><span class="p">(</span><span class="n">viewModel</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">      <span class="nl">loading:</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="kd">const</span> <span class="n">CircularProgressIndicator</span><span class="p">(),</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">      <span class="nl">error:</span> <span class="p">(</span><span class="n">error</span><span class="p">,</span> <span class="n">stack</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="n">ErrorWidget</span><span class="p">(</span><span class="n">error</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl">  <span class="n">Widget</span> <span class="n">_buildProgressContent</span><span class="p">(</span><span class="n">EnrichmentProgressViewModel</span> <span class="n">vm</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">    <span class="k">return</span> <span class="n">Column</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">      <span class="nl">children:</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">        <span class="c1">// 使用 ViewModel 的 UI 屬性
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="c1"></span>        <span class="n">Icon</span><span class="p">(</span><span class="n">vm</span><span class="p">.</span><span class="n">statusIcon</span><span class="p">,</span> <span class="nl">color:</span> <span class="n">vm</span><span class="p">.</span><span class="n">progressColor</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">        <span class="n">Text</span><span class="p">(</span><span class="n">vm</span><span class="p">.</span><span class="n">displayStatus</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">        <span class="n">LinearProgressIndicator</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">          <span class="nl">value:</span> <span class="n">vm</span><span class="p">.</span><span class="n">progressPercentage</span> <span class="o">/</span> <span class="m">100</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">          <span class="nl">color:</span> <span class="n">vm</span><span class="p">.</span><span class="n">progressColor</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">        <span class="p">),</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">        <span class="n">Text</span><span class="p">(</span><span class="n">vm</span><span class="p">.</span><span class="n">summaryText</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">
</span></span><span class="line"><span class="ln">34</span><span class="cl">        <span class="c1">// 失敗清單
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="c1"></span>        <span class="k">if</span> <span class="p">(</span><span class="n">vm</span><span class="p">.</span><span class="n">showFailedBooks</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">          <span class="n">_buildFailedBooksList</span><span class="p">(</span><span class="n">vm</span><span class="p">.</span><span class="n">failedBooksSummary</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">      <span class="p">],</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h3 id="stateprovider-使用">StateProvider 使用</h3>





<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="kd">class</span> <span class="nc">LibraryDisplayPage</span> <span class="kd">extends</span> <span class="n">ConsumerWidget</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  <span class="err">@</span><span class="n">override</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="n">Widget</span> <span class="n">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">,</span> <span class="n">WidgetRef</span> <span class="n">ref</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="kd">final</span> <span class="n">state</span> <span class="o">=</span> <span class="n">ref</span><span class="p">.</span><span class="n">watch</span><span class="p">(</span><span class="n">libraryDisplayViewModelProvider</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="kd">final</span> <span class="n">viewModel</span> <span class="o">=</span> <span class="n">ref</span><span class="p">.</span><span class="n">read</span><span class="p">(</span><span class="n">libraryDisplayViewModelProvider</span><span class="p">.</span><span class="n">notifier</span><span class="p">);</span>
</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 class="k">return</span> <span class="n">Scaffold</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">      <span class="nl">appBar:</span> <span class="n">AppBar</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">        <span class="nl">title:</span> <span class="n">Text</span><span class="p">(</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="nl">actions:</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">          <span class="n">IconButton</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">            <span class="nl">icon:</span> <span class="n">Icon</span><span class="p">(</span><span class="n">Icons</span><span class="p">.</span><span class="n">view_list</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">            <span class="nl">onPressed:</span> <span class="n">viewModel</span><span class="p">.</span><span class="n">toggleDisplayMode</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">          <span class="p">),</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">        <span class="p">],</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">      <span class="p">),</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">      <span class="nl">body:</span> <span class="n">ListView</span><span class="p">.</span><span class="n">builder</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">        <span class="nl">itemCount:</span> <span class="n">state</span><span class="p">.</span><span class="n">books</span><span class="p">.</span><span class="n">length</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">        <span class="nl">itemBuilder:</span> <span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">index</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">          <span class="kd">final</span> <span class="n">book</span> <span class="o">=</span> <span class="n">state</span><span class="p">.</span><span class="n">books</span><span class="p">[</span><span class="n">index</span><span class="p">];</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">          <span class="kd">final</span> <span class="n">isSelected</span> <span class="o">=</span> <span class="n">state</span><span class="p">.</span><span class="n">selectedBookIds</span><span class="p">.</span><span class="n">contains</span><span class="p">(</span><span class="n">book</span><span class="p">.</span><span class="n">id</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl">          <span class="k">return</span> <span class="n">ListTile</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">            <span class="nl">title:</span> <span class="n">Text</span><span class="p">(</span><span class="n">book</span><span class="p">.</span><span class="n">title</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">            <span class="nl">selected:</span> <span class="n">isSelected</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">            <span class="nl">onTap:</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="n">viewModel</span><span class="p">.</span><span class="n">toggleBookSelection</span><span class="p">(</span><span class="n">book</span><span class="p">.</span><span class="n">id</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">          <span class="p">);</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">      <span class="p">),</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><hr>
<h2 id="測試要求">測試要求</h2>
<h3 id="單元測試覆蓋率">單元測試覆蓋率</h3>
<p><strong>每個 ViewModel 必須有單元測試，覆蓋率 ≥ 90%</strong>。</p>
<h3 id="測試項目">測試項目</h3>
<ol>
<li><strong>Domain → UI 轉換邏輯</strong></li>
<li><strong>UI 專用計算邏輯</strong></li>
<li><strong>狀態管理邏輯</strong>（如果是 Notifier）</li>
<li><strong>邊界條件和錯誤處理</strong></li>
</ol>
<h3 id="測試範例">測試範例</h3>





<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="k">import</span> <span class="s1">&#39;package:flutter_test/flutter_test.dart&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">  2</span><span class="cl"><span class="k">import</span> <span class="s1">&#39;package:book_overview_app/domains/import/value_objects/enrichment_progress.dart&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">  3</span><span class="cl"><span class="k">import</span> <span class="s1">&#39;package:book_overview_app/presentation/import/enrichment_progress_viewmodel.dart&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">  4</span><span class="cl"><span class="k">import</span> <span class="s1">&#39;package:book_overview_app/presentation/import/enrichment_progress_mapper.dart&#39;</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="kt">void</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">  7</span><span class="cl">  <span class="n">group</span><span class="p">(</span><span class="s1">&#39;EnrichmentProgressViewModel&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">  8</span><span class="cl">    <span class="n">group</span><span class="p">(</span><span class="s1">&#39;displayStatus&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">  9</span><span class="cl">      <span class="n">test</span><span class="p">(</span><span class="s1">&#39;準備中 - processedBooks == 0&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 10</span><span class="cl">        <span class="c1">// Arrange
</span></span></span><span class="line"><span class="ln"> 11</span><span class="cl"><span class="c1"></span>        <span class="kd">final</span> <span class="n">progress</span> <span class="o">=</span> <span class="n">EnrichmentProgress</span><span class="p">.</span><span class="n">initial</span><span class="p">(</span><span class="m">10</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 12</span><span class="cl">        <span class="kd">final</span> <span class="n">vm</span> <span class="o">=</span> <span class="n">EnrichmentProgressMapper</span><span class="p">.</span><span class="n">toViewModel</span><span class="p">(</span><span class="n">progress</span><span class="p">,</span> <span class="p">[]);</span>
</span></span><span class="line"><span class="ln"> 13</span><span class="cl">
</span></span><span class="line"><span class="ln"> 14</span><span class="cl">        <span class="c1">// Act &amp; Assert
</span></span></span><span class="line"><span class="ln"> 15</span><span class="cl"><span class="c1"></span>        <span class="n">expect</span><span class="p">(</span><span class="n">vm</span><span class="p">.</span><span class="n">displayStatus</span><span class="p">,</span> <span class="s1">&#39;準備中&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 16</span><span class="cl">      <span class="p">});</span>
</span></span><span class="line"><span class="ln"> 17</span><span class="cl">
</span></span><span class="line"><span class="ln"> 18</span><span class="cl">      <span class="n">test</span><span class="p">(</span><span class="s1">&#39;補充中 - processedBooks &gt; 0 且未完成&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 19</span><span class="cl">        <span class="c1">// Arrange
</span></span></span><span class="line"><span class="ln"> 20</span><span class="cl"><span class="c1"></span>        <span class="kd">final</span> <span class="n">progress</span> <span class="o">=</span> <span class="n">EnrichmentProgress</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 21</span><span class="cl">          <span class="nl">totalBooks:</span> <span class="m">10</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 22</span><span class="cl">          <span class="nl">processedBooks:</span> <span class="m">5</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 23</span><span class="cl">          <span class="nl">successfulEnrichments:</span> <span class="m">5</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 24</span><span class="cl">          <span class="nl">failedEnrichments:</span> <span class="m">0</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 25</span><span class="cl">        <span class="p">);</span>
</span></span><span class="line"><span class="ln"> 26</span><span class="cl">        <span class="kd">final</span> <span class="n">vm</span> <span class="o">=</span> <span class="n">EnrichmentProgressMapper</span><span class="p">.</span><span class="n">toViewModel</span><span class="p">(</span><span class="n">progress</span><span class="p">,</span> <span class="p">[]);</span>
</span></span><span class="line"><span class="ln"> 27</span><span class="cl">
</span></span><span class="line"><span class="ln"> 28</span><span class="cl">        <span class="c1">// Act &amp; Assert
</span></span></span><span class="line"><span class="ln"> 29</span><span class="cl"><span class="c1"></span>        <span class="n">expect</span><span class="p">(</span><span class="n">vm</span><span class="p">.</span><span class="n">displayStatus</span><span class="p">,</span> <span class="s1">&#39;補充中&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 30</span><span class="cl">      <span class="p">});</span>
</span></span><span class="line"><span class="ln"> 31</span><span class="cl">
</span></span><span class="line"><span class="ln"> 32</span><span class="cl">      <span class="n">test</span><span class="p">(</span><span class="s1">&#39;已完成 - processedBooks == totalBooks&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 33</span><span class="cl">        <span class="c1">// Arrange
</span></span></span><span class="line"><span class="ln"> 34</span><span class="cl"><span class="c1"></span>        <span class="kd">final</span> <span class="n">progress</span> <span class="o">=</span> <span class="n">EnrichmentProgress</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 35</span><span class="cl">          <span class="nl">totalBooks:</span> <span class="m">10</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 36</span><span class="cl">          <span class="nl">processedBooks:</span> <span class="m">10</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 37</span><span class="cl">          <span class="nl">successfulEnrichments:</span> <span class="m">10</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 38</span><span class="cl">          <span class="nl">failedEnrichments:</span> <span class="m">0</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 39</span><span class="cl">        <span class="p">);</span>
</span></span><span class="line"><span class="ln"> 40</span><span class="cl">        <span class="kd">final</span> <span class="n">vm</span> <span class="o">=</span> <span class="n">EnrichmentProgressMapper</span><span class="p">.</span><span class="n">toViewModel</span><span class="p">(</span><span class="n">progress</span><span class="p">,</span> <span class="p">[]);</span>
</span></span><span class="line"><span class="ln"> 41</span><span class="cl">
</span></span><span class="line"><span class="ln"> 42</span><span class="cl">        <span class="c1">// Act &amp; Assert
</span></span></span><span class="line"><span class="ln"> 43</span><span class="cl"><span class="c1"></span>        <span class="n">expect</span><span class="p">(</span><span class="n">vm</span><span class="p">.</span><span class="n">displayStatus</span><span class="p">,</span> <span class="s1">&#39;已完成&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 44</span><span class="cl">      <span class="p">});</span>
</span></span><span class="line"><span class="ln"> 45</span><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="ln"> 46</span><span class="cl">
</span></span><span class="line"><span class="ln"> 47</span><span class="cl">    <span class="n">group</span><span class="p">(</span><span class="s1">&#39;statusIcon&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 48</span><span class="cl">      <span class="n">test</span><span class="p">(</span><span class="s1">&#39;準備中 - Icons.pending&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 49</span><span class="cl">        <span class="kd">final</span> <span class="n">progress</span> <span class="o">=</span> <span class="n">EnrichmentProgress</span><span class="p">.</span><span class="n">initial</span><span class="p">(</span><span class="m">10</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 50</span><span class="cl">        <span class="kd">final</span> <span class="n">vm</span> <span class="o">=</span> <span class="n">EnrichmentProgressMapper</span><span class="p">.</span><span class="n">toViewModel</span><span class="p">(</span><span class="n">progress</span><span class="p">,</span> <span class="p">[]);</span>
</span></span><span class="line"><span class="ln"> 51</span><span class="cl">
</span></span><span class="line"><span class="ln"> 52</span><span class="cl">        <span class="n">expect</span><span class="p">(</span><span class="n">vm</span><span class="p">.</span><span class="n">statusIcon</span><span class="p">,</span> <span class="n">Icons</span><span class="p">.</span><span class="n">pending</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 53</span><span class="cl">      <span class="p">});</span>
</span></span><span class="line"><span class="ln"> 54</span><span class="cl">
</span></span><span class="line"><span class="ln"> 55</span><span class="cl">      <span class="n">test</span><span class="p">(</span><span class="s1">&#39;補充中 - Icons.sync&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 56</span><span class="cl">        <span class="kd">final</span> <span class="n">progress</span> <span class="o">=</span> <span class="n">EnrichmentProgress</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 57</span><span class="cl">          <span class="nl">totalBooks:</span> <span class="m">10</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 58</span><span class="cl">          <span class="nl">processedBooks:</span> <span class="m">5</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 59</span><span class="cl">          <span class="nl">successfulEnrichments:</span> <span class="m">5</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 60</span><span class="cl">          <span class="nl">failedEnrichments:</span> <span class="m">0</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 61</span><span class="cl">        <span class="p">);</span>
</span></span><span class="line"><span class="ln"> 62</span><span class="cl">        <span class="kd">final</span> <span class="n">vm</span> <span class="o">=</span> <span class="n">EnrichmentProgressMapper</span><span class="p">.</span><span class="n">toViewModel</span><span class="p">(</span><span class="n">progress</span><span class="p">,</span> <span class="p">[]);</span>
</span></span><span class="line"><span class="ln"> 63</span><span class="cl">
</span></span><span class="line"><span class="ln"> 64</span><span class="cl">        <span class="n">expect</span><span class="p">(</span><span class="n">vm</span><span class="p">.</span><span class="n">statusIcon</span><span class="p">,</span> <span class="n">Icons</span><span class="p">.</span><span class="kd">sync</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 65</span><span class="cl">      <span class="p">});</span>
</span></span><span class="line"><span class="ln"> 66</span><span class="cl">
</span></span><span class="line"><span class="ln"> 67</span><span class="cl">      <span class="n">test</span><span class="p">(</span><span class="s1">&#39;已完成 - Icons.check_circle&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 68</span><span class="cl">        <span class="kd">final</span> <span class="n">progress</span> <span class="o">=</span> <span class="n">EnrichmentProgress</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 69</span><span class="cl">          <span class="nl">totalBooks:</span> <span class="m">10</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 70</span><span class="cl">          <span class="nl">processedBooks:</span> <span class="m">10</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 71</span><span class="cl">          <span class="nl">successfulEnrichments:</span> <span class="m">10</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 72</span><span class="cl">          <span class="nl">failedEnrichments:</span> <span class="m">0</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 73</span><span class="cl">        <span class="p">);</span>
</span></span><span class="line"><span class="ln"> 74</span><span class="cl">        <span class="kd">final</span> <span class="n">vm</span> <span class="o">=</span> <span class="n">EnrichmentProgressMapper</span><span class="p">.</span><span class="n">toViewModel</span><span class="p">(</span><span class="n">progress</span><span class="p">,</span> <span class="p">[]);</span>
</span></span><span class="line"><span class="ln"> 75</span><span class="cl">
</span></span><span class="line"><span class="ln"> 76</span><span class="cl">        <span class="n">expect</span><span class="p">(</span><span class="n">vm</span><span class="p">.</span><span class="n">statusIcon</span><span class="p">,</span> <span class="n">Icons</span><span class="p">.</span><span class="n">check_circle</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 77</span><span class="cl">      <span class="p">});</span>
</span></span><span class="line"><span class="ln"> 78</span><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="ln"> 79</span><span class="cl">
</span></span><span class="line"><span class="ln"> 80</span><span class="cl">    <span class="n">group</span><span class="p">(</span><span class="s1">&#39;progressColor&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 81</span><span class="cl">      <span class="n">test</span><span class="p">(</span><span class="s1">&#39;有失敗 - Colors.orange&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 82</span><span class="cl">        <span class="kd">final</span> <span class="n">progress</span> <span class="o">=</span> <span class="n">EnrichmentProgress</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 83</span><span class="cl">          <span class="nl">totalBooks:</span> <span class="m">10</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 84</span><span class="cl">          <span class="nl">processedBooks:</span> <span class="m">10</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 85</span><span class="cl">          <span class="nl">successfulEnrichments:</span> <span class="m">8</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 86</span><span class="cl">          <span class="nl">failedEnrichments:</span> <span class="m">2</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 87</span><span class="cl">        <span class="p">);</span>
</span></span><span class="line"><span class="ln"> 88</span><span class="cl">        <span class="kd">final</span> <span class="n">vm</span> <span class="o">=</span> <span class="n">EnrichmentProgressMapper</span><span class="p">.</span><span class="n">toViewModel</span><span class="p">(</span><span class="n">progress</span><span class="p">,</span> <span class="p">[]);</span>
</span></span><span class="line"><span class="ln"> 89</span><span class="cl">
</span></span><span class="line"><span class="ln"> 90</span><span class="cl">        <span class="n">expect</span><span class="p">(</span><span class="n">vm</span><span class="p">.</span><span class="n">progressColor</span><span class="p">,</span> <span class="n">Colors</span><span class="p">.</span><span class="n">orange</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 91</span><span class="cl">      <span class="p">});</span>
</span></span><span class="line"><span class="ln"> 92</span><span class="cl">
</span></span><span class="line"><span class="ln"> 93</span><span class="cl">      <span class="n">test</span><span class="p">(</span><span class="s1">&#39;已完成無失敗 - Colors.green&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 94</span><span class="cl">        <span class="kd">final</span> <span class="n">progress</span> <span class="o">=</span> <span class="n">EnrichmentProgress</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 95</span><span class="cl">          <span class="nl">totalBooks:</span> <span class="m">10</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 96</span><span class="cl">          <span class="nl">processedBooks:</span> <span class="m">10</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 97</span><span class="cl">          <span class="nl">successfulEnrichments:</span> <span class="m">10</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 98</span><span class="cl">          <span class="nl">failedEnrichments:</span> <span class="m">0</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 99</span><span class="cl">        <span class="p">);</span>
</span></span><span class="line"><span class="ln">100</span><span class="cl">        <span class="kd">final</span> <span class="n">vm</span> <span class="o">=</span> <span class="n">EnrichmentProgressMapper</span><span class="p">.</span><span class="n">toViewModel</span><span class="p">(</span><span class="n">progress</span><span class="p">,</span> <span class="p">[]);</span>
</span></span><span class="line"><span class="ln">101</span><span class="cl">
</span></span><span class="line"><span class="ln">102</span><span class="cl">        <span class="n">expect</span><span class="p">(</span><span class="n">vm</span><span class="p">.</span><span class="n">progressColor</span><span class="p">,</span> <span class="n">Colors</span><span class="p">.</span><span class="n">green</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">103</span><span class="cl">      <span class="p">});</span>
</span></span><span class="line"><span class="ln">104</span><span class="cl">
</span></span><span class="line"><span class="ln">105</span><span class="cl">      <span class="n">test</span><span class="p">(</span><span class="s1">&#39;進行中 - Colors.blue&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">106</span><span class="cl">        <span class="kd">final</span> <span class="n">progress</span> <span class="o">=</span> <span class="n">EnrichmentProgress</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">107</span><span class="cl">          <span class="nl">totalBooks:</span> <span class="m">10</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">108</span><span class="cl">          <span class="nl">processedBooks:</span> <span class="m">5</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">109</span><span class="cl">          <span class="nl">successfulEnrichments:</span> <span class="m">5</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">110</span><span class="cl">          <span class="nl">failedEnrichments:</span> <span class="m">0</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">111</span><span class="cl">        <span class="p">);</span>
</span></span><span class="line"><span class="ln">112</span><span class="cl">        <span class="kd">final</span> <span class="n">vm</span> <span class="o">=</span> <span class="n">EnrichmentProgressMapper</span><span class="p">.</span><span class="n">toViewModel</span><span class="p">(</span><span class="n">progress</span><span class="p">,</span> <span class="p">[]);</span>
</span></span><span class="line"><span class="ln">113</span><span class="cl">
</span></span><span class="line"><span class="ln">114</span><span class="cl">        <span class="n">expect</span><span class="p">(</span><span class="n">vm</span><span class="p">.</span><span class="n">progressColor</span><span class="p">,</span> <span class="n">Colors</span><span class="p">.</span><span class="n">blue</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">115</span><span class="cl">      <span class="p">});</span>
</span></span><span class="line"><span class="ln">116</span><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="ln">117</span><span class="cl">
</span></span><span class="line"><span class="ln">118</span><span class="cl">    <span class="n">group</span><span class="p">(</span><span class="s1">&#39;summaryText&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">119</span><span class="cl">      <span class="n">test</span><span class="p">(</span><span class="s1">&#39;無失敗 - 顯示處理進度&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">120</span><span class="cl">        <span class="kd">final</span> <span class="n">progress</span> <span class="o">=</span> <span class="n">EnrichmentProgress</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">121</span><span class="cl">          <span class="nl">totalBooks:</span> <span class="m">10</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">122</span><span class="cl">          <span class="nl">processedBooks:</span> <span class="m">5</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">123</span><span class="cl">          <span class="nl">successfulEnrichments:</span> <span class="m">5</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">124</span><span class="cl">          <span class="nl">failedEnrichments:</span> <span class="m">0</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">125</span><span class="cl">        <span class="p">);</span>
</span></span><span class="line"><span class="ln">126</span><span class="cl">        <span class="kd">final</span> <span class="n">vm</span> <span class="o">=</span> <span class="n">EnrichmentProgressMapper</span><span class="p">.</span><span class="n">toViewModel</span><span class="p">(</span><span class="n">progress</span><span class="p">,</span> <span class="p">[]);</span>
</span></span><span class="line"><span class="ln">127</span><span class="cl">
</span></span><span class="line"><span class="ln">128</span><span class="cl">        <span class="n">expect</span><span class="p">(</span><span class="n">vm</span><span class="p">.</span><span class="n">summaryText</span><span class="p">,</span> <span class="s1">&#39;已處理 5/10 本&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">129</span><span class="cl">      <span class="p">});</span>
</span></span><span class="line"><span class="ln">130</span><span class="cl">
</span></span><span class="line"><span class="ln">131</span><span class="cl">      <span class="n">test</span><span class="p">(</span><span class="s1">&#39;有失敗 - 顯示成功和失敗數&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">132</span><span class="cl">        <span class="kd">final</span> <span class="n">progress</span> <span class="o">=</span> <span class="n">EnrichmentProgress</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">133</span><span class="cl">          <span class="nl">totalBooks:</span> <span class="m">10</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">134</span><span class="cl">          <span class="nl">processedBooks:</span> <span class="m">10</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">135</span><span class="cl">          <span class="nl">successfulEnrichments:</span> <span class="m">8</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">136</span><span class="cl">          <span class="nl">failedEnrichments:</span> <span class="m">2</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">137</span><span class="cl">        <span class="p">);</span>
</span></span><span class="line"><span class="ln">138</span><span class="cl">        <span class="kd">final</span> <span class="n">vm</span> <span class="o">=</span> <span class="n">EnrichmentProgressMapper</span><span class="p">.</span><span class="n">toViewModel</span><span class="p">(</span><span class="n">progress</span><span class="p">,</span> <span class="p">[]);</span>
</span></span><span class="line"><span class="ln">139</span><span class="cl">
</span></span><span class="line"><span class="ln">140</span><span class="cl">        <span class="n">expect</span><span class="p">(</span><span class="n">vm</span><span class="p">.</span><span class="n">summaryText</span><span class="p">,</span> <span class="s1">&#39;已處理 10/10 本（成功 8，失敗 2）&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">141</span><span class="cl">      <span class="p">});</span>
</span></span><span class="line"><span class="ln">142</span><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="ln">143</span><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="ln">144</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><hr>
<h2 id="viewmodel-開發檢查清單">ViewModel 開發檢查清單</h2>
<h3 id="phase-1-設計階段">Phase 1: 設計階段</h3>
<ul>
<li><input disabled="" type="checkbox"> 確認 Domain 模型已完成</li>
<li><input disabled="" type="checkbox"> 定義 ViewModel 需要的 UI 屬性</li>
<li><input disabled="" type="checkbox"> 設計 Domain → UI 轉換邏輯</li>
<li><input disabled="" type="checkbox"> 規劃 Mapper 結構</li>
<li><input disabled="" type="checkbox"> 定義 Provider 整合方式</li>
</ul>
<h3 id="phase-2-實作階段">Phase 2: 實作階段</h3>
<ul>
<li><input disabled="" type="checkbox"> 建立 ViewModel 類別和欄位</li>
<li><input disabled="" type="checkbox"> 實作 UI 專用計算屬性</li>
<li><input disabled="" type="checkbox"> 實作 Mapper 轉換方法</li>
<li><input disabled="" type="checkbox"> 定義 Provider</li>
<li><input disabled="" type="checkbox"> 撰寫完整註解（包含需求編號）</li>
</ul>
<h3 id="phase-3-測試階段">Phase 3: 測試階段</h3>
<ul>
<li><input disabled="" type="checkbox"> 撰寫 ViewModel 單元測試</li>
<li><input disabled="" type="checkbox"> 測試所有計算屬性</li>
<li><input disabled="" type="checkbox"> 測試 Mapper 轉換邏輯</li>
<li><input disabled="" type="checkbox"> 測試邊界條件</li>
<li><input disabled="" type="checkbox"> 達成 90% 以上覆蓋率</li>
</ul>
<h3 id="phase-4-整合階段">Phase 4: 整合階段</h3>
<ul>
<li><input disabled="" type="checkbox"> Widget 整合 ViewModel Provider</li>
<li><input disabled="" type="checkbox"> 驗證 UI 正確顯示</li>
<li><input disabled="" type="checkbox"> 執行 Widget 測試</li>
<li><input disabled="" type="checkbox"> Code Review 確認符合規範</li>
</ul>
<hr>
<h2 id="常見問題和最佳實踐">常見問題和最佳實踐</h2>
<h3 id="q1-viewmodel-可以包含-statefulwidget-的狀態嗎">Q1: ViewModel 可以包含 StatefulWidget 的狀態嗎？</h3>
<p><strong>A</strong>: 不可以。ViewModel 應該是純資料模型，不包含 Widget 生命週期邏輯。</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="kd">class</span> <span class="nc">MyViewModel</span> <span class="kd">extends</span> <span class="n">StatefulWidget</span> <span class="p">{</span> <span class="p">}</span>
</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 class="c1">// 正例
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">MyViewModel</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">  <span class="kd">final</span> <span class="n">MyDomainModel</span> <span class="n">domainModel</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl">  <span class="c1">// 純資料模型
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><h3 id="q2-如何處理複雜的-ui-狀態">Q2: 如何處理複雜的 UI 狀態？</h3>
<p><strong>A</strong>: 使用 Notifier 管理狀態，定義 State 類別。</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="kd">class</span> <span class="nc">MyState</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">DisplayMode</span> <span class="n">mode</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="kd">final</span> <span class="n">List</span><span class="o">&lt;</span><span class="n">Item</span><span class="o">&gt;</span> <span class="n">items</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="kd">final</span> <span class="n">Set</span><span class="o">&lt;</span><span class="kt">String</span><span class="o">&gt;</span> <span class="n">selectedIds</span><span class="p">;</span>
</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 class="n">MyState</span> <span class="n">copyWith</span><span class="p">({...})</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="kd">class</span> <span class="nc">MyViewModel</span> <span class="kd">extends</span> <span class="n">Notifier</span><span class="o">&lt;</span><span class="n">MyState</span><span class="o">&gt;</span> <span class="p">{</span> <span class="p">}</span></span></span></code></pre></div><h3 id="q3-viewmodel-可以呼叫-domain-service-嗎">Q3: ViewModel 可以呼叫 Domain Service 嗎？</h3>
<p><strong>A</strong>: 可以，但建議透過 Provider 整合而非直接呼叫。</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">// 推薦：透過 Provider 整合
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="kd">final</span> <span class="n">myViewModelProvider</span> <span class="o">=</span> <span class="n">Provider</span><span class="p">((</span><span class="n">ref</span><span class="p">)</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">domainData</span> <span class="o">=</span> <span class="n">ref</span><span class="p">.</span><span class="n">watch</span><span class="p">(</span><span class="n">domainServiceProvider</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="k">return</span> <span class="n">MyMapper</span><span class="p">.</span><span class="n">toViewModel</span><span class="p">(</span><span class="n">domainData</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="p">});</span>
</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 class="c1">// 可接受但不推薦：直接呼叫
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">MyViewModel</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="kd">final</span> <span class="n">MyDomainService</span> <span class="n">service</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">fetchData</span><span class="p">()</span> <span class="kd">async</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="kd">final</span> <span class="n">data</span> <span class="o">=</span> <span class="kd">await</span> <span class="n">service</span><span class="p">.</span><span class="n">getData</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h3 id="q4-多個-domain-模型如何整合到一個-viewmodel">Q4: 多個 Domain 模型如何整合到一個 ViewModel？</h3>
<p><strong>A</strong>: 在 Mapper 中整合多個來源。</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="kd">class</span> <span class="nc">MyMapper</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  <span class="kd">static</span> <span class="n">MyViewModel</span> <span class="n">toViewModel</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="n">DomainModel1</span> <span class="n">model1</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="n">DomainModel2</span> <span class="n">model2</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="n">List</span><span class="o">&lt;</span><span class="n">Entity</span><span class="o">&gt;</span> <span class="n">entities</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  <span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="k">return</span> <span class="n">MyViewModel</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">      <span class="nl">field1:</span> <span class="n">model1</span><span class="p">.</span><span class="n">value</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">      <span class="nl">field2:</span> <span class="n">model2</span><span class="p">.</span><span class="n">value</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">      <span class="nl">items:</span> <span class="n">entities</span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="n">_mapEntity</span><span class="p">).</span><span class="n">toList</span><span class="p">(),</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="p">}</span></span></span></code></pre></div>]]></content:encoded></item><item><title>Ticket 拆分標準方法論</title><link>https://tarrragon.github.io/blog/record/ticket-%E6%8B%86%E5%88%86%E6%A8%99%E6%BA%96%E6%96%B9%E6%B3%95%E8%AB%96/</link><pubDate>Mon, 13 Oct 2025 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/ticket-%E6%8B%86%E5%88%86%E6%A8%99%E6%BA%96%E6%96%B9%E6%B3%95%E8%AB%96/</guid><description>&lt;h2 id="方法論概述">方法論概述&lt;/h2>
&lt;h3 id="為什麼需要-ticket-拆分標準">為什麼需要 Ticket 拆分標準&lt;/h3>
&lt;p>&lt;strong>問題背景&lt;/strong>:&lt;/p>
&lt;p>在大型軟體開發專案中，我們經常面臨以下挑戰：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>God Ticket 問題&lt;/strong>：單一任務過於龐大，包含數十個檔案修改，跨越多個架構層級&lt;/li>
&lt;li>&lt;strong>主觀判斷困境&lt;/strong>：不同開發者對「任務大小」的理解不同，導致協作效率低&lt;/li>
&lt;li>&lt;strong>進度追蹤困難&lt;/strong>：任務粒度不一致，難以準確評估完成度&lt;/li>
&lt;li>&lt;strong>風險控制失衡&lt;/strong>：大任務失敗影響大，小任務過碎增加管理成本&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>傳統拆分方法的問題&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>經驗導向&lt;/strong>：依賴個人經驗判斷，缺乏客觀標準&lt;/li>
&lt;li>&lt;strong>時間估算&lt;/strong>：受個人能力和環境影響，難以標準化&lt;/li>
&lt;li>&lt;strong>模糊描述&lt;/strong>：「不要太大」「保持適中」等描述無法執行&lt;/li>
&lt;li>&lt;strong>後驗調整&lt;/strong>：任務執行後才發現過大，增加返工成本&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="核心目標">核心目標&lt;/h3>
&lt;ol>
&lt;li>&lt;strong>標準化任務拆分&lt;/strong>：提供統一的拆分標準，消除主觀判斷&lt;/li>
&lt;li>&lt;strong>控制任務複雜度&lt;/strong>：確保每個 Ticket 在可控範圍內&lt;/li>
&lt;li>&lt;strong>提升協作效率&lt;/strong>：明確的任務邊界，減少溝通成本&lt;/li>
&lt;li>&lt;strong>及早風險管控&lt;/strong>：設計階段就識別過大任務，降低執行風險&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h3 id="與其他方法論的關係">與其他方法論的關係&lt;/h3>
&lt;p>&lt;strong>本方法論的定位&lt;/strong>:&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">Ticket 設計派工方法論 (主方法論)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">├── Ticket 拆分標準方法論 ← 你正在閱讀
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">│ └── 提供量化拆分標準和決策樹
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">├── Code Smell 品質閘門檢測方法論
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">│ └── 提供設計階段的品質檢測
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">├── Ticket 生命週期管理方法論
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">│ └── 提供 Ticket 執行流程管理
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">└── 即時 Review 機制方法論
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">9&lt;/span>&lt;span class="cl"> └── 提供執行過程中的 Review 機制&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h2 id="第一章量化指標體系">第一章：量化指標體系&lt;/h2>
&lt;h3 id="11-指標-1職責數量responsibilities-最優先">1.1 指標 1：職責數量（Responsibilities） 最優先&lt;/h3>
&lt;p>&lt;strong>定義&lt;/strong>：Ticket 需要完成的獨立職責數量。&lt;/p>
&lt;p>&lt;strong>為什麼職責是第一指標&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>最客觀&lt;/strong>：不受個人能力影響&lt;/li>
&lt;li>&lt;strong>最穩定&lt;/strong>：不受環境和參考資料影響&lt;/li>
&lt;li>&lt;strong>最易溝通&lt;/strong>：PM 和工程師都能理解&lt;/li>
&lt;li>&lt;strong>最精確&lt;/strong>：直接對應業務需求&lt;/li>
&lt;/ul>
&lt;h4 id="職責的精確定義">職責的精確定義&lt;/h4>
&lt;p>&lt;strong>什麼算一個職責&lt;/strong>：&lt;/p>
&lt;p>一個職責 = 一個明確可驗證的功能點或邊界條件&lt;/p>
&lt;p>&lt;strong>識別方式&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>每個「需要實作的功能點」算一個職責&lt;/li>
&lt;li>每個「需要驗證的邊界條件」算一個職責&lt;/li>
&lt;li>每個「需要處理的錯誤情境」算一個職責&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>範例說明&lt;/strong>：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">範例任務：實作書籍評分功能
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">職責識別：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="k">1.&lt;/span> 定義 Rating Value Object（數值範圍驗證） ← 職責 1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="k">2.&lt;/span> 定義 Rating Entity（包含評分和評論） ← 職責 2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="k">3.&lt;/span> 實作 IRatingRepository 介面 ← 職責 3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="k">4.&lt;/span> 實作評分儲存邏輯 ← 職責 4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="k">5.&lt;/span> 處理無效評分錯誤 ← 職責 5
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="k">6.&lt;/span> 處理資料庫錯誤 ← 職責 6
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">總計：6 個職責 → 超過 5 個，必須拆分&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="職責數量標準">職責數量標準&lt;/h4>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>等級&lt;/th>
 &lt;th>職責數量&lt;/th>
 &lt;th>判定&lt;/th>
 &lt;th>說明&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;strong>簡單 Ticket&lt;/strong>&lt;/td>
 &lt;td>1 個&lt;/td>
 &lt;td>理想&lt;/td>
 &lt;td>單一職責，最易管理&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>中等 Ticket&lt;/strong>&lt;/td>
 &lt;td>2-3 個&lt;/td>
 &lt;td>可接受&lt;/td>
 &lt;td>少數相關職責，可控範圍&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>複雜 Ticket&lt;/strong>&lt;/td>
 &lt;td>3-5 個&lt;/td>
 &lt;td>需檢查&lt;/td>
 &lt;td>多職責，建議拆分&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>必須拆分&lt;/strong>&lt;/td>
 &lt;td>&amp;gt; 5 個&lt;/td>
 &lt;td>禁止&lt;/td>
 &lt;td>範圍失控，必須拆分&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>強制規則&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>超過 5 個職責 = 必須拆分&lt;/strong>，無例外&lt;/li>
&lt;li>&lt;strong>3-5 個職責 = 評估是否可拆分&lt;/strong>，優先拆分&lt;/li>
&lt;li>&lt;strong>1-2 個職責 = 理想狀態&lt;/strong>，鼓勵維持&lt;/li>
&lt;/ul>
&lt;h4 id="職責識別實例">職責識別實例&lt;/h4>
&lt;h5 id="實例-1簡單-ticket1-職責">實例 1：簡單 Ticket（1 職責）&lt;/h5>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">任務：定義 SelectionManager 介面方法簽名
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">職責分析：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">職責 1：定義 toggleSelection、clearSelection、getSelectedIds 三個方法簽名
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">總計：1 個職責
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">判定：簡單 Ticket&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h6 id="實例-2中等-ticket2-3-職責">實例 2：中等 Ticket（2-3 職責）&lt;/h6>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">任務：實作 SelectionManager 基礎功能
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">職責分析：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">職責 1：實作 toggleSelection 方法
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">職責 2：實作 clearSelection 方法
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">職責 3：實作 ChangeNotifier 通知機制
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">總計：3 個職責
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">9&lt;/span>&lt;span class="cl">判定：中等 Ticket（可接受）&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h6 id="實例-3複雜-ticket3-5-職責--建議拆分">實例 3：複雜 Ticket（3-5 職責）- 建議拆分&lt;/h6>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">任務：實作完整的 BookRepository
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">職責分析：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">職責 1：實作 getBookByIsbn CRUD 方法
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">職責 2：實作 saveBook CRUD 方法
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">職責 3：實作 Data Mapper 轉換
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">職責 4：實作錯誤處理
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">職責 5：實作 Cache 管理
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">總計：5 個職責
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">判定：複雜 Ticket（建議拆分為 2-3 個 Ticket）&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h6 id="實例-4必須拆分-5-職責">實例 4：必須拆分（&amp;gt; 5 職責）&lt;/h6>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">任務：實作書籍評分完整功能（包含 UI、UseCase、Repository）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">職責分析：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">職責 1：定義 Rating Value Object
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">職責 2：定義 Rating Entity
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">職責 3：建立 RatingWidget UI
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">職責 4：實作 RateBookUseCase
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">職責 5：定義 IRatingRepository
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">職責 6：實作 SQLiteRatingRepository
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">職責 7：處理各種異常情境
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">職責 8：撰寫完整測試
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">總計：8 個職責
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">判定：God Ticket 必須拆分&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>拆分建議&lt;/strong>：&lt;/p></description><content:encoded><![CDATA[<h2 id="方法論概述">方法論概述</h2>
<h3 id="為什麼需要-ticket-拆分標準">為什麼需要 Ticket 拆分標準</h3>
<p><strong>問題背景</strong>:</p>
<p>在大型軟體開發專案中，我們經常面臨以下挑戰：</p>
<ol>
<li><strong>God Ticket 問題</strong>：單一任務過於龐大，包含數十個檔案修改，跨越多個架構層級</li>
<li><strong>主觀判斷困境</strong>：不同開發者對「任務大小」的理解不同，導致協作效率低</li>
<li><strong>進度追蹤困難</strong>：任務粒度不一致，難以準確評估完成度</li>
<li><strong>風險控制失衡</strong>：大任務失敗影響大，小任務過碎增加管理成本</li>
</ol>
<p><strong>傳統拆分方法的問題</strong>:</p>
<ul>
<li><strong>經驗導向</strong>：依賴個人經驗判斷，缺乏客觀標準</li>
<li><strong>時間估算</strong>：受個人能力和環境影響，難以標準化</li>
<li><strong>模糊描述</strong>：「不要太大」「保持適中」等描述無法執行</li>
<li><strong>後驗調整</strong>：任務執行後才發現過大，增加返工成本</li>
</ul>
<hr>
<h3 id="核心目標">核心目標</h3>
<ol>
<li><strong>標準化任務拆分</strong>：提供統一的拆分標準，消除主觀判斷</li>
<li><strong>控制任務複雜度</strong>：確保每個 Ticket 在可控範圍內</li>
<li><strong>提升協作效率</strong>：明確的任務邊界，減少溝通成本</li>
<li><strong>及早風險管控</strong>：設計階段就識別過大任務，降低執行風險</li>
</ol>
<hr>
<h3 id="與其他方法論的關係">與其他方法論的關係</h3>
<p><strong>本方法論的定位</strong>:</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">Ticket 設計派工方法論 (主方法論)
</span></span><span class="line"><span class="ln">2</span><span class="cl">├── Ticket 拆分標準方法論 ← 你正在閱讀
</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">├── Code Smell 品質閘門檢測方法論
</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">├── Ticket 生命週期管理方法論
</span></span><span class="line"><span class="ln">7</span><span class="cl">│   └── 提供 Ticket 執行流程管理
</span></span><span class="line"><span class="ln">8</span><span class="cl">└── 即時 Review 機制方法論
</span></span><span class="line"><span class="ln">9</span><span class="cl">    └── 提供執行過程中的 Review 機制</span></span></code></pre></div><hr>
<h2 id="第一章量化指標體系">第一章：量化指標體系</h2>
<h3 id="11-指標-1職責數量responsibilities-最優先">1.1 指標 1：職責數量（Responsibilities） 最優先</h3>
<p><strong>定義</strong>：Ticket 需要完成的獨立職責數量。</p>
<p><strong>為什麼職責是第一指標</strong>：</p>
<ul>
<li><strong>最客觀</strong>：不受個人能力影響</li>
<li><strong>最穩定</strong>：不受環境和參考資料影響</li>
<li><strong>最易溝通</strong>：PM 和工程師都能理解</li>
<li><strong>最精確</strong>：直接對應業務需求</li>
</ul>
<h4 id="職責的精確定義">職責的精確定義</h4>
<p><strong>什麼算一個職責</strong>：</p>
<p>一個職責 = 一個明確可驗證的功能點或邊界條件</p>
<p><strong>識別方式</strong>：</p>
<ul>
<li>每個「需要實作的功能點」算一個職責</li>
<li>每個「需要驗證的邊界條件」算一個職責</li>
<li>每個「需要處理的錯誤情境」算一個職責</li>
</ul>
<p><strong>範例說明</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">範例任務：實作書籍評分功能
</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 class="k">1.</span> 定義 Rating Value Object（數值範圍驗證）      ← 職責 1
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">2.</span> 定義 Rating Entity（包含評分和評論）         ← 職責 2
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">3.</span> 實作 IRatingRepository 介面                  ← 職責 3
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">4.</span> 實作評分儲存邏輯                              ← 職責 4
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">5.</span> 處理無效評分錯誤                              ← 職責 5
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">6.</span> 處理資料庫錯誤                                ← 職責 6
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">總計：6 個職責 → 超過 5 個，必須拆分</span></span></code></pre></div><h4 id="職責數量標準">職責數量標準</h4>
<table>
  <thead>
      <tr>
          <th>等級</th>
          <th>職責數量</th>
          <th>判定</th>
          <th>說明</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>簡單 Ticket</strong></td>
          <td>1 個</td>
          <td>理想</td>
          <td>單一職責，最易管理</td>
      </tr>
      <tr>
          <td><strong>中等 Ticket</strong></td>
          <td>2-3 個</td>
          <td>可接受</td>
          <td>少數相關職責，可控範圍</td>
      </tr>
      <tr>
          <td><strong>複雜 Ticket</strong></td>
          <td>3-5 個</td>
          <td>需檢查</td>
          <td>多職責，建議拆分</td>
      </tr>
      <tr>
          <td><strong>必須拆分</strong></td>
          <td>&gt; 5 個</td>
          <td>禁止</td>
          <td>範圍失控，必須拆分</td>
      </tr>
  </tbody>
</table>
<p><strong>強制規則</strong>：</p>
<ul>
<li><strong>超過 5 個職責 = 必須拆分</strong>，無例外</li>
<li><strong>3-5 個職責 = 評估是否可拆分</strong>，優先拆分</li>
<li><strong>1-2 個職責 = 理想狀態</strong>，鼓勵維持</li>
</ul>
<h4 id="職責識別實例">職責識別實例</h4>
<h5 id="實例-1簡單-ticket1-職責">實例 1：簡單 Ticket（1 職責）</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">任務：定義 SelectionManager 介面方法簽名
</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">職責 1：定義 toggleSelection、clearSelection、getSelectedIds 三個方法簽名
</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">總計：1 個職責
</span></span><span class="line"><span class="ln">7</span><span class="cl">判定：簡單 Ticket</span></span></code></pre></div><h6 id="實例-2中等-ticket2-3-職責">實例 2：中等 Ticket（2-3 職責）</h6>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">任務：實作 SelectionManager 基礎功能
</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">職責 1：實作 toggleSelection 方法
</span></span><span class="line"><span class="ln">5</span><span class="cl">職責 2：實作 clearSelection 方法
</span></span><span class="line"><span class="ln">6</span><span class="cl">職責 3：實作 ChangeNotifier 通知機制
</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">總計：3 個職責
</span></span><span class="line"><span class="ln">9</span><span class="cl">判定：中等 Ticket（可接受）</span></span></code></pre></div><h6 id="實例-3複雜-ticket3-5-職責--建議拆分">實例 3：複雜 Ticket（3-5 職責）- 建議拆分</h6>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">任務：實作完整的 BookRepository
</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">職責 1：實作 getBookByIsbn CRUD 方法
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">職責 2：實作 saveBook CRUD 方法
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">職責 3：實作 Data Mapper 轉換
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">職責 4：實作錯誤處理
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">職責 5：實作 Cache 管理
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">總計：5 個職責
</span></span><span class="line"><span class="ln">11</span><span class="cl">判定：複雜 Ticket（建議拆分為 2-3 個 Ticket）</span></span></code></pre></div><h6 id="實例-4必須拆分-5-職責">實例 4：必須拆分（&gt; 5 職責）</h6>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">任務：實作書籍評分完整功能（包含 UI、UseCase、Repository）
</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">職責 1：定義 Rating Value Object
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">職責 2：定義 Rating Entity
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">職責 3：建立 RatingWidget UI
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">職責 4：實作 RateBookUseCase
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">職責 5：定義 IRatingRepository
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">職責 6：實作 SQLiteRatingRepository
</span></span><span class="line"><span class="ln">10</span><span class="cl">職責 7：處理各種異常情境
</span></span><span class="line"><span class="ln">11</span><span class="cl">職責 8：撰寫完整測試
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">總計：8 個職責
</span></span><span class="line"><span class="ln">14</span><span class="cl">判定：God Ticket 必須拆分</span></span></code></pre></div><p><strong>拆分建議</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">拆分為 4 個 Ticket：
</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 class="k">-</span> Ticket 1: 定義 Rating Domain 模型（職責 1, 2）
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="k">-</span> Ticket 2: 實作 RatingRepository（職責 5, 6）
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="k">-</span> Ticket 3: 實作 RateBookUseCase（職責 4, 7）
</span></span><span class="line"><span class="ln">6</span><span class="cl">- Ticket 4: 實作 RatingWidget UI（職責 3, 8）</span></span></code></pre></div><hr>
<h3 id="12-指標-2程式碼行數lines-of-code">1.2 指標 2：程式碼行數（Lines of Code）</h3>
<p><strong>定義</strong>：Ticket 涉及的程式碼修改行數（新增 + 修改 + 刪除）。</p>
<p><strong>為什麼需要行數指標</strong>：</p>
<ul>
<li><strong>可量化</strong>：使用 <code>git diff --stat</code> 精確統計</li>
<li><strong>可驗證</strong>：執行後可驗證預估準確性</li>
<li><strong>風險指標</strong>：行數越多，出錯風險越高</li>
</ul>
<h4 id="行數統計標準">行數統計標準</h4>
<p><strong>測量方式</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><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">git diff --stat
</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 class="c1"># 範例輸出</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">lib/domain/entities/book.dart        <span class="p">|</span> <span class="m">25</span> +++++++++++++
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">lib/domain/repositories/i_repo.dart  <span class="p">|</span> <span class="m">15</span> ++++++--
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">test/unit/domain/book_test.dart      <span class="p">|</span> <span class="m">40</span> ++++++++++++++++++++
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="m">3</span> files changed, <span class="m">78</span> insertions<span class="o">(</span>+<span class="o">)</span>, <span class="m">2</span> deletions<span class="o">(</span>-<span class="o">)</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 計算方式</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nv">總行數</span> <span class="o">=</span> 新增行數 + 修改行數 + <span class="nv">刪除行數</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">      <span class="o">=</span> <span class="m">25</span> + <span class="m">15</span> + <span class="nv">40</span> <span class="o">=</span> <span class="m">80</span> 行</span></span></code></pre></div><p><strong>計算規則</strong>：</p>
<ul>
<li>包含新增行數</li>
<li>包含修改行數</li>
<li>包含刪除行數</li>
<li>不包含空行（pure whitespace changes）</li>
<li>不包含純註解行（comment-only changes）</li>
</ul>
<h4 id="行數標準">行數標準</h4>
<table>
  <thead>
      <tr>
          <th>等級</th>
          <th>行數範圍</th>
          <th>判定</th>
          <th>說明</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>簡單 Ticket</strong></td>
          <td>&lt; 30 行</td>
          <td>理想</td>
          <td>Interface 定義、簡單 Value Object</td>
      </tr>
      <tr>
          <td><strong>中等 Ticket</strong></td>
          <td>30-70 行</td>
          <td>可接受</td>
          <td>中等實作、含測試</td>
      </tr>
      <tr>
          <td><strong>複雜 Ticket</strong></td>
          <td>70-100 行</td>
          <td>需檢查</td>
          <td>複雜實作、多測試案例</td>
      </tr>
      <tr>
          <td><strong>必須拆分</strong></td>
          <td>&gt; 100 行</td>
          <td>禁止</td>
          <td>範圍過大，必須拆分</td>
      </tr>
  </tbody>
</table>
<p><strong>強制規則</strong>：</p>
<ul>
<li><strong>超過 100 行 = 必須拆分</strong></li>
<li><strong>70-100 行 = 評估是否可拆分</strong></li>
<li><strong>&lt; 70 行 = 可接受範圍</strong></li>
</ul>
<h4 id="行數估算實例">行數估算實例</h4>
<h5 id="實例-1簡單-ticket-30-行">實例 1：簡單 Ticket（&lt; 30 行）</h5>





<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">// 任務：定義 IBookRepository 介面
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1">// lib/domain/repositories/i_book_repository.dart
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">IBookRepository</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="c1">/// 根據 ISBN 取得書籍
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span>  <span class="n">Future</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">?&gt;</span> <span class="n">getBookByIsbn</span><span class="p">(</span><span class="kt">String</span> <span class="n">isbn</span><span class="p">);</span>
</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">  <span class="c1">/// 儲存書籍
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"></span>  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">saveBook</span><span class="p">(</span><span class="n">Book</span> <span class="n">book</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">  <span class="c1">/// 刪除書籍
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"></span>  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">deleteBook</span><span class="p">(</span><span class="kt">String</span> <span class="n">isbn</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1">// 預估行數：~20 行（含註解）
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1"></span><span class="o">//</span> <span class="err">判定：簡單</span> <span class="n">Ticket</span></span></span></code></pre></div><h6 id="實例-2中等-ticket30-70-行">實例 2：中等 Ticket（30-70 行）</h6>





<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">// 任務：實作 Rating Value Object
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1">// lib/domain/value_objects/rating.dart + test
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1">// rating.dart (~40 行)
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">Rating</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  <span class="kd">final</span> <span class="kt">int</span> <span class="n">value</span><span class="p">;</span>
</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">  <span class="n">Rating</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="n">value</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">value</span> <span class="o">&lt;</span> <span class="m">1</span> <span class="o">||</span> <span class="n">value</span> <span class="o">&gt;</span> <span class="m">5</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">      <span class="k">throw</span> <span class="n">ValidationException</span><span class="p">.</span><span class="n">invalidRating</span><span class="p">(</span><span class="n">value</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl">  <span class="c1">// ... 其他方法（equals, hashCode, toString）
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c1">// rating_test.dart (~25 行)
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">  <span class="n">test</span><span class="p">(</span><span class="s1">&#39;建立有效評分&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">  <span class="n">test</span><span class="p">(</span><span class="s1">&#39;評分過低拋出異常&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">  <span class="n">test</span><span class="p">(</span><span class="s1">&#39;評分過高拋出異常&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="c1">// 總計：~65 行
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="c1"></span><span class="o">//</span> <span class="err">判定：中等</span> <span class="n">Ticket</span></span></span></code></pre></div><h6 id="實例-3複雜-ticket70-100-行--建議拆分">實例 3：複雜 Ticket（70-100 行）- 建議拆分</h6>





<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">// 任務：實作完整的 BookRepository
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1">// lib/infrastructure/repositories/sqlite_book_repository.dart + test
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1">// repository.dart (~80 行)
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">SQLiteBookRepository</span> <span class="kd">implements</span> <span class="n">IBookRepository</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  <span class="c1">// ... 建構子和欄位定義
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="err">@</span><span class="n">override</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">?&gt;</span> <span class="n">getBookByIsbn</span><span class="p">(</span><span class="kt">String</span> <span class="n">isbn</span><span class="p">)</span> <span class="kd">async</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="c1">// ... SQL 查詢邏輯（~15 行）
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"></span>    <span class="c1">// ... Data Mapper 轉換（~10 行）
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"></span>    <span class="c1">// ... 錯誤處理（~5 行）
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">  <span class="err">@</span><span class="n">override</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">saveBook</span><span class="p">(</span><span class="n">Book</span> <span class="n">book</span><span class="p">)</span> <span class="kd">async</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="c1">// ... SQL 插入/更新邏輯（~20 行）
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"></span>    <span class="c1">// ... Data Mapper 轉換（~10 行）
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="c1"></span>    <span class="c1">// ... 錯誤處理（~5 行）
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl">  <span class="err">@</span><span class="n">override</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">deleteBook</span><span class="p">(</span><span class="kt">String</span> <span class="n">isbn</span><span class="p">)</span> <span class="kd">async</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="c1">// ... SQL 刪除邏輯（~10 行）
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="c1"></span>    <span class="c1">// ... 錯誤處理（~5 行）
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="c1">// repository_test.dart (~80 行)
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="c1">// ... 8 個測試案例
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="c1">// 總計：~160 行
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="c1"></span><span class="o">//</span> <span class="err">判定：超過</span> <span class="m">100</span> <span class="err">行，必須拆分</span></span></span></code></pre></div><p><strong>拆分建議</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">拆分為 3 個 Ticket：
</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 class="k">-</span> Ticket 1: 定義 IBookRepository 介面（~20 行）
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="k">-</span> Ticket 2: 實作 getBookByIsbn + 測試（~60 行）
</span></span><span class="line"><span class="ln">5</span><span class="cl">- Ticket 3: 實作 saveBook + deleteBook + 測試（~80 行）</span></span></code></pre></div><hr>
<h3 id="13-指標-3涉及檔案數files">1.3 指標 3：涉及檔案數（Files）</h3>
<p><strong>定義</strong>：Ticket 需要修改的檔案數量（不含測試檔案）。</p>
<p><strong>為什麼需要檔案數指標</strong>：</p>
<ul>
<li><strong>架構層級指標</strong>：檔案數反映任務的架構範圍</li>
<li><strong>風險管控</strong>：修改越多檔案，影響範圍越大</li>
<li><strong>測試複雜度</strong>：檔案數越多，測試整合越複雜</li>
</ul>
<h4 id="檔案統計標準">檔案統計標準</h4>
<p><strong>測量方式</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><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">git diff --name-only <span class="p">|</span> grep -v <span class="s1">&#39;_test\.dart$&#39;</span> <span class="p">|</span> wc -l
</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 class="c1"># 範例輸出</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">lib/domain/entities/book.dart
</span></span><span class="line"><span class="ln">6</span><span class="cl">lib/domain/repositories/i_book_repository.dart
</span></span><span class="line"><span class="ln">7</span><span class="cl">lib/infrastructure/repositories/sqlite_book_repository.dart
</span></span><span class="line"><span class="ln">8</span><span class="cl">
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="c1"># 結果：3 個檔案（不含測試）</span></span></span></code></pre></div><p><strong>計算規則</strong>：</p>
<ul>
<li>包含新建檔案</li>
<li>包含修改檔案</li>
<li>包含刪除檔案</li>
<li>不包含測試檔案（測試檔案另計為「測試數量」指標）</li>
<li>不包含配置檔案（如 pubspec.yaml, analysis_options.yaml）</li>
</ul>
<h4 id="檔案數標準">檔案數標準</h4>
<table>
  <thead>
      <tr>
          <th>等級</th>
          <th>檔案數量</th>
          <th>判定</th>
          <th>說明</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>簡單 Ticket</strong></td>
          <td>1 個</td>
          <td>理想</td>
          <td>單一檔案修改</td>
      </tr>
      <tr>
          <td><strong>中等 Ticket</strong></td>
          <td>2-3 個</td>
          <td>可接受</td>
          <td>相關檔案修改</td>
      </tr>
      <tr>
          <td><strong>複雜 Ticket</strong></td>
          <td>3-5 個</td>
          <td>需檢查</td>
          <td>多檔案修改，建議拆分</td>
      </tr>
      <tr>
          <td><strong>必須拆分</strong></td>
          <td>&gt; 5 個</td>
          <td>禁止</td>
          <td>範圍過大，必須拆分</td>
      </tr>
  </tbody>
</table>
<p><strong>強制規則</strong>：</p>
<ul>
<li><strong>超過 5 個檔案 = 必須拆分</strong></li>
<li><strong>3-5 個檔案 = 評估是否可拆分</strong></li>
<li><strong>1-2 個檔案 = 理想狀態</strong></li>
</ul>
<h4 id="檔案數實例">檔案數實例</h4>
<h5 id="實例-1簡單-ticket1-個檔案">實例 1：簡單 Ticket（1 個檔案）</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">任務：建立 Rating Value Object
</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">lib/domain/value_objects/rating.dart
</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">總計：1 個檔案
</span></span><span class="line"><span class="ln">7</span><span class="cl">判定：簡單 Ticket</span></span></code></pre></div><h6 id="實例-2中等-ticket2-3-個檔案">實例 2：中等 Ticket（2-3 個檔案）</h6>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">任務：更新 Book Entity 增加評分欄位
</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">lib/domain/entities/book.dart         （修改）
</span></span><span class="line"><span class="ln">5</span><span class="cl">lib/domain/value_objects/rating.dart  （新增）
</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">總計：2 個檔案
</span></span><span class="line"><span class="ln">8</span><span class="cl">判定：中等 Ticket</span></span></code></pre></div><h6 id="實例-3複雜-ticket3-5-個檔案--建議拆分">實例 3：複雜 Ticket（3-5 個檔案）- 建議拆分</h6>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">任務：實作完整的書籍評分功能
</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">lib/domain/entities/rating.dart
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">lib/domain/entities/book.dart
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">lib/domain/repositories/i_rating_repository.dart
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">lib/infrastructure/repositories/sqlite_rating_repository.dart
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">lib/infrastructure/mappers/rating_mapper.dart
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">總計：5 個檔案
</span></span><span class="line"><span class="ln">11</span><span class="cl">判定：複雜 Ticket（建議拆分）</span></span></code></pre></div><h6 id="實例-4必須拆分-5-個檔案">實例 4：必須拆分（&gt; 5 個檔案）</h6>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">任務：實作評分功能（含 UI、UseCase、Repository）
</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">lib/presentation/widgets/rating_widget.dart
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">lib/presentation/controllers/rating_controller.dart
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">lib/application/use_cases/rate_book_use_case.dart
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">lib/domain/entities/rating.dart
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">lib/domain/entities/book.dart
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">lib/domain/repositories/i_rating_repository.dart
</span></span><span class="line"><span class="ln">10</span><span class="cl">lib/infrastructure/repositories/sqlite_rating_repository.dart
</span></span><span class="line"><span class="ln">11</span><span class="cl">lib/infrastructure/mappers/rating_mapper.dart
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">總計：8 個檔案
</span></span><span class="line"><span class="ln">14</span><span class="cl">判定：God Ticket，必須拆分</span></span></code></pre></div><p><strong>拆分建議</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">按 Clean Architecture 分層拆分為 4 個 Ticket：
</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 class="k">-</span> Ticket 1: Domain 層（檔案 4, 5, 6）             - 3 個檔案
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="k">-</span> Ticket 2: Infrastructure 層（檔案 7, 8）        - 2 個檔案
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="k">-</span> Ticket 3: Application 層（檔案 3）              - 1 個檔案
</span></span><span class="line"><span class="ln">6</span><span class="cl">- Ticket 4: Presentation 層（檔案 1, 2）          - 2 個檔案</span></span></code></pre></div><hr>
<h3 id="14-指標-4測試用例數tests">1.4 指標 4：測試用例數（Tests）</h3>
<p><strong>定義</strong>：Ticket 對應的測試用例數量。</p>
<p><strong>為什麼需要測試數量指標</strong>：</p>
<ul>
<li><strong>品質保證指標</strong>：測試數量反映功能複雜度</li>
<li><strong>執行時間預估</strong>：測試數量影響 TDD 循環時間</li>
<li><strong>維護成本</strong>：過多測試增加維護負擔</li>
</ul>
<h4 id="測試統計標準">測試統計標準</h4>
<p><strong>測量方式</strong>：</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="kt">void</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="n">group</span><span class="p">(</span><span class="s1">&#39;Rating Value Object&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="n">test</span><span class="p">(</span><span class="s1">&#39;建立有效評分&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span>           <span class="c1">// 測試 1
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span>    <span class="n">test</span><span class="p">(</span><span class="s1">&#39;評分過低拋出異常&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span>        <span class="c1">// 測試 2
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span>    <span class="n">test</span><span class="p">(</span><span class="s1">&#39;評分過高拋出異常&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span>        <span class="c1">// 測試 3
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"></span>    <span class="n">test</span><span class="p">(</span><span class="s1">&#39;相同評分視為相等&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span>        <span class="c1">// 測試 4
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span>  <span class="p">});</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="o">//</span> <span class="err">總計：</span><span class="m">4</span> <span class="err">個測試</span></span></span></code></pre></div><p><strong>計算規則</strong>：</p>
<ul>
<li>每個 <code>test('...', () {...})</code> 算一個測試</li>
<li>每個 <code>testWidgets('...', () {...})</code> 算一個測試</li>
<li>包含單元測試和整合測試</li>
<li>不包含 <code>group()</code> 本身（只是測試組織）</li>
</ul>
<h4 id="測試數量標準">測試數量標準</h4>
<table>
  <thead>
      <tr>
          <th>等級</th>
          <th>測試數量</th>
          <th>判定</th>
          <th>說明</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>簡單 Ticket</strong></td>
          <td>1-3 個</td>
          <td>理想</td>
          <td>基本功能測試</td>
      </tr>
      <tr>
          <td><strong>中等 Ticket</strong></td>
          <td>3-6 個</td>
          <td>可接受</td>
          <td>含邊界和異常測試</td>
      </tr>
      <tr>
          <td><strong>複雜 Ticket</strong></td>
          <td>6-10 個</td>
          <td>需檢查</td>
          <td>複雜邏輯，多測試案例</td>
      </tr>
      <tr>
          <td><strong>必須拆分</strong></td>
          <td>&gt; 10 個</td>
          <td>禁止</td>
          <td>測試過多，必須拆分</td>
      </tr>
  </tbody>
</table>
<p><strong>強制規則</strong>：</p>
<ul>
<li><strong>超過 10 個測試 = 必須拆分</strong></li>
<li><strong>6-10 個測試 = 評估是否可拆分</strong></li>
<li><strong>1-6 個測試 = 可接受範圍</strong></li>
</ul>
<h4 id="測試數量實例">測試數量實例</h4>
<h5 id="實例-1簡單-ticket1-3-個測試">實例 1：簡單 Ticket（1-3 個測試）</h5>





<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">// 任務：定義 Rating Value Object
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kt">void</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="n">group</span><span class="p">(</span><span class="s1">&#39;Rating Value Object&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="n">test</span><span class="p">(</span><span class="s1">&#39;建立有效評分&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">      <span class="kd">final</span> <span class="n">rating</span> <span class="o">=</span> <span class="n">Rating</span><span class="p">(</span><span class="m">4</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">      <span class="n">expect</span><span class="p">(</span><span class="n">rating</span><span class="p">.</span><span class="n">value</span><span class="p">,</span> <span class="m">4</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="n">test</span><span class="p">(</span><span class="s1">&#39;評分過低拋出異常&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">      <span class="n">expect</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="n">Rating</span><span class="p">(</span><span class="m">0</span><span class="p">),</span> <span class="n">throwsA</span><span class="p">(</span><span class="n">isA</span><span class="o">&lt;</span><span class="n">ValidationException</span><span class="o">&gt;</span><span class="p">()));</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="n">test</span><span class="p">(</span><span class="s1">&#39;評分過高拋出異常&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">      <span class="n">expect</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="n">Rating</span><span class="p">(</span><span class="m">6</span><span class="p">),</span> <span class="n">throwsA</span><span class="p">(</span><span class="n">isA</span><span class="o">&lt;</span><span class="n">ValidationException</span><span class="o">&gt;</span><span class="p">()));</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="c1">// 總計：3 個測試
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="c1"></span><span class="o">//</span> <span class="err">判定：簡單</span> <span class="n">Ticket</span></span></span></code></pre></div><h6 id="實例-2中等-ticket3-6-個測試">實例 2：中等 Ticket（3-6 個測試）</h6>





<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">// 任務：實作 BookRepository.getBookByIsbn
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kt">void</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="n">group</span><span class="p">(</span><span class="s1">&#39;BookRepository.getBookByIsbn&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="n">test</span><span class="p">(</span><span class="s1">&#39;成功取得存在的書籍&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span>          <span class="c1">// 測試 1
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span>    <span class="n">test</span><span class="p">(</span><span class="s1">&#39;書籍不存在回傳 null&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span>          <span class="c1">// 測試 2
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"></span>    <span class="n">test</span><span class="p">(</span><span class="s1">&#39;無效 ISBN 拋出 ValidationException&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span> <span class="c1">// 測試 3
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span>    <span class="n">test</span><span class="p">(</span><span class="s1">&#39;資料庫錯誤拋出 StorageException&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span>    <span class="c1">// 測試 4
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"></span>    <span class="n">test</span><span class="p">(</span><span class="s1">&#39;資料庫連線失敗拋出 NetworkException&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span> <span class="c1">// 測試 5
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"></span>  <span class="p">});</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1">// 總計：5 個測試
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"></span><span class="o">//</span> <span class="err">判定：中等</span> <span class="n">Ticket</span></span></span></code></pre></div><h6 id="實例-3複雜-ticket6-10-個測試--建議拆分">實例 3：複雜 Ticket（6-10 個測試）- 建議拆分</h6>





<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">// 任務：實作完整的 BookRepository CRUD
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kt">void</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="n">group</span><span class="p">(</span><span class="s1">&#39;BookRepository CRUD&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="c1">// getBookByIsbn 測試
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span>    <span class="n">test</span><span class="p">(</span><span class="s1">&#39;取得存在的書籍&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span>              <span class="c1">// 測試 1
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"></span>    <span class="n">test</span><span class="p">(</span><span class="s1">&#39;書籍不存在回傳 null&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span>         <span class="c1">// 測試 2
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="c1">// saveBook 測試
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"></span>    <span class="n">test</span><span class="p">(</span><span class="s1">&#39;新增書籍成功&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span>                <span class="c1">// 測試 3
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"></span>    <span class="n">test</span><span class="p">(</span><span class="s1">&#39;更新書籍成功&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span>                <span class="c1">// 測試 4
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"></span>    <span class="n">test</span><span class="p">(</span><span class="s1">&#39;儲存時資料庫錯誤&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span>            <span class="c1">// 測試 5
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="c1">// deleteBook 測試
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"></span>    <span class="n">test</span><span class="p">(</span><span class="s1">&#39;刪除存在的書籍&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span>              <span class="c1">// 測試 6
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1"></span>    <span class="n">test</span><span class="p">(</span><span class="s1">&#39;刪除不存在的書籍&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span>            <span class="c1">// 測試 7
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c1"></span>    <span class="n">test</span><span class="p">(</span><span class="s1">&#39;刪除時資料庫錯誤&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span>            <span class="c1">// 測試 8
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="c1">// Data Mapper 測試
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="c1"></span>    <span class="n">test</span><span class="p">(</span><span class="s1">&#39;Entity 轉 DTO 正確&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span>          <span class="c1">// 測試 9
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="c1"></span>    <span class="n">test</span><span class="p">(</span><span class="s1">&#39;DTO 轉 Entity 正確&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span> <span class="p">...</span> <span class="p">});</span>          <span class="c1">// 測試 10
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="c1"></span>  <span class="p">});</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="c1">// 總計：10 個測試
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="c1"></span><span class="o">//</span> <span class="err">判定：複雜</span> <span class="n">Ticket</span><span class="err">（達上限，建議拆分）</span></span></span></code></pre></div><p><strong>拆分建議</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">拆分為 3 個 Ticket：
</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 class="k">-</span> Ticket 1: getBookByIsbn + 測試（2 個測試）    
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="k">-</span> Ticket 2: saveBook + 測試（3 個測試）         
</span></span><span class="line"><span class="ln">5</span><span class="cl">- Ticket 3: deleteBook + Mapper + 測試（5 個測試）</span></span></code></pre></div><hr>
<h3 id="15-指標整合評估方法">1.5 指標整合評估方法</h3>
<h4 id="整合評估原則">整合評估原則</h4>
<p><strong>最高等級原則</strong>：</p>
<ul>
<li>取 4 個指標中「最高的複雜度等級」作為最終評估結果</li>
<li>任一指標達到「必須拆分」，則整個 Ticket 必須拆分</li>
</ul>
<p><strong>評估公式</strong>：</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">Ticket 複雜度 = max(職責複雜度, 行數複雜度, 檔案數複雜度, 測試數複雜度)
</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">where 複雜度等級：
</span></span><span class="line"><span class="ln">4</span><span class="cl">  簡單 = 1
</span></span><span class="line"><span class="ln">5</span><span class="cl">  中等 = 2
</span></span><span class="line"><span class="ln">6</span><span class="cl">  複雜 = 3
</span></span><span class="line"><span class="ln">7</span><span class="cl">  必須拆分 = 4</span></span></code></pre></div><h4 id="整合評估實例">整合評估實例</h4>
<h5 id="實例-1所有指標都是簡單--簡單-ticket">實例 1：所有指標都是簡單 → 簡單 Ticket</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">任務：定義 Rating Value Object
</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"><span class="k">-</span> 職責數量：1 個職責 → 簡單
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">-</span> 程式碼行數：25 行 → 簡單
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">-</span> 涉及檔案：1 個檔案 → 簡單
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">-</span> 測試用例：3 個測試 → 簡單
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">最終判定：簡單 Ticket（最理想狀態）</span></span></code></pre></div><h6 id="實例-2有一個指標是中等--中等-ticket">實例 2：有一個指標是中等 → 中等 Ticket</h6>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">任務：實作 Rating Value Object
</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"><span class="k">-</span> 職責數量：2 個職責（建立、驗證）→ 中等
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">-</span> 程式碼行數：45 行 → 中等
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">-</span> 涉及檔案：1 個檔案 → 簡單
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">-</span> 測試用例：5 個測試 → 中等
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">最終判定：中等 Ticket（可接受）</span></span></code></pre></div><h6 id="實例-3有一個指標是複雜--複雜-ticket建議拆分">實例 3：有一個指標是複雜 → 複雜 Ticket（建議拆分）</h6>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">任務：實作 BookRepository.getBookByIsbn
</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"><span class="k">-</span> 職責數量：3 個職責（查詢、轉換、異常處理）→ 複雜
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">-</span> 程式碼行數：65 行 → 中等
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">-</span> 涉及檔案：2 個檔案 → 中等
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">-</span> 測試用例：6 個測試 → 複雜
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">最終判定：複雜 Ticket（建議拆分）</span></span></code></pre></div><h6 id="實例-4有任一指標超標--必須拆分">實例 4：有任一指標超標 → 必須拆分</h6>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">任務：實作完整的書籍評分功能
</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"><span class="k">-</span> 職責數量：8 個職責 → 必須拆分
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">-</span> 程式碼行數：180 行 → 必須拆分
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">-</span> 涉及檔案：7 個檔案 → 必須拆分
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">-</span> 測試用例：15 個測試 → 必須拆分
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">最終判定：God Ticket 必須立即拆分</span></span></code></pre></div><h4 id="評估決策流程">評估決策流程</h4>





<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">步驟 1：計算 4 個指標
</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">步驟 2：取最高複雜度等級
</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">    ├─ 簡單 → 可直接建立 Ticket
</span></span><span class="line"><span class="ln">6</span><span class="cl">    ├─ 中等 → 可直接建立 Ticket（可選：評估是否拆分為更小 Ticket）
</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">    └─ 必須拆分 → 阻止建立，必須先拆分再重新評估</span></span></code></pre></div><p><strong>評估檢查清單</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">□ 已計算 4 個指標的值
</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">□ 如為「複雜」，已評估是否可拆分為更小 Ticket</span></span></code></pre></div><hr>
<h2 id="第二章複雜度評估方法">第二章：複雜度評估方法</h2>
<h3 id="21-複雜度等級定義">2.1 複雜度等級定義</h3>
<p><strong>4 級複雜度體系</strong>：</p>
<table>
  <thead>
      <tr>
          <th>等級</th>
          <th>職責</th>
          <th>行數</th>
          <th>檔案</th>
          <th>測試</th>
          <th>描述</th>
          <th>處理方式</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Level 1: 簡單</strong></td>
          <td>1 個</td>
          <td>&lt; 30 行</td>
          <td>1 個</td>
          <td>1-3 個</td>
          <td>單一職責，單一檔案</td>
          <td>直接建立 Ticket</td>
      </tr>
      <tr>
          <td><strong>Level 2: 中等</strong></td>
          <td>2-3 個</td>
          <td>30-70 行</td>
          <td>2-3 個</td>
          <td>3-6 個</td>
          <td>少數相關職責，少數檔案</td>
          <td>直接建立 Ticket</td>
      </tr>
      <tr>
          <td><strong>Level 3: 複雜</strong></td>
          <td>3-5 個</td>
          <td>70-100 行</td>
          <td>3-5 個</td>
          <td>6-10 個</td>
          <td>多職責，多檔案</td>
          <td>建議拆分</td>
      </tr>
      <tr>
          <td><strong>Level 4: 超標</strong></td>
          <td>&gt; 5 個</td>
          <td>&gt; 100 行</td>
          <td>&gt; 5 個</td>
          <td>&gt; 10 個</td>
          <td>範圍失控</td>
          <td>必須拆分</td>
      </tr>
  </tbody>
</table>
<p><strong>複雜度特徵</strong>：</p>
<h4 id="level-1-簡單">Level 1: 簡單</h4>
<ul>
<li><strong>特徵</strong>: 最小可交付單元，職責明確</li>
<li><strong>適用</strong>: Interface 定義、單一 Value Object、單一方法實作</li>
<li><strong>優點</strong>: 風險低、易測試、易 Review</li>
<li><strong>預估時間</strong>: 5-20 分鐘</li>
</ul>
<h5 id="level-2-中等">Level 2: 中等</h5>
<ul>
<li><strong>特徵</strong>: 少數相關職責，內聚性高</li>
<li><strong>適用</strong>: 含業務邏輯的 Entity、基礎 Repository 方法</li>
<li><strong>注意</strong>: 確保職責相關性，避免職責分散</li>
<li><strong>預估時間</strong>: 20-40 分鐘</li>
</ul>
<h6 id="level-3-複雜">Level 3: 複雜</h6>
<ul>
<li><strong>特徵</strong>: 多職責或跨檔案，整合性高</li>
<li><strong>適用</strong>: 完整 UseCase、Repository CRUD、複雜業務邏輯</li>
<li><strong>風險</strong>: 測試複雜、Review 困難、易出錯</li>
<li><strong>預估時間</strong>: 40-60 分鐘</li>
<li><strong>建議</strong>: 優先評估是否可拆分為 Level 1-2</li>
</ul>
<h6 id="level-4-超標">Level 4: 超標</h6>
<ul>
<li><strong>特徵</strong>: 任一指標超標，範圍失控</li>
<li><strong>問題</strong>: God Ticket、高風險、難以管理</li>
<li><strong>處理</strong>: 必須拆分，無例外</li>
<li><strong>禁止</strong>: 禁止建立此等級 Ticket</li>
</ul>
<hr>
<h3 id="22-評估流程">2.2 評估流程</h3>
<p><strong>3 步驟評估流程</strong>：</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">步驟 1: 初步評估（基於任務描述）
</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">    計算 4 個指標的預估值
</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">步驟 2: 複雜度確認（取最高等級）
</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">    取 4 個指標中最高的複雜度等級
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    ↓
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">步驟 3: 拆分決策（基於等級決定）
</span></span><span class="line"><span class="ln">10</span><span class="cl">    ↓
</span></span><span class="line"><span class="ln">11</span><span class="cl">    ├─ Level 1-2 → 可直接建立 Ticket
</span></span><span class="line"><span class="ln">12</span><span class="cl">    ├─ Level 3 → 評估是否可拆分
</span></span><span class="line"><span class="ln">13</span><span class="cl">    └─ Level 4 → 必須拆分</span></span></code></pre></div><h4 id="步驟-1初步評估">步驟 1：初步評估</h4>
<p><strong>目標</strong>: 快速估算 4 個指標的值</p>
<p><strong>評估依據</strong>:</p>
<ol>
<li>任務描述（What to do）</li>
<li>驗收條件（Acceptance Criteria）</li>
<li>預期步驟（Steps）</li>
</ol>
<p><strong>評估方法</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">範例任務：實作 Book Entity
</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">步驟 1-1：估算職責數量
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="k">-</span> 分析任務描述，列出所有需要完成的功能點
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">-</span> 功能點 1：定義 Entity 欄位
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">-</span> 功能點 2：實作 equals/hashCode
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">-</span> 功能點 3：實作 toString
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">-</span> 功能點 4：撰寫測試
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">→ 預估：4 個職責
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">步驟 1-2：估算程式碼行數
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">-</span> 根據類似任務經驗估算
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">-</span> Entity 定義：~30 行
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">-</span> equals/hashCode：~15 行
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">-</span> toString：~5 行
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">-</span> 測試：~40 行
</span></span><span class="line"><span class="ln">17</span><span class="cl">→ 預估：~90 行
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl">步驟 1-3：估算檔案數量
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">-</span> 列出需要建立/修改的檔案
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">-</span> lib/domain/entities/book.dart（新增）
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">-</span> test/unit/domain/entities/book_test.dart（新增）
</span></span><span class="line"><span class="ln">23</span><span class="cl">→ 預估：2 個檔案（1 個生產 + 1 個測試）
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span><span class="line"><span class="ln">25</span><span class="cl">步驟 1-4：估算測試數量
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="k">-</span> 根據驗收條件估算測試案例
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="k">-</span> 測試 Entity 建立
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="k">-</span> 測試 equals（相等/不等）
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="k">-</span> 測試 hashCode
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="k">-</span> 測試 toString
</span></span><span class="line"><span class="ln">31</span><span class="cl">→ 預估：5 個測試</span></span></code></pre></div><h4 id="步驟-2複雜度確認">步驟 2：複雜度確認</h4>
<p><strong>目標</strong>: 確定最終複雜度等級</p>
<p><strong>確認方法</strong>:</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">對應 4 個指標到複雜度等級：
</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">職責數量：4 個 → Level 3（複雜）
</span></span><span class="line"><span class="ln">4</span><span class="cl">程式碼行數：90 行 → Level 3（複雜）
</span></span><span class="line"><span class="ln">5</span><span class="cl">檔案數量：2 個（1 生產 + 1 測試）→ Level 2（中等）
</span></span><span class="line"><span class="ln">6</span><span class="cl">測試數量：5 個 → Level 2（中等）
</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">取最高等級：Level 3（複雜）</span></span></code></pre></div><p><strong>確認檢查清單</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">□ 已計算所有 4 個指標
</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></code></pre></div><h4 id="步驟-3拆分決策">步驟 3：拆分決策</h4>
<p><strong>目標</strong>: 決定是否拆分以及如何拆分</p>
<p><strong>決策規則</strong>:</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">Level 1-2 → 可直接建立 Ticket
</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">    └─ 直接進入 Phase 2（測試設計）
</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">Level 3 → 評估是否可拆分
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    ├─ 可拆分為 Level 1-2 → 執行拆分
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    └─ 無法拆分 → 勉強接受，加強 Review
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">Level 4 → 必須拆分
</span></span><span class="line"><span class="ln">10</span><span class="cl">    ├─ 阻止建立 Ticket
</span></span><span class="line"><span class="ln">11</span><span class="cl">    ├─ 執行拆分（使用第三章拆分策略）
</span></span><span class="line"><span class="ln">12</span><span class="cl">    └─ 重新評估每個拆分後的子 Ticket</span></span></code></pre></div><p><strong>Level 3 拆分評估</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">範例：實作 Book Entity（Level 3）
</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 class="k">1.</span> 是否可拆分為更小 Ticket？
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">   → 是，可拆分為兩個 Ticket
</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 class="k">2.</span> 如何拆分？
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">   Ticket A: 定義 Book Entity 欄位和基礎方法
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">   <span class="k">-</span> 職責：定義欄位 + equals/hashCode
</span></span><span class="line"><span class="ln">10</span><span class="cl">   <span class="k">-</span> 行數：~45 行
</span></span><span class="line"><span class="ln">11</span><span class="cl">   <span class="k">-</span> 檔案：1 個
</span></span><span class="line"><span class="ln">12</span><span class="cl">   <span class="k">-</span> 測試：3 個
</span></span><span class="line"><span class="ln">13</span><span class="cl">   → Level 2（中等）
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">   Ticket B: 補充 Book Entity 完整功能
</span></span><span class="line"><span class="ln">16</span><span class="cl">   <span class="k">-</span> 職責：toString + 完整測試
</span></span><span class="line"><span class="ln">17</span><span class="cl">   <span class="k">-</span> 行數：~45 行
</span></span><span class="line"><span class="ln">18</span><span class="cl">   <span class="k">-</span> 檔案：1 個（修改）
</span></span><span class="line"><span class="ln">19</span><span class="cl">   <span class="k">-</span> 測試：2 個
</span></span><span class="line"><span class="ln">20</span><span class="cl">   → Level 1（簡單）
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">3.</span> 拆分後依賴關係？
</span></span><span class="line"><span class="ln">23</span><span class="cl">   → Ticket B 依賴 Ticket A（B 在 A 完成後執行）
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="k">4.</span> 拆分價值評估？
</span></span><span class="line"><span class="ln">26</span><span class="cl">   → 降低風險：兩個 Level 1-2 比一個 Level 3 更易管理
</span></span><span class="line"><span class="ln">27</span><span class="cl">   → 易於 Review：分兩次 Review，每次範圍更小
</span></span><span class="line"><span class="ln">28</span><span class="cl">   → 成本：增加一個 Ticket，但風險降低值得
</span></span><span class="line"><span class="ln">29</span><span class="cl">
</span></span><span class="line"><span class="ln">30</span><span class="cl">結論：建議拆分</span></span></code></pre></div><p><strong>Level 4 拆分處理</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">範例：實作完整書籍評分功能（Level 4）
</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"><span class="k">-</span> 職責：8 個 → Level 4
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">-</span> 行數：180 行 → Level 4
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">-</span> 檔案：7 個 → Level 4
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">-</span> 測試：15 個 → Level 4
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">拆分決策：必須拆分（無選項）
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">拆分方式：使用「基於 Clean Architecture 分層拆分策略」（詳見第三章）</span></span></code></pre></div><hr>
<h3 id="23-評估決策樹">2.3 評估決策樹</h3>
<p><strong>完整決策流程圖</strong>:</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><span class="line"><span class="ln"> 2</span><span class="cl">    ↓
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">[計算 4 個指標]
</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">[取最高複雜度等級]
</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">    ├─ Level 1（簡單）
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    │   ↓
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    │   [可直接建立 Ticket]
</span></span><span class="line"><span class="ln">10</span><span class="cl">    │   ↓
</span></span><span class="line"><span class="ln">11</span><span class="cl">    │   [進入 Phase 2：測試設計]
</span></span><span class="line"><span class="ln">12</span><span class="cl">    │
</span></span><span class="line"><span class="ln">13</span><span class="cl">    ├─ Level 2（中等）
</span></span><span class="line"><span class="ln">14</span><span class="cl">    │   ↓
</span></span><span class="line"><span class="ln">15</span><span class="cl">    │   [可直接建立 Ticket]
</span></span><span class="line"><span class="ln">16</span><span class="cl">    │   ↓
</span></span><span class="line"><span class="ln">17</span><span class="cl">    │   [（可選）評估是否拆分為更小 Ticket]
</span></span><span class="line"><span class="ln">18</span><span class="cl">    │   ↓
</span></span><span class="line"><span class="ln">19</span><span class="cl">    │   [進入 Phase 2：測試設計]
</span></span><span class="line"><span class="ln">20</span><span class="cl">    │
</span></span><span class="line"><span class="ln">21</span><span class="cl">    ├─ Level 3（複雜）
</span></span><span class="line"><span class="ln">22</span><span class="cl">    │   ↓
</span></span><span class="line"><span class="ln">23</span><span class="cl">    │   [評估是否可拆分]
</span></span><span class="line"><span class="ln">24</span><span class="cl">    │   ↓
</span></span><span class="line"><span class="ln">25</span><span class="cl">    │   ├─ 可拆分？
</span></span><span class="line"><span class="ln">26</span><span class="cl">    │   │   ├─ Yes → [執行拆分]
</span></span><span class="line"><span class="ln">27</span><span class="cl">    │   │   │           ↓
</span></span><span class="line"><span class="ln">28</span><span class="cl">    │   │   │       [重新評估子 Ticket]
</span></span><span class="line"><span class="ln">29</span><span class="cl">    │   │   │           ↓
</span></span><span class="line"><span class="ln">30</span><span class="cl">    │   │   │       [確保所有子 Ticket 為 Level 1-2]
</span></span><span class="line"><span class="ln">31</span><span class="cl">    │   │   │
</span></span><span class="line"><span class="ln">32</span><span class="cl">    │   │   └─ No → [勉強接受]
</span></span><span class="line"><span class="ln">33</span><span class="cl">    │   │              ↓
</span></span><span class="line"><span class="ln">34</span><span class="cl">    │   │          [標記為高風險 Ticket]
</span></span><span class="line"><span class="ln">35</span><span class="cl">    │   │              ↓
</span></span><span class="line"><span class="ln">36</span><span class="cl">    │   │          [加強 Review 機制]
</span></span><span class="line"><span class="ln">37</span><span class="cl">    │   │              ↓
</span></span><span class="line"><span class="ln">38</span><span class="cl">    │   │          [進入 Phase 2]
</span></span><span class="line"><span class="ln">39</span><span class="cl">    │
</span></span><span class="line"><span class="ln">40</span><span class="cl">    └─ Level 4（超標）
</span></span><span class="line"><span class="ln">41</span><span class="cl">        ↓
</span></span><span class="line"><span class="ln">42</span><span class="cl">        [禁止建立 Ticket]
</span></span><span class="line"><span class="ln">43</span><span class="cl">        ↓
</span></span><span class="line"><span class="ln">44</span><span class="cl">        [阻止進入 Phase 2]
</span></span><span class="line"><span class="ln">45</span><span class="cl">        ↓
</span></span><span class="line"><span class="ln">46</span><span class="cl">        [必須拆分（使用第三章策略）]
</span></span><span class="line"><span class="ln">47</span><span class="cl">        ↓
</span></span><span class="line"><span class="ln">48</span><span class="cl">        [重新評估所有子 Ticket]
</span></span><span class="line"><span class="ln">49</span><span class="cl">        ↓
</span></span><span class="line"><span class="ln">50</span><span class="cl">        [確保所有子 Ticket ≤ Level 3]</span></span></code></pre></div><p><strong>決策節點詳細說明</strong>:</p>
<h4 id="節點-1level-3-拆分評估">節點 1：Level 3 拆分評估</h4>





<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 是否可拆分為更小 Ticket？
</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">1. 職責是否可分離？
</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">   - 是否可按照 Clean Architecture 分層拆分？
</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">2. 拆分後是否降低複雜度？
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">   - 拆分後每個子 Ticket 是否 ≤ Level 2？
</span></span><span class="line"><span class="ln">10</span><span class="cl">   - 是否減少單一 Ticket 的風險？
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">3. 拆分成本是否合理？
</span></span><span class="line"><span class="ln">13</span><span class="cl">   - 增加的管理成本 vs 降低的風險
</span></span><span class="line"><span class="ln">14</span><span class="cl">   - 是否需要額外的整合測試？
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">決策：
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl">- 滿足 1 且 2 → 建議拆分
</span></span><span class="line"><span class="ln">19</span><span class="cl">- 不滿足 1 或 2，但 3 成本高 → 勉強接受 Level 3</span></span></code></pre></div><h5 id="節點-2level-4-強制拆分">節點 2：Level 4 強制拆分</h5>





<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">Level 4 無需評估，必須拆分
</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">1. 優先：按 Clean Architecture 分層拆分（詳見第三章 3.1-3.4）
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">2. 次之：按職責拆分（每個職責獨立 Ticket）
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">3. 最後：按檔案拆分（每個檔案獨立 Ticket）
</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">拆分要求：
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">- 所有子 Ticket 必須 ≤ Level 3
</span></span><span class="line"><span class="ln">11</span><span class="cl">- 建議所有子 Ticket ≤ Level 2
</span></span><span class="line"><span class="ln">12</span><span class="cl">- 理想所有子 Ticket = Level 1
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl">拆分後驗證：
</span></span><span class="line"><span class="ln">15</span><span class="cl">□ 所有子 Ticket 都已重新評估
</span></span><span class="line"><span class="ln">16</span><span class="cl">□ 所有子 Ticket 都 ≤ Level 3
</span></span><span class="line"><span class="ln">17</span><span class="cl">□ 子 Ticket 依賴關係明確
</span></span><span class="line"><span class="ln">18</span><span class="cl">□ 子 Ticket 總和涵蓋原始 Ticket 所有功能</span></span></code></pre></div><hr>
<h3 id="24-複雜度評估實例">2.4 複雜度評估實例</h3>
<p><strong>完整評估案例</strong>：</p>
<h4 id="案例-1簡單-ticket-評估">案例 1：簡單 Ticket 評估</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">任務：定義 IBookRepository 介面
</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">步驟 1：初步評估
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="k">-</span> 職責數量：1 個（定義介面方法簽名）
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">-</span> 程式碼行數：~20 行
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">-</span> 涉及檔案：1 個（i_book_repository.dart）
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">-</span> 測試數量：0 個（Interface 不需測試）
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">步驟 2：複雜度確認
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">-</span> 職責：1 個 → Level 1
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">-</span> 行數：20 行 → Level 1
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">-</span> 檔案：1 個 → Level 1
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">-</span> 測試：0 個 → Level 1
</span></span><span class="line"><span class="ln">14</span><span class="cl">→ 最高等級：Level 1
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">步驟 3：拆分決策
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">-</span> Level 1 → 可直接建立 Ticket
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">-</span> 無需拆分
</span></span><span class="line"><span class="ln">19</span><span class="cl">- 進入 Phase 2</span></span></code></pre></div><h4 id="案例-2中等-ticket-評估">案例 2：中等 Ticket 評估</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">任務：實作 Rating Value Object
</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">步驟 1：初步評估
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="k">-</span> 職責數量：2 個（建立 + 驗證）
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">-</span> 程式碼行數：~50 行（含測試）
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">-</span> 涉及檔案：1 個（rating.dart）
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">-</span> 測試數量：5 個
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">步驟 2：複雜度確認
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">-</span> 職責：2 個 → Level 2
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">-</span> 行數：50 行 → Level 2
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">-</span> 檔案：1 個 → Level 1
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">-</span> 測試：5 個 → Level 2
</span></span><span class="line"><span class="ln">14</span><span class="cl">→ 最高等級：Level 2
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">步驟 3：拆分決策
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">-</span> Level 2 → 可直接建立 Ticket
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">-</span> 評估：可選拆分，但不必要（職責內聚性高）
</span></span><span class="line"><span class="ln">19</span><span class="cl">- 進入 Phase 2</span></span></code></pre></div><h4 id="案例-3複雜-ticket-評估與拆分">案例 3：複雜 Ticket 評估與拆分</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">任務：實作 BookRepository CRUD
</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">步驟 1：初步評估
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="k">-</span> 職責數量：5 個（get + save + delete + mapper + 錯誤處理）
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">-</span> 程式碼行數：~160 行（含測試）
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">-</span> 涉及檔案：2 個（repository.dart + mapper.dart）
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">-</span> 測試數量：10 個
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">步驟 2：複雜度確認
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">-</span> 職責：5 個 → Level 3
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">-</span> 行數：160 行 → Level 4（超過 100 行）
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">-</span> 檔案：2 個 → Level 2
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">-</span> 測試：10 個 → Level 3
</span></span><span class="line"><span class="ln">14</span><span class="cl">→ 最高等級：Level 4（行數超標）
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">步驟 3：拆分決策
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">-</span> Level 4 → 必須拆分
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">-</span> 拆分方式：按 CRUD 方法拆分
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl">  Ticket A: 實作 getBookByIsbn
</span></span><span class="line"><span class="ln">21</span><span class="cl">  <span class="k">-</span> 職責：2 個（查詢 + 轉換）
</span></span><span class="line"><span class="ln">22</span><span class="cl">  <span class="k">-</span> 行數：~50 行
</span></span><span class="line"><span class="ln">23</span><span class="cl">  <span class="k">-</span> 檔案：2 個
</span></span><span class="line"><span class="ln">24</span><span class="cl">  <span class="k">-</span> 測試：3 個
</span></span><span class="line"><span class="ln">25</span><span class="cl">  → Level 2
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl">  Ticket B: 實作 saveBook
</span></span><span class="line"><span class="ln">28</span><span class="cl">  <span class="k">-</span> 職責：2 個（儲存 + 錯誤處理）
</span></span><span class="line"><span class="ln">29</span><span class="cl">  <span class="k">-</span> 行數：~60 行
</span></span><span class="line"><span class="ln">30</span><span class="cl">  <span class="k">-</span> 檔案：1 個（修改 repository.dart）
</span></span><span class="line"><span class="ln">31</span><span class="cl">  <span class="k">-</span> 測試：4 個
</span></span><span class="line"><span class="ln">32</span><span class="cl">  → Level 2
</span></span><span class="line"><span class="ln">33</span><span class="cl">
</span></span><span class="line"><span class="ln">34</span><span class="cl">  Ticket C: 實作 deleteBook
</span></span><span class="line"><span class="ln">35</span><span class="cl">  <span class="k">-</span> 職責：2 個（刪除 + 錯誤處理）
</span></span><span class="line"><span class="ln">36</span><span class="cl">  <span class="k">-</span> 行數：~50 行
</span></span><span class="line"><span class="ln">37</span><span class="cl">  <span class="k">-</span> 檔案：1 個（修改 repository.dart）
</span></span><span class="line"><span class="ln">38</span><span class="cl">  <span class="k">-</span> 測試：3 個
</span></span><span class="line"><span class="ln">39</span><span class="cl">  → Level 2
</span></span><span class="line"><span class="ln">40</span><span class="cl">
</span></span><span class="line"><span class="ln">41</span><span class="cl">結果：拆分為 3 個 Level 2 Ticket</span></span></code></pre></div><h4 id="案例-4god-ticket-評估與拆分">案例 4：God Ticket 評估與拆分</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">任務：實作完整書籍評分功能（UI + UseCase + Repository）
</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">步驟 1：初步評估
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="k">-</span> 職責數量：8 個（UI + Controller + UseCase + Entity + Repository + Mapper + 測試 + 整合）
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">-</span> 程式碼行數：~300 行
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">-</span> 涉及檔案：8 個（跨 4 個架構層級）
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">-</span> 測試數量：20 個
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">步驟 2：複雜度確認
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">-</span> 職責：8 個 → Level 4
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">-</span> 行數：300 行 → Level 4
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">-</span> 檔案：8 個 → Level 4
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">-</span> 測試：20 個 → Level 4
</span></span><span class="line"><span class="ln">14</span><span class="cl">→ 最高等級：Level 4（所有指標都超標）
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">步驟 3：拆分決策
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">-</span> Level 4 → 必須拆分（God Ticket）
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">-</span> 拆分方式：使用 Clean Architecture 分層拆分策略
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl">  Ticket 1: Domain 層實作（Layer 5）
</span></span><span class="line"><span class="ln">21</span><span class="cl">  <span class="k">-</span> Rating Entity + Rating Value Object
</span></span><span class="line"><span class="ln">22</span><span class="cl">  <span class="k">-</span> 職責：2 個
</span></span><span class="line"><span class="ln">23</span><span class="cl">  <span class="k">-</span> 行數：~60 行
</span></span><span class="line"><span class="ln">24</span><span class="cl">  <span class="k">-</span> 檔案：2 個
</span></span><span class="line"><span class="ln">25</span><span class="cl">  <span class="k">-</span> 測試：6 個
</span></span><span class="line"><span class="ln">26</span><span class="cl">  → Level 2
</span></span><span class="line"><span class="ln">27</span><span class="cl">
</span></span><span class="line"><span class="ln">28</span><span class="cl">  Ticket 2: Repository 層實作（Layer 4-5 Interface + Infra）
</span></span><span class="line"><span class="ln">29</span><span class="cl">  <span class="k">-</span> IRatingRepository + SQLiteRatingRepository + Mapper
</span></span><span class="line"><span class="ln">30</span><span class="cl">  <span class="k">-</span> 職責：3 個
</span></span><span class="line"><span class="ln">31</span><span class="cl">  <span class="k">-</span> 行數：~90 行
</span></span><span class="line"><span class="ln">32</span><span class="cl">  <span class="k">-</span> 檔案：3 個
</span></span><span class="line"><span class="ln">33</span><span class="cl">  <span class="k">-</span> 測試：8 個
</span></span><span class="line"><span class="ln">34</span><span class="cl">  → Level 3（可接受）
</span></span><span class="line"><span class="ln">35</span><span class="cl">
</span></span><span class="line"><span class="ln">36</span><span class="cl">  Ticket 3: UseCase 層實作（Layer 3）
</span></span><span class="line"><span class="ln">37</span><span class="cl">  <span class="k">-</span> RateBookUseCase
</span></span><span class="line"><span class="ln">38</span><span class="cl">  <span class="k">-</span> 職責：2 個
</span></span><span class="line"><span class="ln">39</span><span class="cl">  <span class="k">-</span> 行數：~60 行
</span></span><span class="line"><span class="ln">40</span><span class="cl">  <span class="k">-</span> 檔案：1 個
</span></span><span class="line"><span class="ln">41</span><span class="cl">  <span class="k">-</span> 測試：4 個
</span></span><span class="line"><span class="ln">42</span><span class="cl">  → Level 2
</span></span><span class="line"><span class="ln">43</span><span class="cl">
</span></span><span class="line"><span class="ln">44</span><span class="cl">  Ticket 4: Presentation 層實作（Layer 1-2）
</span></span><span class="line"><span class="ln">45</span><span class="cl">  <span class="k">-</span> RatingWidget + RatingController
</span></span><span class="line"><span class="ln">46</span><span class="cl">  <span class="k">-</span> 職責：2 個
</span></span><span class="line"><span class="ln">47</span><span class="cl">  <span class="k">-</span> 行數：~90 行
</span></span><span class="line"><span class="ln">48</span><span class="cl">  <span class="k">-</span> 檔案：2 個
</span></span><span class="line"><span class="ln">49</span><span class="cl">  <span class="k">-</span> 測試：4 個（Widget 測試）
</span></span><span class="line"><span class="ln">50</span><span class="cl">  → Level 2
</span></span><span class="line"><span class="ln">51</span><span class="cl">
</span></span><span class="line"><span class="ln">52</span><span class="cl">結果：拆分為 4 個 Ticket（3 個 Level 2 + 1 個 Level 3）
</span></span><span class="line"><span class="ln">53</span><span class="cl">依賴順序：Ticket 1 → Ticket 2 → Ticket 3 → Ticket 4</span></span></code></pre></div><hr>
<h2 id="第三章clean-architecture-分層拆分策略">第三章：Clean Architecture 分層拆分策略</h2>
<h3 id="為什麼需要基於架構分層拆分">為什麼需要基於架構分層拆分</h3>
<p><strong>架構分層拆分的核心價值</strong>:</p>
<ol>
<li>
<p><strong>單層修改原則</strong>（Single Layer Modification Principle）</p>
<ul>
<li>每個 Ticket 專注於單一架構層級</li>
<li>降低跨層依賴帶來的複雜度</li>
<li>提升程式碼審查效率</li>
</ul>
</li>
<li>
<p>依賴方向一致性</p>
<ul>
<li>遵循 Clean Architecture 依賴規則（內層不依賴外層）</li>
<li>避免循環依賴</li>
<li>確保架構穩定性</li>
</ul>
</li>
<li>
<p>測試可獨立性</p>
<ul>
<li>每層有明確的測試策略</li>
<li>可獨立測試不依賴其他層</li>
<li>簡化 Mock 和 Stub</li>
</ul>
</li>
</ol>
<p><strong>本章內容結構</strong>:</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">3.1 Clean Architecture 五層架構回顧
</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">3.2 四種標準拆分策略
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    ├── 策略 1: Interface 定義 Ticket
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    ├── 策略 2: 具體實作 Ticket
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    ├── 策略 3: 測試驗證 Ticket
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    └── 策略 4: 整合連接 Ticket
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">3.3 分層拆分決策指引
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    └── 如何選擇合適的拆分策略
</span></span><span class="line"><span class="ln">10</span><span class="cl">3.4 分層拆分實務案例
</span></span><span class="line"><span class="ln">11</span><span class="cl">    └── 完整的書籍評分功能拆分範例</span></span></code></pre></div><hr>
<h3 id="31-clean-architecture-五層架構回顧">3.1 Clean Architecture 五層架構回顧</h3>
<p><strong>五層架構定義</strong>（引用自 Clean Architecture 實作方法論）:</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">Layer 1 (UI - 最外層)
</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">├── 路徑: lib/presentation/widgets/, lib/presentation/pages/
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">├── 依賴: Layer 2 (Behavior)
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">└── 不依賴: Layer 3-5
</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">Layer 2 (Behavior)
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">├── 職責: UI 行為控制（State Management）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">├── 路徑: lib/presentation/controllers/, lib/presentation/providers/
</span></span><span class="line"><span class="ln">10</span><span class="cl">├── 依賴: Layer 3 (UseCase)
</span></span><span class="line"><span class="ln">11</span><span class="cl">└── 不依賴: Layer 1, 4-5
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">Layer 3 (UseCase)
</span></span><span class="line"><span class="ln">14</span><span class="cl">├── 職責: 業務用例協調
</span></span><span class="line"><span class="ln">15</span><span class="cl">├── 路徑: lib/application/use_cases/, lib/application/services/
</span></span><span class="line"><span class="ln">16</span><span class="cl">├── 依賴: Layer 4-5 (Domain)
</span></span><span class="line"><span class="ln">17</span><span class="cl">└── 不依賴: Layer 1-2
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl">Layer 4 (Domain Events/Interfaces)
</span></span><span class="line"><span class="ln">20</span><span class="cl">├── 職責: 領域事件和介面定義
</span></span><span class="line"><span class="ln">21</span><span class="cl">├── 路徑: lib/domain/events/, lib/domain/repositories/ (介面)
</span></span><span class="line"><span class="ln">22</span><span class="cl">├── 依賴: Layer 5 (Domain Implementation)
</span></span><span class="line"><span class="ln">23</span><span class="cl">└── 不依賴: Layer 1-3
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span><span class="line"><span class="ln">25</span><span class="cl">Layer 5 (Domain Implementation - 最內層)
</span></span><span class="line"><span class="ln">26</span><span class="cl">├── 職責: 領域模型實作和基礎設施
</span></span><span class="line"><span class="ln">27</span><span class="cl">├── 路徑: lib/domain/entities/, lib/domain/value_objects/, lib/infrastructure/
</span></span><span class="line"><span class="ln">28</span><span class="cl">├── 依賴: 無（核心層）
</span></span><span class="line"><span class="ln">29</span><span class="cl">└── 不依賴: 任何層</span></span></code></pre></div><p><strong>依賴規則（Dependency Rule）</strong>:</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><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">
</span></span><span class="line"><span class="ln">6</span><span class="cl">- Layer 2 (Behavior) → Layer 3 (UseCase)
</span></span><span class="line"><span class="ln">7</span><span class="cl">- Layer 3 (UseCase) → Layer 2 (Behavior)</span></span></code></pre></div><p><strong>單層修改原則</strong>:</p>
<ul>
<li><strong>理想</strong>: 每個 Ticket 只修改單一層級</li>
<li><strong>可接受</strong>: Ticket 修改相鄰兩層（如 Interface + Implementation）</li>
<li><strong>禁止</strong>: Ticket 跨越超過 2 層（如 UI → Domain 直接跨越）</li>
</ul>
<hr>
<h3 id="32-四種標準拆分策略">3.2 四種標準拆分策略</h3>
<h4 id="策略-1interface-定義-ticket">策略 1：Interface 定義 Ticket</h4>
<p><strong>定義</strong>: 定義一個介面及其輸入輸出契約。</p>
<p><strong>適用層級</strong>: 主要用於 Layer 4 (Domain Interfaces)</p>
<p><strong>職責範圍</strong>:</p>
<ul>
<li>定義 Interface 簽名</li>
<li>定義輸入參數類型</li>
<li>定義回傳類型</li>
<li>撰寫文檔註解（含業務需求編號）</li>
</ul>
<p><strong>禁止包含</strong>:</p>
<ul>
<li>具體實作邏輯</li>
<li>資料庫操作</li>
<li>業務邏輯</li>
</ul>
<p><strong>Ticket 範本</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #NNN: 定義 {Interface 名稱} 介面
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 業務需求
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>[引用業務需求編號，如 REQ-001]
</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="gu">### 目標
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span>建立 <span class="sb">`{Interface 名稱}`</span> 介面，定義 {業務功能} 的契約
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 在 <span class="sb">`lib/domain/repositories/`</span> 建立 <span class="sb">`{interface_file}.dart`</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">2.</span> 定義 <span class="sb">`{method1}`</span> 方法簽名
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">3.</span> 定義 <span class="sb">`{method2}`</span> 方法簽名
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">4.</span> 撰寫文檔註解（含需求編號）
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> Interface 檔案建立在正確位置
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">- [ ]</span> 所有方法簽名完整且明確
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">- [ ]</span> 輸入輸出類型定義清楚
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">- [ ]</span> 包含完整的文檔註解（含需求編號）
</span></span><span class="line"><span class="ln">20</span><span class="cl">- [ ] dart analyze 0 錯誤</span></span></code></pre></div><p><strong>實務範例</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #101: 定義 IBookRepository 介面
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 業務需求
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>REQ-LIB-001: 書籍資料存取功能
</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="gu">### 目標
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span>建立 <span class="sb">`IBookRepository`</span> 介面，定義書籍資料存取的契約
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 在 <span class="sb">`lib/domain/repositories/`</span> 建立 <span class="sb">`i_book_repository.dart`</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">2.</span> 定義 <span class="sb">`getBookByIsbn`</span> 方法簽名
</span></span><span class="line"><span class="ln">12</span><span class="cl">   ```dart
</span></span><span class="line"><span class="ln">13</span><span class="cl">   /// [REQ-LIB-001.1] 根據 ISBN 查詢書籍
</span></span><span class="line"><span class="ln">14</span><span class="cl">   ///
</span></span><span class="line"><span class="ln">15</span><span class="cl">   /// 參數:
</span></span><span class="line"><span class="ln">16</span><span class="cl">   /// - isbn: 書籍 ISBN 編號
</span></span><span class="line"><span class="ln">17</span><span class="cl">   ///
</span></span><span class="line"><span class="ln">18</span><span class="cl">   /// 回傳:
</span></span><span class="line"><span class="ln">19</span><span class="cl">   /// - Book 物件（存在）或 null（不存在）
</span></span><span class="line"><span class="ln">20</span><span class="cl">   Future<span class="p">&lt;</span><span class="nt">Book</span><span class="err">?</span><span class="p">&gt;</span> getBookByIsbn(String isbn);</span></span></code></pre></div><ol start="3">
<li>定義 <code>saveBook</code> 方法簽名</li>
<li>定義 <code>deleteBook</code> 方法簽名</li>
<li>撰寫文檔註解</li>
</ol>
<h3 id="驗收條件">驗收條件</h3>
<ul>
<li><input disabled="" type="checkbox"> Interface 檔案建立在 <code>lib/domain/repositories/</code></li>
<li><input disabled="" type="checkbox"> 3 個方法簽名完整且明確</li>
<li><input disabled="" type="checkbox"> 輸入輸出類型定義清楚</li>
<li><input disabled="" type="checkbox"> 包含完整的文檔註解（含需求編號）</li>
<li><input disabled="" type="checkbox"> dart analyze 0 錯誤</li>
</ul>
<h3 id="指標評估">指標評估</h3>
<ul>
<li>職責: 1 個（定義介面）</li>
<li>行數: ~25 行</li>
<li>檔案: 1 個</li>
<li>測試: 0 個（Interface 不需單元測試）</li>
</ul>
<p>→ Level 1（簡單）</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><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">#### 策略 2：具體實作 Ticket
</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></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">**適用層級**:
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- Layer 5 (Domain Implementation): Entity, Value Object
</span></span><span class="line"><span class="ln">10</span><span class="cl">- Layer 5 (Infrastructure): Repository 實作, Service 實作
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">**職責範圍**:
</span></span><span class="line"><span class="ln">13</span><span class="cl">- 實作類別邏輯
</span></span><span class="line"><span class="ln">14</span><span class="cl">- 實現介面方法
</span></span><span class="line"><span class="ln">15</span><span class="cl">- 處理異常
</span></span><span class="line"><span class="ln">16</span><span class="cl">- 撰寫單元測試
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl">**禁止包含**:
</span></span><span class="line"><span class="ln">19</span><span class="cl">- UI 元件
</span></span><span class="line"><span class="ln">20</span><span class="cl">- 跨層整合（如直接呼叫 UseCase）
</span></span><span class="line"><span class="ln">21</span><span class="cl">- 測試以外的其他層修改
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl">**Ticket 範本**:
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span><span class="line"><span class="ln">25</span><span class="cl">```markdown
</span></span><span class="line"><span class="ln">26</span><span class="cl">## Ticket #NNN: 實作 {類別名稱}
</span></span><span class="line"><span class="ln">27</span><span class="cl">
</span></span><span class="line"><span class="ln">28</span><span class="cl">### 業務需求
</span></span><span class="line"><span class="ln">29</span><span class="cl">[引用業務需求編號]
</span></span><span class="line"><span class="ln">30</span><span class="cl">
</span></span><span class="line"><span class="ln">31</span><span class="cl">### 目標
</span></span><span class="line"><span class="ln">32</span><span class="cl">實作 `{類別名稱}`，提供 {業務功能} 的具體實現
</span></span><span class="line"><span class="ln">33</span><span class="cl">
</span></span><span class="line"><span class="ln">34</span><span class="cl">### 依賴 Ticket
</span></span><span class="line"><span class="ln">35</span><span class="cl">- Ticket #XXX: 定義 {Interface 名稱} 介面（必須先完成）
</span></span><span class="line"><span class="ln">36</span><span class="cl">
</span></span><span class="line"><span class="ln">37</span><span class="cl">### 步驟
</span></span><span class="line"><span class="ln">38</span><span class="cl">1. 建立 `{類別名稱}` 類別（實作 `{Interface}`）
</span></span><span class="line"><span class="ln">39</span><span class="cl">2. 實作 `{method1}` 方法
</span></span><span class="line"><span class="ln">40</span><span class="cl">3. 實作 `{method2}` 方法
</span></span><span class="line"><span class="ln">41</span><span class="cl">4. 處理異常情況
</span></span><span class="line"><span class="ln">42</span><span class="cl">5. 撰寫單元測試（正常流程 + 異常處理）
</span></span><span class="line"><span class="ln">43</span><span class="cl">6. 確保所有測試通過
</span></span><span class="line"><span class="ln">44</span><span class="cl">
</span></span><span class="line"><span class="ln">45</span><span class="cl">### 驗收條件
</span></span><span class="line"><span class="ln">46</span><span class="cl">- [ ] 實作所有 Interface 方法
</span></span><span class="line"><span class="ln">47</span><span class="cl">- [ ] 異常處理完整
</span></span><span class="line"><span class="ln">48</span><span class="cl">- [ ] 單元測試 100% 通過
</span></span><span class="line"><span class="ln">49</span><span class="cl">- [ ] 測試覆蓋正常流程和異常處理
</span></span><span class="line"><span class="ln">50</span><span class="cl">- [ ] dart analyze 0 錯誤</span></span></code></pre></div><p><strong>實務範例</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #102: 實作 SQLiteBookRepository
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 業務需求
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>REQ-LIB-001: 書籍資料存取功能
</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="gu">### 目標
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span>實作 <span class="sb">`SQLiteBookRepository`</span>，提供書籍資料的 SQLite 儲存
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">### 依賴 Ticket
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span><span class="k">-</span> Ticket <span class="ni">#101:</span> 定義 IBookRepository 介面（必須先完成）
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 在 <span class="sb">`lib/infrastructure/repositories/`</span> 建立 <span class="sb">`sqlite_book_repository.dart`</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">2.</span> 實作 <span class="sb">`getBookByIsbn`</span> 方法
</span></span><span class="line"><span class="ln">15</span><span class="cl">   <span class="k">-</span> SQL 查詢邏輯
</span></span><span class="line"><span class="ln">16</span><span class="cl">   <span class="k">-</span> Data Mapper 轉換（DTO → Entity）
</span></span><span class="line"><span class="ln">17</span><span class="cl">   <span class="k">-</span> 錯誤處理（Database Exception）
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">3.</span> 實作 <span class="sb">`saveBook`</span> 方法
</span></span><span class="line"><span class="ln">19</span><span class="cl">   <span class="k">-</span> SQL 插入/更新邏輯
</span></span><span class="line"><span class="ln">20</span><span class="cl">   <span class="k">-</span> Data Mapper 轉換（Entity → DTO）
</span></span><span class="line"><span class="ln">21</span><span class="cl">   <span class="k">-</span> 錯誤處理
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">4.</span> 實作 <span class="sb">`deleteBook`</span> 方法
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="k">5.</span> 撰寫單元測試
</span></span><span class="line"><span class="ln">24</span><span class="cl">   <span class="k">-</span> 正常流程：CRUD 操作成功
</span></span><span class="line"><span class="ln">25</span><span class="cl">   <span class="k">-</span> 異常處理：資料庫錯誤、資料不存在
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="k">6.</span> 確保所有測試通過
</span></span><span class="line"><span class="ln">27</span><span class="cl">
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 實作所有 IBookRepository 方法
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="k">- [ ]</span> 異常處理完整（DatabaseException, ValidationException）
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="k">- [ ]</span> 單元測試 100% 通過（至少 6 個測試案例）
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">33</span><span class="cl">
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="gu">### 指標評估
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="gu"></span><span class="k">-</span> 職責: 3 個（CRUD 實作、Mapper、異常處理）
</span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="k">-</span> 行數: ~80 行（含測試）
</span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="k">-</span> 檔案: 1 個
</span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="k">-</span> 測試: 6 個
</span></span><span class="line"><span class="ln">39</span><span class="cl">→ Level 2（中等）</span></span></code></pre></div><hr>
<h4 id="策略-3測試驗證-ticket">策略 3：測試驗證 Ticket</h4>
<p><strong>定義</strong>: 撰寫一組相關的測試用例，補強現有實作的測試覆蓋率。</p>
<p><strong>適用時機</strong>:</p>
<ul>
<li>現有實作缺乏完整測試</li>
<li>需要補充邊界測試和異常測試</li>
<li>TDD 紅綠燈循環中的「紅燈」階段</li>
</ul>
<p><strong>職責範圍</strong>:</p>
<ul>
<li>撰寫單元測試</li>
<li>覆蓋正常流程</li>
<li>覆蓋邊界條件</li>
<li>覆蓋異常處理</li>
<li>確保測試通過</li>
</ul>
<p><strong>禁止包含</strong>:</p>
<ul>
<li>修改生產程式碼（除非是修正測試發現的 Bug）</li>
<li>新增功能</li>
<li>重構（測試 Ticket 專注於驗證，不做重構）</li>
</ul>
<p><strong>Ticket 範本</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #NNN: 撰寫 {功能} 測試
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 業務需求
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></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="gu">### 目標
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span>撰寫 <span class="sb">`{類別名稱}`</span> 的完整測試用例，確保 {業務功能} 正確性
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">### 依賴 Ticket
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span><span class="k">-</span> Ticket <span class="ni">#XXX:</span> 實作 {類別名稱}（必須先完成）
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 建立測試檔案 <span class="sb">`{class}_test.dart`</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">2.</span> 撰寫正常流程測試
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">3.</span> 撰寫邊界條件測試
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">4.</span> 撰寫異常處理測試
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">5.</span> 確保所有測試通過
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 測試檔案建立在正確位置
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">- [ ]</span> 至少 N 個測試用例
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">- [ ]</span> 覆蓋正常流程、邊界條件和異常處理
</span></span><span class="line"><span class="ln">23</span><span class="cl">- [ ] 所有測試 100% 通過</span></span></code></pre></div><p><strong>實務範例</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #103: 撰寫 BookRepository 整合測試
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 業務需求
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>REQ-LIB-001: 書籍資料存取功能
</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="gu">### 目標
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span>撰寫 <span class="sb">`BookRepository`</span> 的整合測試，驗證資料庫操作正確性
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">### 依賴 Ticket
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span><span class="k">-</span> Ticket <span class="ni">#102:</span> 實作 SQLiteBookRepository（必須先完成）
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 建立測試檔案 <span class="sb">`test/integration/repositories/book_repository_test.dart`</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">2.</span> 撰寫 <span class="sb">`getBookByIsbn`</span> 測試
</span></span><span class="line"><span class="ln">15</span><span class="cl">   <span class="k">-</span> 測試 1: 成功取得存在的書籍
</span></span><span class="line"><span class="ln">16</span><span class="cl">   <span class="k">-</span> 測試 2: 書籍不存在回傳 null
</span></span><span class="line"><span class="ln">17</span><span class="cl">   <span class="k">-</span> 測試 3: 無效 ISBN 拋出異常
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">3.</span> 撰寫 <span class="sb">`saveBook`</span> 測試
</span></span><span class="line"><span class="ln">19</span><span class="cl">   <span class="k">-</span> 測試 4: 新增書籍成功
</span></span><span class="line"><span class="ln">20</span><span class="cl">   <span class="k">-</span> 測試 5: 更新現有書籍成功
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">4.</span> 撰寫 <span class="sb">`deleteBook`</span> 測試
</span></span><span class="line"><span class="ln">22</span><span class="cl">   <span class="k">-</span> 測試 6: 刪除存在的書籍
</span></span><span class="line"><span class="ln">23</span><span class="cl">   <span class="k">-</span> 測試 7: 刪除不存在的書籍無異常
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="k">5.</span> 確保所有測試通過
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 測試檔案建立在 <span class="sb">`test/integration/repositories/`</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="k">- [ ]</span> 至少 7 個整合測試案例
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="k">- [ ]</span> 覆蓋正常流程和異常處理
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="k">- [ ]</span> 所有測試 100% 通過
</span></span><span class="line"><span class="ln">31</span><span class="cl">
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="gu">### 指標評估
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="gu"></span><span class="k">-</span> 職責: 1 個（撰寫測試）
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="k">-</span> 行數: ~60 行（純測試）
</span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="k">-</span> 檔案: 1 個（測試檔案）
</span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="k">-</span> 測試: 7 個
</span></span><span class="line"><span class="ln">37</span><span class="cl">→ Level 2（中等）</span></span></code></pre></div><hr>
<h4 id="策略-4整合連接-ticket">策略 4：整合連接 Ticket</h4>
<p><strong>定義</strong>: 連接兩個模組並驗證整合，實現端到端流程。</p>
<p><strong>適用時機</strong>:</p>
<ul>
<li>需要連接 Layer 3 (UseCase) 和 Layer 5 (Repository)</li>
<li>需要連接 Layer 2 (Controller) 和 Layer 3 (UseCase)</li>
<li>完成分層實作後的整合階段</li>
</ul>
<p><strong>職責範圍</strong>:</p>
<ul>
<li>連接 Use Case 和 Repository</li>
<li>實作依賴注入</li>
<li>撰寫整合測試</li>
<li>驗證端到端流程</li>
</ul>
<p><strong>禁止包含</strong>:</p>
<ul>
<li>修改核心業務邏輯（應在具體實作 Ticket 完成）</li>
<li>跨越超過 2 層的整合</li>
<li>UI 實作（應獨立為 Presentation 層 Ticket）</li>
</ul>
<p><strong>Ticket 範本</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #NNN: 整合 {UseCase} 到 {Repository}
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 業務需求
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></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="gu">### 目標
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span>將 <span class="sb">`{Repository}`</span> 整合到 <span class="sb">`{UseCase}`</span>，實現 {業務功能} 完整流程
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">### 依賴 Ticket
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span><span class="k">-</span> Ticket <span class="ni">#XXX:</span> 定義 {Interface}（必須先完成）
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">-</span> Ticket <span class="ni">#YYY:</span> 實作 {Repository}（必須先完成）
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 修改 <span class="sb">`{UseCase}`</span> 注入 <span class="sb">`{Interface}`</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">2.</span> 在 <span class="sb">`execute`</span> 方法中呼叫 Repository 方法
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">3.</span> 處理 Repository 異常
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">4.</span> 撰寫整合測試驗證端到端流程
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">5.</span> 確保所有測試通過
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> <span class="sb">`{UseCase}`</span> 正確注入 <span class="sb">`{Interface}`</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">- [ ]</span> 端到端流程正常運作
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="k">- [ ]</span> 整合測試 100% 通過
</span></span><span class="line"><span class="ln">24</span><span class="cl">- [ ] 異常處理完整</span></span></code></pre></div><p><strong>實務範例</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #104: 整合 BookRepository 到 GetBookUseCase
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 業務需求
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>REQ-LIB-001: 書籍查詢功能
</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="gu">### 目標
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span>將 <span class="sb">`BookRepository`</span> 整合到 <span class="sb">`GetBookUseCase`</span>，實現完整書籍查詢流程
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">### 依賴 Ticket
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span><span class="k">-</span> Ticket <span class="ni">#101:</span> 定義 IBookRepository 介面（必須先完成）
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">-</span> Ticket <span class="ni">#102:</span> 實作 SQLiteBookRepository（必須先完成）
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 修改 <span class="sb">`GetBookInteractor`</span> 注入 <span class="sb">`IBookRepository`</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">   ```dart
</span></span><span class="line"><span class="ln">16</span><span class="cl">   class GetBookInteractor implements GetBookUseCase {
</span></span><span class="line"><span class="ln">17</span><span class="cl">     final IBookRepository _repository;
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl">     GetBookInteractor(this._repository);
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl">     <span class="ni">@override</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">     Future<span class="p">&lt;</span><span class="nt">Book</span><span class="err">?</span><span class="p">&gt;</span> execute(String isbn) async {
</span></span><span class="line"><span class="ln">23</span><span class="cl">       // 呼叫 Repository
</span></span><span class="line"><span class="ln">24</span><span class="cl">     }
</span></span><span class="line"><span class="ln">25</span><span class="cl">   }</span></span></code></pre></div><ol start="2">
<li>在 <code>execute</code> 方法中呼叫 <code>repository.getBookByIsbn</code></li>
<li>處理 Repository 異常（ValidationException, StorageException）</li>
<li>撰寫整合測試
<ul>
<li>測試 1: 成功查詢書籍</li>
<li>測試 2: 書籍不存在</li>
<li>測試 3: Repository 異常處理</li>
</ul>
</li>
<li>確保所有測試通過</li>
</ol>
<h3 id="驗收條件整合應用案例">驗收條件（整合應用案例）</h3>
<ul>
<li><input disabled="" type="checkbox"> <code>GetBookInteractor</code> 正確注入 <code>IBookRepository</code></li>
<li><input disabled="" type="checkbox"> 端到端流程正常運作</li>
<li><input disabled="" type="checkbox"> 整合測試 100% 通過（至少 3 個測試案例）</li>
<li><input disabled="" type="checkbox"> 異常處理完整</li>
</ul>
<h3 id="指標評估整合應用案例">指標評估（整合應用案例）</h3>
<ul>
<li>職責: 2 個（注入、整合）</li>
<li>行數: ~50 行（含測試）</li>
<li>檔案: 1 個（修改 UseCase）</li>
<li>測試: 3 個</li>
</ul>
<p>→ Level 2（中等）</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><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">### 3.3 分層拆分決策指引
</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></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">```text
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">[分析 Ticket 涉及的架構層級]
</span></span><span class="line"><span class="ln">10</span><span class="cl">    ↓
</span></span><span class="line"><span class="ln">11</span><span class="cl">    ├─ 單層修改？
</span></span><span class="line"><span class="ln">12</span><span class="cl">    │   ├─ Yes → 選擇對應策略
</span></span><span class="line"><span class="ln">13</span><span class="cl">    │   │   ├─ Layer 4 Interface → 策略 1: Interface 定義
</span></span><span class="line"><span class="ln">14</span><span class="cl">    │   │   ├─ Layer 5 Implementation → 策略 2: 具體實作
</span></span><span class="line"><span class="ln">15</span><span class="cl">    │   │   └─ 補充測試 → 策略 3: 測試驗證
</span></span><span class="line"><span class="ln">16</span><span class="cl">    │   │
</span></span><span class="line"><span class="ln">17</span><span class="cl">    │   └─ No → 跨層修改？
</span></span><span class="line"><span class="ln">18</span><span class="cl">    │       ├─ 相鄰兩層整合 → 策略 4: 整合連接
</span></span><span class="line"><span class="ln">19</span><span class="cl">    │       └─ 跨越超過 2 層 → 必須拆分為多個 Ticket</span></span></code></pre></div><p><strong>決策準則</strong>:</p>
<h4 id="準則-1-優先單層修改">準則 1: 優先單層修改</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">問題: Ticket 是否只修改單一架構層級？
</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 class="k">-</span> Yes → 選擇策略 1-3（Interface / 實作 / 測試）
</span></span><span class="line"><span class="ln">4</span><span class="cl">- No → 評估是否可拆分為多個單層 Ticket</span></span></code></pre></div><h5 id="準則-2-相鄰層整合可接受">準則 2: 相鄰層整合可接受</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">問題: 是否為相鄰兩層的整合？
</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"><span class="k">-</span> Layer 3 (UseCase) + Layer 4-5 (Repository) 可接受
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">-</span> Layer 2 (Controller) + Layer 3 (UseCase) 可接受
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">-</span> Layer 1 (UI) + Layer 5 (Domain) 禁止（跨越太多層）
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">決策：
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">-</span> 相鄰兩層整合 → 策略 4: 整合連接
</span></span><span class="line"><span class="ln">12</span><span class="cl">- 跨越超過 2 層 → 必須拆分為多個 Ticket</span></span></code></pre></div><h6 id="準則-3-interface-先行">準則 3: Interface 先行</h6>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">原則: Interface 定義必須先於實作
</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 class="k">1.</span> Ticket A: 定義 IBookRepository（策略 1）
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="k">2.</span> Ticket B: 實作 SQLiteBookRepository（策略 2）
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="k">3.</span> Ticket C: 整合到 GetBookUseCase（策略 4）
</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">依賴關係: Ticket C 依賴 B，B 依賴 A</span></span></code></pre></div><h6 id="準則-4-測試獨立或整合">準則 4: 測試獨立或整合</h6>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">問題: 測試應該獨立 Ticket 還是整合到實作 Ticket？
</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"><span class="k">-</span> 實作 Ticket <span class="p">&lt;</span> <span class="nt">Level</span> <span class="na">2</span> <span class="err">→</span> <span class="na">整合測試到實作</span> <span class="na">Ticket</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="k">-</span> <span class="na">實作</span> <span class="na">Ticket </span><span class="o">=</span> <span class="s">Level</span> <span class="na">2-3</span> <span class="err">→</span> <span class="na">可選擇獨立測試</span> <span class="na">Ticket</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="na">-</span> <span class="na">測試案例</span> <span class="p">&gt;</span> 10 個 → 必須獨立測試 Ticket（策略 3）</span></span></code></pre></div><hr>
<h3 id="34-分層拆分實務案例">3.4 分層拆分實務案例</h3>
<h4 id="完整案例書籍評分功能實作">完整案例：書籍評分功能實作</h4>
<h4 id="原始-god-ticket-分析">原始 God Ticket 分析</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">原始任務: 實作完整書籍評分功能（UI + UseCase + Repository）
</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 class="k">-</span> 職責: 8 個
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="k">-</span> 行數: ~300 行
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="k">-</span> 檔案: 8 個
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="k">-</span> 測試: 20 個
</span></span><span class="line"><span class="ln">8</span><span class="cl">→ Level 4（所有指標都超標）必須拆分</span></span></code></pre></div><h4 id="分層拆分方案">分層拆分方案</h4>
<p><strong>拆分為 6 個 Ticket（按 Clean Architecture 分層）</strong>:</p>
<hr>
<h5 id="ticket-1-定義-rating-domain-模型layer-5---domain-implementation">Ticket 1: 定義 Rating Domain 模型（Layer 5 - Domain Implementation）</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #201: 定義 Rating Value Object
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 層級: Layer 5 (Domain Implementation)
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu">### 策略: 策略 2（具體實作）
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="gu">### 業務需求
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span>REQ-RATING-001: 書籍評分功能
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">### 目標
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span>建立 <span class="sb">`Rating`</span> Value Object，封裝評分規則（1-5 分）
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 建立 <span class="sb">`lib/domain/value_objects/rating.dart`</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">2.</span> 實作 Rating 類別
</span></span><span class="line"><span class="ln">15</span><span class="cl">   <span class="k">-</span> 建構子驗證（1-5 分）
</span></span><span class="line"><span class="ln">16</span><span class="cl">   <span class="k">-</span> equals / hashCode
</span></span><span class="line"><span class="ln">17</span><span class="cl">   <span class="k">-</span> toString
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">3.</span> 撰寫單元測試（正常、邊界、異常）
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">4.</span> 確保測試通過
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> Rating 類別實作完整
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="k">- [ ]</span> 驗證邏輯正確（1-5 分）
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="k">- [ ]</span> 單元測試 100% 通過（至少 5 個測試）
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="gu">### 指標評估
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="gu"></span><span class="k">-</span> 職責: 1 個
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="k">-</span> 行數: ~50 行
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="k">-</span> 檔案: 1 個
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="k">-</span> 測試: 5 個
</span></span><span class="line"><span class="ln">32</span><span class="cl">→ Level 2（中等）</span></span></code></pre></div><hr>
<h4 id="ticket-2-定義-rating-entitylayer-5---domain-implementation">Ticket 2: 定義 Rating Entity（Layer 5 - Domain Implementation）</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #202: 定義 Rating Entity
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 層級: Layer 5 (Domain Implementation)
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu">### 策略: 策略 2（具體實作）
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gu">### 依賴: Ticket #201（Rating Value Object）
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu">### 業務需求
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="gu"></span>REQ-RATING-001: 書籍評分功能
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu">### 目標
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu"></span>建立 <span class="sb">`Rating`</span> Entity，包含評分和評論資訊
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 建立 <span class="sb">`lib/domain/entities/rating.dart`</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">2.</span> 實作 Rating Entity
</span></span><span class="line"><span class="ln">16</span><span class="cl">   <span class="k">-</span> 欄位: ratingValue (Rating VO), comment (String), userId, bookIsbn
</span></span><span class="line"><span class="ln">17</span><span class="cl">   <span class="k">-</span> equals / hashCode
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">3.</span> 撰寫單元測試
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">4.</span> 確保測試通過
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> Rating Entity 實作完整
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="k">- [ ]</span> 使用 Rating Value Object
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="k">- [ ]</span> 單元測試 100% 通過（至少 3 個測試）
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="gu">### 指標評估
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="gu"></span><span class="k">-</span> 職責: 1 個
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="k">-</span> 行數: ~40 行
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="k">-</span> 檔案: 1 個
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="k">-</span> 測試: 3 個
</span></span><span class="line"><span class="ln">32</span><span class="cl">→ Level 1（簡單）</span></span></code></pre></div><hr>
<h4 id="ticket-3-定義-iratingrepository-介面layer-4---domain-interfaces">Ticket 3: 定義 IRatingRepository 介面（Layer 4 - Domain Interfaces）</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #203: 定義 IRatingRepository 介面
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 層級: Layer 4 (Domain Interfaces)
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu">### 策略: 策略 1（Interface 定義）
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="gu">### 業務需求
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span>REQ-RATING-001: 書籍評分功能
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">### 目標
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span>建立 <span class="sb">`IRatingRepository`</span> 介面，定義評分資料存取契約
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 建立 <span class="sb">`lib/domain/repositories/i_rating_repository.dart`</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">2.</span> 定義 <span class="sb">`saveRating`</span> 方法簽名
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">3.</span> 定義 <span class="sb">`getRatingsByBookIsbn`</span> 方法簽名
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">4.</span> 撰寫文檔註解
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> Interface 檔案建立在正確位置
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">- [ ]</span> 2 個方法簽名完整
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">- [ ]</span> 文檔註解包含需求編號
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="gu">### 指標評估
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="gu"></span><span class="k">-</span> 職責: 1 個
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="k">-</span> 行數: ~20 行
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="k">-</span> 檔案: 1 個
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="k">-</span> 測試: 0 個
</span></span><span class="line"><span class="ln">29</span><span class="cl">→ Level 1（簡單）</span></span></code></pre></div><hr>
<h4 id="ticket-4-實作-sqliteratingrepositorylayer-5---infrastructure">Ticket 4: 實作 SQLiteRatingRepository（Layer 5 - Infrastructure）</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #204: 實作 SQLiteRatingRepository
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 層級: Layer 5 (Infrastructure)
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu">### 策略: 策略 2（具體實作）
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gu">### 依賴: Ticket #202, #203
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu">### 業務需求
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="gu"></span>REQ-RATING-001: 書籍評分功能
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu">### 目標
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu"></span>實作 <span class="sb">`SQLiteRatingRepository`</span>，提供評分資料的 SQLite 儲存
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 建立 <span class="sb">`lib/infrastructure/repositories/sqlite_rating_repository.dart`</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">2.</span> 實作 <span class="sb">`saveRating`</span> 方法
</span></span><span class="line"><span class="ln">16</span><span class="cl">   <span class="k">-</span> SQL 插入邏輯
</span></span><span class="line"><span class="ln">17</span><span class="cl">   <span class="k">-</span> Data Mapper 轉換
</span></span><span class="line"><span class="ln">18</span><span class="cl">   <span class="k">-</span> 錯誤處理
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">3.</span> 實作 <span class="sb">`getRatingsByBookIsbn`</span> 方法
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">4.</span> 撰寫單元測試（CRUD + 異常）
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">5.</span> 確保測試通過
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 實作所有 IRatingRepository 方法
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="k">- [ ]</span> 異常處理完整
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="k">- [ ]</span> 單元測試 100% 通過（至少 6 個測試）
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">28</span><span class="cl">
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="gu">### 指標評估
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="gu"></span><span class="k">-</span> 職責: 3 個（CRUD、Mapper、異常）
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="k">-</span> 行數: ~90 行
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="k">-</span> 檔案: 1 個
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="k">-</span> 測試: 6 個
</span></span><span class="line"><span class="ln">34</span><span class="cl">→ Level 3（複雜）可接受</span></span></code></pre></div><hr>
<h4 id="ticket-5-實作-ratebookusecaselayer-3---usecase">Ticket 5: 實作 RateBookUseCase（Layer 3 - UseCase）</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #205: 實作 RateBookUseCase
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 層級: Layer 3 (UseCase)
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu">### 策略: 策略 4（整合連接）
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gu">### 依賴: Ticket #203, #204
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu">### 業務需求
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="gu"></span>REQ-RATING-001: 書籍評分功能
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu">### 目標
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu"></span>實作 <span class="sb">`RateBookUseCase`</span>，協調書籍評分業務流程
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 建立 <span class="sb">`lib/application/use_cases/rate_book_use_case.dart`</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">2.</span> 注入 <span class="sb">`IRatingRepository`</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">3.</span> 實作 <span class="sb">`execute`</span> 方法
</span></span><span class="line"><span class="ln">17</span><span class="cl">   <span class="k">-</span> 建立 Rating Entity
</span></span><span class="line"><span class="ln">18</span><span class="cl">   <span class="k">-</span> 呼叫 Repository 儲存
</span></span><span class="line"><span class="ln">19</span><span class="cl">   <span class="k">-</span> 處理異常
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">4.</span> 撰寫整合測試
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">5.</span> 確保測試通過
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> UseCase 正確注入 IRatingRepository
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="k">- [ ]</span> 業務流程完整
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="k">- [ ]</span> 整合測試 100% 通過（至少 4 個測試）
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="k">- [ ]</span> 異常處理完整
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">29</span><span class="cl">
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="gu">### 指標評估
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="gu"></span><span class="k">-</span> 職責: 2 個（協調、整合）
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="k">-</span> 行數: ~60 行
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="k">-</span> 檔案: 1 個
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="k">-</span> 測試: 4 個
</span></span><span class="line"><span class="ln">35</span><span class="cl">→ Level 2（中等）</span></span></code></pre></div><hr>
<h4 id="ticket-6-實作-ratingwidget-uilayer-1-2---presentation">Ticket 6: 實作 RatingWidget UI（Layer 1-2 - Presentation）</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #206: 實作 RatingWidget 和 RatingController
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 層級: Layer 1 (UI) + Layer 2 (Behavior)
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu">### 策略: 策略 2 + 策略 4（實作 + 整合）
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gu">### 依賴: Ticket #205
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu">### 業務需求
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="gu"></span>REQ-RATING-001: 書籍評分功能
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu">### 目標
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu"></span>實作評分 UI 元件和行為控制
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 建立 <span class="sb">`lib/presentation/widgets/rating_widget.dart`</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">2.</span> 實作 RatingWidget（5 星評分 UI）
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">3.</span> 建立 <span class="sb">`lib/presentation/controllers/rating_controller.dart`</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">4.</span> 實作 RatingController
</span></span><span class="line"><span class="ln">18</span><span class="cl">   <span class="k">-</span> 注入 RateBookUseCase
</span></span><span class="line"><span class="ln">19</span><span class="cl">   <span class="k">-</span> 呼叫 UseCase 評分
</span></span><span class="line"><span class="ln">20</span><span class="cl">   <span class="k">-</span> 狀態管理
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">5.</span> 撰寫 Widget 測試
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">6.</span> 確保測試通過
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> RatingWidget UI 正確顯示
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="k">- [ ]</span> RatingController 正確整合 UseCase
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="k">- [ ]</span> Widget 測試 100% 通過（至少 4 個測試）
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">29</span><span class="cl">
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="gu">### 指標評估
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="gu"></span><span class="k">-</span> 職責: 2 個（UI、Controller）
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="k">-</span> 行數: ~80 行
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="k">-</span> 檔案: 2 個
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="k">-</span> 測試: 4 個
</span></span><span class="line"><span class="ln">35</span><span class="cl">→ Level 2（中等）</span></span></code></pre></div><hr>
<h4 id="拆分結果總結">拆分結果總結</h4>
<p><strong>拆分前後對比</strong>:</p>
<table>
  <thead>
      <tr>
          <th>項目</th>
          <th>拆分前（God Ticket）</th>
          <th>拆分後（6 個 Ticket）</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>職責數量</td>
          <td>8 個</td>
          <td>平均 1.7 個</td>
      </tr>
      <tr>
          <td>程式碼行數</td>
          <td>~300 行</td>
          <td>平均 57 行</td>
      </tr>
      <tr>
          <td>檔案數量</td>
          <td>8 個</td>
          <td>平均 1.2 個</td>
      </tr>
      <tr>
          <td>測試數量</td>
          <td>20 個</td>
          <td>平均 4 個</td>
      </tr>
      <tr>
          <td>複雜度等級</td>
          <td>Level 4</td>
          <td>5 個 Level 1-2<br>1 個 Level 3</td>
      </tr>
  </tbody>
</table>
<p><strong>拆分效益</strong>:</p>
<ul>
<li><strong>風險降低</strong>: 從 1 個高風險任務 → 6 個低風險任務</li>
<li><strong>並行開發</strong>: 可 2-3 人同時開發不同層級</li>
<li><strong>易於 Review</strong>: 每個 PR 範圍小，Review 時間縮短</li>
<li><strong>依賴明確</strong>: 清楚的 Ticket 依賴順序</li>
<li><strong>測試獨立</strong>: 每層可獨立測試，Mock 簡單</li>
</ul>
<p><strong>執行順序</strong>:</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">Phase 1（可並行）:
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">├─ Ticket #201: Rating Value Object
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">└─ Ticket #202: Rating Entity （依賴 #201）
</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">Phase 2（可並行）:
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">├─ Ticket #203: IRatingRepository Interface
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">└─ Ticket #204: SQLiteRatingRepository （依賴 #202, #203）
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">Phase 3:
</span></span><span class="line"><span class="ln">10</span><span class="cl">└─ Ticket #205: RateBookUseCase （依賴 #204）
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">Phase 4:
</span></span><span class="line"><span class="ln">13</span><span class="cl">└─ Ticket #206: RatingWidget UI （依賴 #205）</span></span></code></pre></div><hr>
<h2 id="第四章ticket-大小標準與範例">第四章：Ticket 大小標準與範例</h2>
<h3 id="41-簡單-ticket-標準與範例level-1">4.1 簡單 Ticket 標準與範例（Level 1）</h3>
<p><strong>Level 1 特徵</strong>：</p>
<ul>
<li>職責：1 個明確職責</li>
<li>行數：&lt; 30 行</li>
<li>檔案：1 個</li>
<li>測試：1-3 個</li>
</ul>
<p><strong>適用場景</strong>：</p>
<ul>
<li>Interface 定義</li>
<li>單一 Value Object</li>
<li>單一方法實作</li>
<li>簡單配置修改</li>
</ul>
<hr>
<h4 id="範例-1定義-ibookrepository-介面">範例 1：定義 IBookRepository 介面</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #1: 定義 IBookRepository 介面
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 層級：Layer 4 (Domain Interfaces)
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gu">### 業務需求
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="gu"></span>REQ-LIB-001: 書籍資料存取功能
</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"><span class="gu">### 目標
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu"></span>建立 <span class="sb">`IBookRepository`</span> 介面，定義書籍資料存取的契約
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 在 <span class="sb">`lib/domain/repositories/`</span> 建立 <span class="sb">`i_book_repository.dart`</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">2.</span> 定義 <span class="sb">`getBookByIsbn(String isbn)`</span> 方法簽名
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">3.</span> 定義 <span class="sb">`saveBook(Book book)`</span> 方法簽名
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">4.</span> 定義 <span class="sb">`deleteBook(String isbn)`</span> 方法簽名
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">5.</span> 撰寫文檔註解（含需求編號）
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> Interface 檔案建立在 <span class="sb">`lib/domain/repositories/`</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">- [ ]</span> 3 個方法簽名完整且明確
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">- [ ]</span> 輸入輸出類型定義清楚
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">- [ ]</span> 文檔註解包含需求編號 REQ-LIB-001
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="gu">### 指標評估
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="gu"></span><span class="k">-</span> 職責數量：1 個（定義介面契約）
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="k">-</span> 程式碼行數：~20 行
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="k">-</span> 涉及檔案：1 個
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="k">-</span> 測試用例：0 個（Interface 不需單元測試）
</span></span><span class="line"><span class="ln">30</span><span class="cl">→ Level 1（簡單）
</span></span><span class="line"><span class="ln">31</span><span class="cl">
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="gu">### 預估時間
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="gu"></span>10-15 分鐘</span></span></code></pre></div><hr>
<h4 id="範例-2建立-rating-value-object">範例 2：建立 Rating Value Object</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #2: 建立 Rating Value Object
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 層級：Layer 5 (Domain Implementation)
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gu">### 業務需求
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="gu"></span>REQ-RATING-001: 書籍評分功能
</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"><span class="gu">### 目標
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu"></span>建立 <span class="sb">`Rating`</span> Value Object，封裝評分規則（1-5 分）
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 在 <span class="sb">`lib/domain/value_objects/`</span> 建立 <span class="sb">`rating.dart`</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">2.</span> 實作 Rating 類別
</span></span><span class="line"><span class="ln">14</span><span class="cl">   <span class="k">-</span> 建構子驗證（1-5 分範圍）
</span></span><span class="line"><span class="ln">15</span><span class="cl">   <span class="k">-</span> equals / hashCode
</span></span><span class="line"><span class="ln">16</span><span class="cl">   <span class="k">-</span> toString
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">3.</span> 撰寫單元測試
</span></span><span class="line"><span class="ln">18</span><span class="cl">   <span class="k">-</span> 測試 1：建立有效評分
</span></span><span class="line"><span class="ln">19</span><span class="cl">   <span class="k">-</span> 測試 2：評分過低拋出異常
</span></span><span class="line"><span class="ln">20</span><span class="cl">   <span class="k">-</span> 測試 3：評分過高拋出異常
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">4.</span> 確保所有測試通過
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> Rating 類別實作完整
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="k">- [ ]</span> 驗證邏輯正確（1-5 分）
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="k">- [ ]</span> equals / hashCode / toString 實作正確
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="k">- [ ]</span> 單元測試 100% 通過（至少 3 個測試）
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">29</span><span class="cl">
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="gu">### 指標評估
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="gu"></span><span class="k">-</span> 職責數量：1 個（實作 Value Object）
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="k">-</span> 程式碼行數：~25 行（含測試）
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="k">-</span> 涉及檔案：1 個
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="k">-</span> 測試用例：3 個
</span></span><span class="line"><span class="ln">35</span><span class="cl">→ Level 1（簡單）
</span></span><span class="line"><span class="ln">36</span><span class="cl">
</span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="gu">### 預估時間
</span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="gu"></span>15-20 分鐘</span></span></code></pre></div><hr>
<h3 id="42-中等-ticket-標準與範例level-2">4.2 中等 Ticket 標準與範例（Level 2）</h3>
<p><strong>Level 2 特徵</strong>：</p>
<ul>
<li>職責：2-3 個相關職責</li>
<li>行數：30-70 行</li>
<li>檔案：2-3 個</li>
<li>測試：3-6 個</li>
</ul>
<p><strong>適用場景</strong>：</p>
<ul>
<li>含業務邏輯的 Entity</li>
<li>基礎 Repository 方法</li>
<li>簡單 UseCase</li>
<li>單一功能的 Controller</li>
</ul>
<hr>
<h4 id="範例-3實作-book-entity">範例 3：實作 Book Entity</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #3: 實作 Book Entity
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 層級：Layer 5 (Domain Implementation)
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gu">### 業務需求
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="gu"></span>REQ-LIB-001: 書籍資料模型
</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"><span class="gu">### 目標
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu"></span>建立 <span class="sb">`Book`</span> Entity，封裝書籍核心資料
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 在 <span class="sb">`lib/domain/entities/`</span> 建立 <span class="sb">`book.dart`</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">2.</span> 定義 Entity 欄位
</span></span><span class="line"><span class="ln">14</span><span class="cl">   <span class="k">-</span> isbn (String, required)
</span></span><span class="line"><span class="ln">15</span><span class="cl">   <span class="k">-</span> title (String, required)
</span></span><span class="line"><span class="ln">16</span><span class="cl">   <span class="k">-</span> author (String, required)
</span></span><span class="line"><span class="ln">17</span><span class="cl">   <span class="k">-</span> publishDate (DateTime, optional)
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">3.</span> 實作 equals / hashCode（基於 isbn）
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">4.</span> 實作 toString
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">5.</span> 撰寫單元測試
</span></span><span class="line"><span class="ln">21</span><span class="cl">   <span class="k">-</span> 測試 1：建立有效 Book
</span></span><span class="line"><span class="ln">22</span><span class="cl">   <span class="k">-</span> 測試 2：equals 正確比較（相同 ISBN）
</span></span><span class="line"><span class="ln">23</span><span class="cl">   <span class="k">-</span> 測試 3：equals 正確比較（不同 ISBN）
</span></span><span class="line"><span class="ln">24</span><span class="cl">   <span class="k">-</span> 測試 4：hashCode 一致性
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="k">6.</span> 確保測試通過
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> Book Entity 欄位定義完整
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="k">- [ ]</span> equals / hashCode 實作正確（基於 isbn）
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="k">- [ ]</span> toString 回傳清楚的字串表示
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="k">- [ ]</span> 單元測試 100% 通過（至少 4 個測試）
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">33</span><span class="cl">
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="gu">### 指標評估
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="gu"></span><span class="k">-</span> 職責數量：3 個（欄位定義、equals/hashCode、toString）
</span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="k">-</span> 程式碼行數：~50 行（含測試）
</span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="k">-</span> 涉及檔案：1 個
</span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="k">-</span> 測試用例：4 個
</span></span><span class="line"><span class="ln">39</span><span class="cl">→ Level 2（中等）
</span></span><span class="line"><span class="ln">40</span><span class="cl">
</span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="gu">### 預估時間
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="gu"></span>25-35 分鐘</span></span></code></pre></div><hr>
<h4 id="範例-4實作-getbookusecase">範例 4：實作 GetBookUseCase</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #4: 實作 GetBookUseCase
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 層級：Layer 3 (UseCase)
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gu">### 業務需求
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="gu"></span>REQ-LIB-001: 查詢書籍功能
</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"><span class="gu">### 目標
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu"></span>實作 <span class="sb">`GetBookUseCase`</span>，協調書籍查詢業務流程
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu">### 依賴 Ticket
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu"></span><span class="k">-</span> Ticket <span class="ni">#1:</span> 定義 IBookRepository 介面（必須先完成）
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 在 <span class="sb">`lib/application/use_cases/`</span> 建立 <span class="sb">`get_book_use_case.dart`</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">2.</span> 定義 <span class="sb">`GetBookUseCase`</span> 介面
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">3.</span> 實作 <span class="sb">`GetBookInteractor`</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">   <span class="k">-</span> 注入 <span class="sb">`IBookRepository`</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">   <span class="k">-</span> 實作 <span class="sb">`execute(String isbn)`</span> 方法
</span></span><span class="line"><span class="ln">20</span><span class="cl">   <span class="k">-</span> 處理 Repository 異常
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">4.</span> 撰寫單元測試
</span></span><span class="line"><span class="ln">22</span><span class="cl">   <span class="k">-</span> 測試 1：成功查詢書籍
</span></span><span class="line"><span class="ln">23</span><span class="cl">   <span class="k">-</span> 測試 2：書籍不存在回傳 null
</span></span><span class="line"><span class="ln">24</span><span class="cl">   <span class="k">-</span> 測試 3：無效 ISBN 拋出 ValidationException
</span></span><span class="line"><span class="ln">25</span><span class="cl">   <span class="k">-</span> 測試 4：Repository 異常正確處理
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="k">5.</span> 確保測試通過
</span></span><span class="line"><span class="ln">27</span><span class="cl">
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> GetBookInteractor 正確注入 IBookRepository
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="k">- [ ]</span> execute 方法實作正確
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="k">- [ ]</span> 異常處理完整
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="k">- [ ]</span> 單元測試 100% 通過（至少 4 個測試）
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">34</span><span class="cl">
</span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="gu">### 指標評估
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="gu"></span><span class="k">-</span> 職責數量：2 個（業務協調、異常處理）
</span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="k">-</span> 程式碼行數：~55 行（含測試）
</span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="k">-</span> 涉及檔案：1 個
</span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="k">-</span> 測試用例：4 個
</span></span><span class="line"><span class="ln">40</span><span class="cl">→ Level 2（中等）
</span></span><span class="line"><span class="ln">41</span><span class="cl">
</span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="gu">### 預估時間
</span></span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="gu"></span>30-40 分鐘</span></span></code></pre></div><hr>
<h3 id="43-複雜-ticket-標準與範例level-3">4.3 複雜 Ticket 標準與範例（Level 3）</h3>
<p><strong>Level 3 特徵</strong>：</p>
<ul>
<li>職責：3-5 個相關職責</li>
<li>行數：70-100 行</li>
<li>檔案：3-5 個</li>
<li>測試：6-10 個</li>
</ul>
<p><strong>適用場景</strong>：</p>
<ul>
<li>完整 Repository CRUD</li>
<li>複雜 UseCase（含多重驗證）</li>
<li>複雜業務邏輯實作</li>
</ul>
<p><strong>注意</strong>：Level 3 Ticket 建議優先評估是否可拆分為更小 Ticket</p>
<hr>
<h4 id="範例-5實作-bookrepository-crud建議拆分">範例 5：實作 BookRepository CRUD（建議拆分）</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #5: 實作 SQLiteBookRepository CRUD
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 層級：Layer 5 (Infrastructure)
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gu">### 業務需求
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="gu"></span>REQ-LIB-001: 書籍資料存取功能
</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"><span class="gu">### 目標
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu"></span>實作 <span class="sb">`SQLiteBookRepository`</span>，提供完整的 CRUD 操作
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu">### 依賴 Ticket
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu"></span><span class="k">-</span> Ticket <span class="ni">#1:</span> 定義 IBookRepository 介面（必須先完成）
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu">### 複雜度警告
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="gu"></span>此 Ticket 為 Level 3（複雜），建議拆分為 3 個 Level 2 Ticket：
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">-</span> Ticket A: 實作 getBookByIsbn + 測試
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">-</span> Ticket B: 實作 saveBook + 測試
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">-</span> Ticket C: 實作 deleteBook + Data Mapper + 測試
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="gu">### 步驟（如不拆分）
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 在 <span class="sb">`lib/infrastructure/repositories/`</span> 建立 <span class="sb">`sqlite_book_repository.dart`</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="k">2.</span> 在 <span class="sb">`lib/infrastructure/mappers/`</span> 建立 <span class="sb">`book_mapper.dart`</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="k">3.</span> 實作 <span class="sb">`getBookByIsbn`</span> 方法
</span></span><span class="line"><span class="ln">25</span><span class="cl">   <span class="k">-</span> SQL 查詢邏輯
</span></span><span class="line"><span class="ln">26</span><span class="cl">   <span class="k">-</span> Data Mapper 轉換（DTO → Entity）
</span></span><span class="line"><span class="ln">27</span><span class="cl">   <span class="k">-</span> 錯誤處理
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="k">4.</span> 實作 <span class="sb">`saveBook`</span> 方法
</span></span><span class="line"><span class="ln">29</span><span class="cl">   <span class="k">-</span> SQL 插入/更新邏輯
</span></span><span class="line"><span class="ln">30</span><span class="cl">   <span class="k">-</span> Data Mapper 轉換（Entity → DTO）
</span></span><span class="line"><span class="ln">31</span><span class="cl">   <span class="k">-</span> 錯誤處理
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="k">5.</span> 實作 <span class="sb">`deleteBook`</span> 方法
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="k">6.</span> 撰寫完整測試
</span></span><span class="line"><span class="ln">34</span><span class="cl">   <span class="k">-</span> getBookByIsbn: 成功、不存在、異常（3 個測試）
</span></span><span class="line"><span class="ln">35</span><span class="cl">   <span class="k">-</span> saveBook: 新增、更新、異常（3 個測試）
</span></span><span class="line"><span class="ln">36</span><span class="cl">   <span class="k">-</span> deleteBook: 成功、不存在、異常（3 個測試）
</span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="k">7.</span> 確保所有測試通過
</span></span><span class="line"><span class="ln">38</span><span class="cl">
</span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 實作所有 IBookRepository 方法
</span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="k">- [ ]</span> Data Mapper 轉換正確
</span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="k">- [ ]</span> 異常處理完整
</span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="k">- [ ]</span> 單元測試 100% 通過（至少 9 個測試）
</span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">45</span><span class="cl">
</span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="gu">### 指標評估
</span></span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="gu"></span><span class="k">-</span> 職責數量：5 個（get、save、delete、mapper、異常處理）
</span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="k">-</span> 程式碼行數：~160 行（含測試）
</span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="k">-</span> 涉及檔案：2 個
</span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="k">-</span> 測試用例：9 個
</span></span><span class="line"><span class="ln">51</span><span class="cl">→ Level 3（複雜）建議拆分
</span></span><span class="line"><span class="ln">52</span><span class="cl">
</span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="gu">### 預估時間
</span></span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="gu"></span>60-90 分鐘
</span></span><span class="line"><span class="ln">55</span><span class="cl">
</span></span><span class="line"><span class="ln">56</span><span class="cl"><span class="gu">### 建議拆分方案
</span></span></span><span class="line"><span class="ln">57</span><span class="cl"><span class="gu"></span>拆分為 3 個 Ticket（詳見第五章決策樹）</span></span></code></pre></div><hr>
<h3 id="44-必須拆分標準level-4">4.4 必須拆分標準（Level 4）</h3>
<p><strong>Level 4 特徵</strong>：</p>
<ul>
<li>職責：&gt; 5 個</li>
<li>行數：&gt; 100 行</li>
<li>檔案：&gt; 5 個</li>
<li>測試：&gt; 10 個</li>
</ul>
<p><strong>處理方式</strong>：</p>
<ul>
<li><strong>禁止建立 Level 4 Ticket</strong></li>
<li><strong>必須拆分為多個 Level 1-2 Ticket</strong></li>
<li><strong>拆分後可接受少數 Level 3 Ticket</strong></li>
</ul>
<hr>
<h4 id="範例-6god-ticket-檢測與拆分">範例 6：God Ticket 檢測與拆分</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 禁止：實作完整書籍評分功能
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 原始任務描述
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>實作書籍評分功能，包含 UI、Controller、UseCase、Repository
</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="gu">### God Ticket 檢測結果
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span><span class="k">-</span> 職責數量：8 個 → Level 4
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">-</span> 程式碼行數：~300 行 → Level 4
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">-</span> 涉及檔案：8 個 → Level 4
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">-</span> 測試用例：20 個 → Level 4
</span></span><span class="line"><span class="ln">11</span><span class="cl">→ <span class="gs">**所有指標都超標，必須拆分**</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu">### 拆分決策
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu"></span>使用「Clean Architecture 分層拆分策略」（詳見第三章 3.4）
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">拆分為 6 個 Ticket：
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">1.</span> Ticket <span class="ni">#201:</span> Rating Value Object（Level 2）
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">2.</span> Ticket <span class="ni">#202:</span> Rating Entity（Level 1）
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">3.</span> Ticket <span class="ni">#203:</span> IRatingRepository Interface（Level 1）
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">4.</span> Ticket <span class="ni">#204:</span> SQLiteRatingRepository（Level 3）
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">5.</span> Ticket <span class="ni">#205:</span> RateBookUseCase（Level 2）
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">6.</span> Ticket <span class="ni">#206:</span> RatingWidget UI（Level 2）
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl">結果：5 個 Level 1-2 + 1 個 Level 3 全部可接受</span></span></code></pre></div><hr>
<h3 id="45-ticket-大小對照表">4.5 Ticket 大小對照表</h3>
<p><strong>快速參考表</strong>：</p>
<table>
  <thead>
      <tr>
          <th>Level</th>
          <th>職責</th>
          <th>行數</th>
          <th>檔案</th>
          <th>測試</th>
          <th>預估時間</th>
          <th>範例</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>1 簡單</strong></td>
          <td>1</td>
          <td>&lt;30</td>
          <td>1</td>
          <td>1-3</td>
          <td>5-20分鐘</td>
          <td>Interface 定義、簡單 VO</td>
      </tr>
      <tr>
          <td><strong>2 中等</strong></td>
          <td>2-3</td>
          <td>30-70</td>
          <td>2-3</td>
          <td>3-6</td>
          <td>20-40分鐘</td>
          <td>Entity、基礎 UseCase</td>
      </tr>
      <tr>
          <td><strong>3 複雜</strong></td>
          <td>3-5</td>
          <td>70-100</td>
          <td>3-5</td>
          <td>6-10</td>
          <td>40-90分鐘</td>
          <td>完整 Repository CRUD</td>
      </tr>
      <tr>
          <td><strong>4 超標</strong></td>
          <td>&gt;5</td>
          <td>&gt;100</td>
          <td>&gt;5</td>
          <td>&gt;10</td>
          <td>N/A</td>
          <td>禁止建立</td>
      </tr>
  </tbody>
</table>
<p><strong>決策建議</strong>：</p>
<ul>
<li>Level 1-2：直接建立 Ticket</li>
<li>Level 3：優先評估是否可拆分</li>
<li>Level 4：必須拆分，無例外</li>
</ul>
<hr>
<h2 id="第五章拆分決策樹">第五章：拆分決策樹</h2>
<h3 id="51-決策樹總覽">5.1 決策樹總覽</h3>
<p><strong>完整決策流程</strong>：</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">[Ticket 設計階段]
</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">[計算 4 個量化指標]
</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">[取最高複雜度等級]
</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">    ├─ Level 1-2
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    │   ↓
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    │   [直接建立 Ticket]
</span></span><span class="line"><span class="ln">10</span><span class="cl">    │   ↓
</span></span><span class="line"><span class="ln">11</span><span class="cl">    │   [進入 Phase 2（測試設計）]
</span></span><span class="line"><span class="ln">12</span><span class="cl">    │
</span></span><span class="line"><span class="ln">13</span><span class="cl">    ├─ Level 3
</span></span><span class="line"><span class="ln">14</span><span class="cl">    │   ↓
</span></span><span class="line"><span class="ln">15</span><span class="cl">    │   [拆分評估決策]
</span></span><span class="line"><span class="ln">16</span><span class="cl">    │   ↓
</span></span><span class="line"><span class="ln">17</span><span class="cl">    │   ├─ 可拆分？
</span></span><span class="line"><span class="ln">18</span><span class="cl">    │   │   ├─ Yes → [執行拆分策略] → [重新評估]
</span></span><span class="line"><span class="ln">19</span><span class="cl">    │   │   └─ No → [勉強接受] → [標記高風險]
</span></span><span class="line"><span class="ln">20</span><span class="cl">    │   ↓
</span></span><span class="line"><span class="ln">21</span><span class="cl">    │   [進入 Phase 2]
</span></span><span class="line"><span class="ln">22</span><span class="cl">    │
</span></span><span class="line"><span class="ln">23</span><span class="cl">    └─ Level 4
</span></span><span class="line"><span class="ln">24</span><span class="cl">        ↓
</span></span><span class="line"><span class="ln">25</span><span class="cl">        [阻止建立 Ticket]
</span></span><span class="line"><span class="ln">26</span><span class="cl">        ↓
</span></span><span class="line"><span class="ln">27</span><span class="cl">        [執行強制拆分]
</span></span><span class="line"><span class="ln">28</span><span class="cl">        ↓
</span></span><span class="line"><span class="ln">29</span><span class="cl">        [選擇拆分策略]
</span></span><span class="line"><span class="ln">30</span><span class="cl">        ↓
</span></span><span class="line"><span class="ln">31</span><span class="cl">        ├─ 優先：按架構分層拆分
</span></span><span class="line"><span class="ln">32</span><span class="cl">        ├─ 次之：按職責拆分
</span></span><span class="line"><span class="ln">33</span><span class="cl">        └─ 最後：按檔案拆分
</span></span><span class="line"><span class="ln">34</span><span class="cl">        ↓
</span></span><span class="line"><span class="ln">35</span><span class="cl">        [重新評估所有子 Ticket]
</span></span><span class="line"><span class="ln">36</span><span class="cl">        ↓
</span></span><span class="line"><span class="ln">37</span><span class="cl">        [確保所有子 Ticket ≤ Level 3]
</span></span><span class="line"><span class="ln">38</span><span class="cl">        ↓
</span></span><span class="line"><span class="ln">39</span><span class="cl">        [可建立 Ticket]</span></span></code></pre></div><hr>
<h3 id="52-level-3-拆分評估決策">5.2 Level 3 拆分評估決策</h3>
<p><strong>決策問題</strong>：此 Level 3 Ticket 是否應該拆分？</p>
<p><strong>評估準則</strong>：</p>
<h4 id="準則-1職責可分離性">準則 1：職責可分離性</h4>





<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？
</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">1. 列出所有職責
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">2. 檢查職責之間的依賴關係
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">3. 判斷是否可獨立完成
</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">範例：
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">原始任務：實作 BookRepository CRUD
</span></span><span class="line"><span class="ln">10</span><span class="cl">職責分析：
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">- 職責 1：getBookByIsbn → 可獨立
</span></span><span class="line"><span class="ln">13</span><span class="cl">- 職責 2：saveBook → 可獨立（依賴職責 1 的測試模式）
</span></span><span class="line"><span class="ln">14</span><span class="cl">- 職責 3：deleteBook → 可獨立
</span></span><span class="line"><span class="ln">15</span><span class="cl">- 職責 4：Data Mapper → 可整合到職責 1-3
</span></span><span class="line"><span class="ln">16</span><span class="cl">- 職責 5：異常處理 → 可整合到職責 1-3
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl">結論：可拆分為 3 個 Ticket（每個職責對應 1 個 Ticket）</span></span></code></pre></div><h4 id="準則-2拆分後複雜度降低">準則 2：拆分後複雜度降低</h4>





<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 是否 ≤ Level 2？
</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">對每個拆分後的子 Ticket 重新計算 4 個指標
</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></span><span class="line"><span class="ln"> 7</span><span class="cl">原始 Ticket（Level 3）：
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- 職責：5 個
</span></span><span class="line"><span class="ln">10</span><span class="cl">- 行數：160 行
</span></span><span class="line"><span class="ln">11</span><span class="cl">- 檔案：2 個
</span></span><span class="line"><span class="ln">12</span><span class="cl">- 測試：9 個
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl">拆分後 Ticket A：getBookByIsbn
</span></span><span class="line"><span class="ln">15</span><span class="cl">- 職責：2 個（查詢 + 轉換）
</span></span><span class="line"><span class="ln">16</span><span class="cl">- 行數：50 行
</span></span><span class="line"><span class="ln">17</span><span class="cl">- 檔案：2 個
</span></span><span class="line"><span class="ln">18</span><span class="cl">- 測試：3 個
</span></span><span class="line"><span class="ln">19</span><span class="cl">→ Level 2
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl">拆分後 Ticket B：saveBook
</span></span><span class="line"><span class="ln">22</span><span class="cl">- 職責：2 個（儲存 + 異常處理）
</span></span><span class="line"><span class="ln">23</span><span class="cl">- 行數：60 行
</span></span><span class="line"><span class="ln">24</span><span class="cl">- 檔案：1 個（修改）
</span></span><span class="line"><span class="ln">25</span><span class="cl">- 測試：3 個
</span></span><span class="line"><span class="ln">26</span><span class="cl">→ Level 2
</span></span><span class="line"><span class="ln">27</span><span class="cl">
</span></span><span class="line"><span class="ln">28</span><span class="cl">拆分後 Ticket C：deleteBook
</span></span><span class="line"><span class="ln">29</span><span class="cl">- 職責：2 個（刪除 + 異常處理）
</span></span><span class="line"><span class="ln">30</span><span class="cl">- 行數：50 行
</span></span><span class="line"><span class="ln">31</span><span class="cl">- 檔案：1 個（修改）
</span></span><span class="line"><span class="ln">32</span><span class="cl">- 測試：3 個
</span></span><span class="line"><span class="ln">33</span><span class="cl">→ Level 2
</span></span><span class="line"><span class="ln">34</span><span class="cl">
</span></span><span class="line"><span class="ln">35</span><span class="cl">結論：拆分成功，所有子 Ticket 都降為 Level 2</span></span></code></pre></div><h4 id="準則-3拆分成本合理性">準則 3：拆分成本合理性</h4>





<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><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">- 收益：風險降低、易於 Review、可並行開發
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">- 成本：增加 Ticket 數量、需要管理依賴關係
</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">決策：
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">- 拆分收益 &gt; 拆分成本 → 建議拆分
</span></span><span class="line"><span class="ln">11</span><span class="cl">- 拆分收益 ≈ 拆分成本 → 可選擇
</span></span><span class="line"><span class="ln">12</span><span class="cl">- 拆分收益 &lt; 拆分成本 → 不建議拆分
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl">範例：
</span></span><span class="line"><span class="ln">15</span><span class="cl">原始 Ticket：Level 3（複雜 Repository CRUD）
</span></span><span class="line"><span class="ln">16</span><span class="cl">拆分收益：
</span></span><span class="line"><span class="ln">17</span><span class="cl">+ 風險降低：1 個高風險 → 3 個低風險
</span></span><span class="line"><span class="ln">18</span><span class="cl">+ Review 效率：每次 Review 50-60 行 vs 160 行
</span></span><span class="line"><span class="ln">19</span><span class="cl">+ 可並行：3 個 Ticket 可依序快速開發
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl">拆分成本：
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl">- 管理成本：需要管理 3 個 Ticket 依賴
</span></span><span class="line"><span class="ln">24</span><span class="cl">- 整合測試：需要額外的整合測試（但本來就需要）
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl">結論：拆分收益 &gt; 拆分成本，建議拆分</span></span></code></pre></div><hr>
<h3 id="53-level-4-強制拆分策略">5.3 Level 4 強制拆分策略</h3>
<h4 id="level-4-無需評估必須拆分">Level 4 無需評估，必須拆分</h4>
<h4 id="拆分策略優先順序">拆分策略優先順序</h4>
<h5 id="策略-1按-clean-architecture-分層拆分優先">策略 1：按 Clean Architecture 分層拆分（優先）</h5>





<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><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">- Ticket 跨越多個架構層級（Layer 1-5）
</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">- 涉及 UI、UseCase、Repository 等多層
</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">1. 按照 Layer 1 → Layer 5 順序分組檔案
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">2. 每個 Layer 建立獨立 Ticket
</span></span><span class="line"><span class="ln">10</span><span class="cl">3. 相鄰兩層可合併為單一 Ticket（如 Interface + Implementation）
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">範例（書籍評分功能）：
</span></span><span class="line"><span class="ln">13</span><span class="cl">原始：8 個檔案，跨越 4 層
</span></span><span class="line"><span class="ln">14</span><span class="cl">拆分後：
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">- Ticket 1: Layer 5 Domain（Rating VO + Entity）
</span></span><span class="line"><span class="ln">17</span><span class="cl">- Ticket 2: Layer 5 + 4 Repository（Interface + Impl）
</span></span><span class="line"><span class="ln">18</span><span class="cl">- Ticket 3: Layer 3 UseCase
</span></span><span class="line"><span class="ln">19</span><span class="cl">- Ticket 4: Layer 1-2 Presentation（UI + Controller）
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl">結果：4 個 Level 1-2 Ticket</span></span></code></pre></div><h6 id="策略-2按職責拆分次之">策略 2：按職責拆分（次之）</h6>





<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><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">- 單一 Repository 包含過多 CRUD 方法
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">- 單一 UseCase 包含過多業務邏輯
</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">1. 列出所有職責
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">2. 每個獨立職責建立 Ticket
</span></span><span class="line"><span class="ln">10</span><span class="cl">3. 相關職責可合併（最多 2-3 個職責）
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">範例（BookRepository CRUD）：
</span></span><span class="line"><span class="ln">13</span><span class="cl">原始：5 個職責（get + save + delete + mapper + error）
</span></span><span class="line"><span class="ln">14</span><span class="cl">拆分後：
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">- Ticket A: getBookByIsbn + mapper + error（2-3 職責）
</span></span><span class="line"><span class="ln">17</span><span class="cl">- Ticket B: saveBook + error（2 職責）
</span></span><span class="line"><span class="ln">18</span><span class="cl">- Ticket C: deleteBook + error（2 職責）
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl">結果：3 個 Level 2 Ticket</span></span></code></pre></div><h6 id="策略-3按檔案拆分最後">策略 3：按檔案拆分（最後）</h6>





<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><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">- 每個檔案本身就是一個完整單元
</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">1. 每個檔案建立獨立 Ticket
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">2. 相關檔案可合併（最多 2-3 個檔案）
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">範例：
</span></span><span class="line"><span class="ln">12</span><span class="cl">原始：7 個檔案
</span></span><span class="line"><span class="ln">13</span><span class="cl">拆分後：
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">- Ticket 1: file1.dart + file2.dart（相關）
</span></span><span class="line"><span class="ln">16</span><span class="cl">- Ticket 2: file3.dart
</span></span><span class="line"><span class="ln">17</span><span class="cl">- Ticket 3: file4.dart + file5.dart（相關）
</span></span><span class="line"><span class="ln">18</span><span class="cl">- Ticket 4: file6.dart + file7.dart（相關）
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl">結果：4 個 Ticket</span></span></code></pre></div><hr>
<h3 id="54-拆分決策檢查清單">5.4 拆分決策檢查清單</h3>
<p><strong>Level 3 拆分評估檢查清單</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">□ 已列出所有職責
</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">□ 已計算拆分後每個子 Ticket 的指標
</span></span><span class="line"><span class="ln">4</span><span class="cl">□ 已確認所有子 Ticket ≤ Level 2
</span></span><span class="line"><span class="ln">5</span><span class="cl">□ 已評估拆分成本 vs 收益
</span></span><span class="line"><span class="ln">6</span><span class="cl">□ 已確定是否拆分的最終決策</span></span></code></pre></div><p><strong>Level 4 強制拆分檢查清單</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">□ 已識別 Ticket 為 Level 4（任一指標超標）
</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">□ 已執行拆分（列出所有子 Ticket）
</span></span><span class="line"><span class="ln">4</span><span class="cl">□ 已重新評估所有子 Ticket
</span></span><span class="line"><span class="ln">5</span><span class="cl">□ 已確認所有子 Ticket ≤ Level 3
</span></span><span class="line"><span class="ln">6</span><span class="cl">□ 已標記子 Ticket 依賴關係
</span></span><span class="line"><span class="ln">7</span><span class="cl">□ 已確認子 Ticket 總和涵蓋原始功能</span></span></code></pre></div><hr>
<h2 id="第六章ticket-拆分檢查清單">第六章：Ticket 拆分檢查清單</h2>
<h3 id="61-拆分前檢查清單">6.1 拆分前檢查清單</h3>
<h4 id="階段-1需求理解">階段 1：需求理解</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">□ 已閱讀完整的業務需求
</span></span><span class="line"><span class="ln">2</span><span class="cl">□ 已理解 Ticket 的業務目標
</span></span><span class="line"><span class="ln">3</span><span class="cl">□ 已確認 Ticket 的驗收條件
</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">□ 已確認與其他 Ticket 的依賴關係</span></span></code></pre></div><h5 id="階段-2指標計算">階段 2：指標計算</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">□ 已列出所有職責（功能點 + 邊界條件 + 異常處理）
</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">□ 已取最高複雜度等級作為最終評估</span></span></code></pre></div><h6 id="階段-3複雜度確認">階段 3：複雜度確認</h6>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">□ 已確定 Ticket 的複雜度等級（Level 1-4）
</span></span><span class="line"><span class="ln">2</span><span class="cl">□ 如為 Level 4，已阻止建立並準備拆分
</span></span><span class="line"><span class="ln">3</span><span class="cl">□ 如為 Level 3，已評估是否拆分
</span></span><span class="line"><span class="ln">4</span><span class="cl">□ 如為 Level 1-2，已確認可直接建立</span></span></code></pre></div><hr>
<h3 id="62-拆分過程檢查清單">6.2 拆分過程檢查清單</h3>
<h4 id="階段-4拆分策略選擇">階段 4：拆分策略選擇</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">□ 已分析 Ticket 涉及的架構層級
</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">□ 已列出所有拆分後的子 Ticket
</span></span><span class="line"><span class="ln">4</span><span class="cl">□ 已為每個子 Ticket 撰寫初步描述</span></span></code></pre></div><h5 id="階段-5子-ticket-設計">階段 5：子 Ticket 設計</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">□ 每個子 Ticket 都有明確的目標
</span></span><span class="line"><span class="ln">2</span><span class="cl">□ 每個子 Ticket 都有清楚的步驟
</span></span><span class="line"><span class="ln">3</span><span class="cl">□ 每個子 Ticket 都有具體的驗收條件
</span></span><span class="line"><span class="ln">4</span><span class="cl">□ 每個子 Ticket 都標記了層級（[Layer X]）
</span></span><span class="line"><span class="ln">5</span><span class="cl">□ 每個子 Ticket 都標記了依賴關係</span></span></code></pre></div><h6 id="階段-6依賴關係確認">階段 6：依賴關係確認</h6>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">□ 已識別所有子 Ticket 之間的依賴
</span></span><span class="line"><span class="ln">2</span><span class="cl">□ 已確保依賴方向符合 Clean Architecture 規則
</span></span><span class="line"><span class="ln">3</span><span class="cl">□ 已標記依賴順序（Phase 1 → Phase 2 → ...）
</span></span><span class="line"><span class="ln">4</span><span class="cl">□ 已確認可並行執行的 Ticket</span></span></code></pre></div><hr>
<h3 id="63-拆分後驗證清單">6.3 拆分後驗證清單</h3>
<h4 id="階段-7指標重新評估">階段 7：指標重新評估</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">□ 已重新計算每個子 Ticket 的 4 個指標
</span></span><span class="line"><span class="ln">2</span><span class="cl">□ 已確認所有子 Ticket ≤ Level 3
</span></span><span class="line"><span class="ln">3</span><span class="cl">□ 已確認大多數子 Ticket ≤ Level 2
</span></span><span class="line"><span class="ln">4</span><span class="cl">□ 如有 Level 3 子 Ticket，已評估合理性</span></span></code></pre></div><h5 id="階段-8完整性驗證">階段 8：完整性驗證</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">□ 所有子 Ticket 功能總和 = 原始 Ticket 功能
</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">□ 所有檔案都被包含在某個子 Ticket 中
</span></span><span class="line"><span class="ln">5</span><span class="cl">□ 所有測試案例都被包含</span></span></code></pre></div><h6 id="階段-9品質檢查">階段 9：品質檢查</h6>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">□ 每個子 Ticket 都有業務需求引用
</span></span><span class="line"><span class="ln">2</span><span class="cl">□ 每個子 Ticket 都有指標評估
</span></span><span class="line"><span class="ln">3</span><span class="cl">□ 每個子 Ticket 都有預估時間
</span></span><span class="line"><span class="ln">4</span><span class="cl">□ 每個子 Ticket 的職責明確且不重疊
</span></span><span class="line"><span class="ln">5</span><span class="cl">□ 每個子 Ticket 的標題包含 [Layer X] 標籤</span></span></code></pre></div><hr>
<h3 id="64-特殊情況檢查清單">6.4 特殊情況檢查清單</h3>
<h4 id="情況-1無法拆分的-level-3-ticket">情況 1：無法拆分的 Level 3 Ticket</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">□ 已明確記錄為何無法拆分
</span></span><span class="line"><span class="ln">2</span><span class="cl">□ 已標記為「高風險 Ticket」
</span></span><span class="line"><span class="ln">3</span><span class="cl">□ 已安排額外的 Code Review
</span></span><span class="line"><span class="ln">4</span><span class="cl">□ 已增加測試覆蓋率要求（&gt; 90%）
</span></span><span class="line"><span class="ln">5</span><span class="cl">□ 已準備更長的開發時間</span></span></code></pre></div><h5 id="情況-2跨層整合-ticket">情況 2：跨層整合 Ticket</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">□ 已確認只涉及相鄰兩層（如 Layer 3 + Layer 4-5）
</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">□ 已明確標記為「整合 Ticket」
</span></span><span class="line"><span class="ln">4</span><span class="cl">□ 已包含整合測試驗收條件</span></span></code></pre></div><h6 id="情況-3測試獨立-ticket">情況 3：測試獨立 Ticket</h6>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">□ 已確認生產程式碼已完成（依賴 Ticket）
</span></span><span class="line"><span class="ln">2</span><span class="cl">□ Ticket 只包含測試程式碼
</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 class="p">&lt;</span> <span class="nt">10</span> <span class="na">個</span><span class="err">）</span></span></span></code></pre></div><hr>
<h2 id="第七章實務案例與最佳實踐">第七章：實務案例與最佳實踐</h2>
<h3 id="71-完整案例書籍搜尋功能">7.1 完整案例：書籍搜尋功能</h3>
<p><strong>業務需求</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">REQ-SEARCH-001: 實作書籍搜尋功能
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="k">-</span> 使用者可輸入關鍵字搜尋書籍
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="k">-</span> 支援書名、作者、ISBN 搜尋
</span></span><span class="line"><span class="ln">4</span><span class="cl">- 顯示搜尋結果列表</span></span></code></pre></div><hr>
<h4 id="初步評估識別為-god-ticket">初步評估：識別為 God Ticket</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">原始任務分析：
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">實作完整書籍搜尋功能（包含 UI、Controller、UseCase、Repository）
</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">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">-</span> 職責數量：10 個
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="k">1.</span> SearchBar UI 元件
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="k">2.</span> SearchResultList UI 元件
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="k">3.</span> SearchController 狀態管理
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="k">4.</span> SearchBookUseCase 業務協調
</span></span><span class="line"><span class="ln">11</span><span class="cl">  <span class="k">5.</span> Repository 查詢方法（書名搜尋）
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="k">6.</span> Repository 查詢方法（作者搜尋）
</span></span><span class="line"><span class="ln">13</span><span class="cl">  <span class="k">7.</span> Repository 查詢方法（ISBN 搜尋）
</span></span><span class="line"><span class="ln">14</span><span class="cl">  <span class="k">8.</span> 錯誤處理
</span></span><span class="line"><span class="ln">15</span><span class="cl">  <span class="k">9.</span> 載入狀態處理
</span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="k">10.</span> 空結果處理
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">-</span> 程式碼行數：~400 行
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">-</span> 涉及檔案：10 個
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">-</span> 測試用例：25 個
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl">複雜度評估：
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="k">-</span> 職責：10 個 → Level 4
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="k">-</span> 行數：400 行 → Level 4
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="k">-</span> 檔案：10 個 → Level 4
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="k">-</span> 測試：25 個 → Level 4
</span></span><span class="line"><span class="ln">28</span><span class="cl">
</span></span><span class="line"><span class="ln">29</span><span class="cl">結論：God Ticket 必須拆分</span></span></code></pre></div><hr>
<h4 id="拆分策略clean-architecture-分層拆分">拆分策略：Clean Architecture 分層拆分</h4>
<h5 id="第一步按層級分組檔案">第一步：按層級分組檔案</h5>





<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">Layer 5 (Domain + Infrastructure):
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">- SearchQuery Value Object
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">- IBookRepository.searchByTitle()
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">- IBookRepository.searchByAuthor()
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">- IBookRepository.searchByIsbn()
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">- SQLiteBookRepository 查詢實作
</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">Layer 3 (UseCase):
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- SearchBookUseCase
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">Layer 2 (Behavior):
</span></span><span class="line"><span class="ln">12</span><span class="cl">- SearchController
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl">Layer 1 (UI):
</span></span><span class="line"><span class="ln">15</span><span class="cl">- SearchBar Widget
</span></span><span class="line"><span class="ln">16</span><span class="cl">- SearchResultList Widget</span></span></code></pre></div><h6 id="第二步設計拆分後的-ticket">第二步：設計拆分後的 Ticket</h6>
<hr>
<h6 id="ticket-1-定義-searchquery-value-objectlayer-5">Ticket 1: 定義 SearchQuery Value Object（Layer 5）</h6>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #301: 定義 SearchQuery Value Object
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 層級：Layer 5 (Domain Implementation)
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu">### 策略：策略 2（具體實作）
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="gu">### 業務需求
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span>REQ-SEARCH-001: 書籍搜尋功能
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">### 目標
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span>建立 <span class="sb">`SearchQuery`</span> Value Object，封裝搜尋條件驗證
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 建立 <span class="sb">`lib/domain/value_objects/search_query.dart`</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">2.</span> 實作 SearchQuery 類別
</span></span><span class="line"><span class="ln">15</span><span class="cl">   <span class="k">-</span> 驗證查詢字串長度（最少 2 個字元）
</span></span><span class="line"><span class="ln">16</span><span class="cl">   <span class="k">-</span> trim() 處理空白
</span></span><span class="line"><span class="ln">17</span><span class="cl">   <span class="k">-</span> equals / hashCode
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">3.</span> 撰寫單元測試
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">4.</span> 確保測試通過
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> SearchQuery 類別實作完整
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="k">- [ ]</span> 驗證邏輯正確（最少 2 字元）
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="k">- [ ]</span> 單元測試 100% 通過（至少 4 個測試）
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="gu">### 指標評估
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="gu"></span><span class="k">-</span> 職責：1 個
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="k">-</span> 行數：~30 行
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="k">-</span> 檔案：1 個
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="k">-</span> 測試：4 個
</span></span><span class="line"><span class="ln">32</span><span class="cl">→ Level 1（簡單）
</span></span><span class="line"><span class="ln">33</span><span class="cl">
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="gu">### 預估時間
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="gu"></span>15-20 分鐘</span></span></code></pre></div><hr>
<h4 id="ticket-2-擴充-ibookrepository-查詢方法layer-4">Ticket 2: 擴充 IBookRepository 查詢方法（Layer 4）</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #302: 定義 IBookRepository 搜尋方法
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 層級：Layer 4 (Domain Interfaces)
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu">### 策略：策略 1（Interface 定義）
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="gu">### 業務需求
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span>REQ-SEARCH-001: 書籍搜尋功能
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">### 目標
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span>在 <span class="sb">`IBookRepository`</span> 介面新增搜尋方法簽名
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 修改 <span class="sb">`lib/domain/repositories/i_book_repository.dart`</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">2.</span> 新增 <span class="sb">`searchByTitle(String query)`</span> 方法簽名
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">3.</span> 新增 <span class="sb">`searchByAuthor(String query)`</span> 方法簽名
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">4.</span> 新增 <span class="sb">`searchByIsbn(String query)`</span> 方法簽名
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">5.</span> 撰寫文檔註解
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 3 個搜尋方法簽名完整
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">- [ ]</span> 回傳類型為 <span class="sb">`Future&lt;List&lt;Book&gt;&gt;`</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">- [ ]</span> 文檔註解包含需求編號
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="gu">### 指標評估
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="gu"></span><span class="k">-</span> 職責：1 個（定義介面）
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="k">-</span> 行數：~15 行
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="k">-</span> 檔案：1 個（修改）
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="k">-</span> 測試：0 個
</span></span><span class="line"><span class="ln">30</span><span class="cl">→ Level 1（簡單）
</span></span><span class="line"><span class="ln">31</span><span class="cl">
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="gu">### 預估時間
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="gu"></span>10 分鐘</span></span></code></pre></div><hr>
<h4 id="ticket-3-實作-bookrepository-搜尋方法layer-5">Ticket 3: 實作 BookRepository 搜尋方法（Layer 5）</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #303: 實作 SQLiteBookRepository 搜尋
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 層級：Layer 5 (Infrastructure)
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu">### 策略：策略 2（具體實作）
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gu">### 依賴：Ticket #301, #302
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu">### 業務需求
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="gu"></span>REQ-SEARCH-001: 書籍搜尋功能
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu">### 目標
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu"></span>實作 <span class="sb">`SQLiteBookRepository`</span> 的 3 個搜尋方法
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 修改 <span class="sb">`lib/infrastructure/repositories/sqlite_book_repository.dart`</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">2.</span> 實作 <span class="sb">`searchByTitle`</span> 方法
</span></span><span class="line"><span class="ln">16</span><span class="cl">   <span class="k">-</span> SQL LIKE 查詢
</span></span><span class="line"><span class="ln">17</span><span class="cl">   <span class="k">-</span> Data Mapper 轉換
</span></span><span class="line"><span class="ln">18</span><span class="cl">   <span class="k">-</span> 錯誤處理
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">3.</span> 實作 <span class="sb">`searchByAuthor`</span> 方法
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">4.</span> 實作 <span class="sb">`searchByIsbn`</span> 方法
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">5.</span> 撰寫單元測試（每個方法 2-3 個測試）
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">6.</span> 確保測試通過
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 3 個搜尋方法實作完整
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="k">- [ ]</span> SQL 查詢使用 LIKE 模糊搜尋
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="k">- [ ]</span> 異常處理完整
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="k">- [ ]</span> 單元測試 100% 通過（至少 8 個測試）
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">30</span><span class="cl">
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="gu">### 指標評估
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="gu"></span><span class="k">-</span> 職責：4 個（3 個搜尋方法 + 異常處理）
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="k">-</span> 行數：~95 行
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="k">-</span> 檔案：1 個（修改）
</span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="k">-</span> 測試：8 個
</span></span><span class="line"><span class="ln">36</span><span class="cl">→ Level 3（複雜）可接受
</span></span><span class="line"><span class="ln">37</span><span class="cl">（註：3 個搜尋方法邏輯相似，整合實作較有效率）
</span></span><span class="line"><span class="ln">38</span><span class="cl">
</span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="gu">### 預估時間
</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="gu"></span>60-75 分鐘</span></span></code></pre></div><hr>
<h4 id="ticket-4-實作-searchbookusecaselayer-3">Ticket 4: 實作 SearchBookUseCase（Layer 3）</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #304: 實作 SearchBookUseCase
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 層級：Layer 3 (UseCase)
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu">### 策略：策略 4（整合連接）
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gu">### 依賴：Ticket #302, #303
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu">### 業務需求
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="gu"></span>REQ-SEARCH-001: 書籍搜尋功能
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu">### 目標
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu"></span>實作 <span class="sb">`SearchBookUseCase`</span>，協調搜尋業務流程
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 建立 <span class="sb">`lib/application/use_cases/search_book_use_case.dart`</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">2.</span> 注入 <span class="sb">`IBookRepository`</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">3.</span> 實作 <span class="sb">`execute(SearchQuery query, SearchType type)`</span> 方法
</span></span><span class="line"><span class="ln">17</span><span class="cl">   <span class="k">-</span> 根據 SearchType 呼叫對應的 Repository 方法
</span></span><span class="line"><span class="ln">18</span><span class="cl">   <span class="k">-</span> 處理空結果
</span></span><span class="line"><span class="ln">19</span><span class="cl">   <span class="k">-</span> 處理異常
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">4.</span> 撰寫整合測試
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">5.</span> 確保測試通過
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> UseCase 正確注入 IBookRepository
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="k">- [ ]</span> 支援 3 種搜尋類型（Title / Author / ISBN）
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="k">- [ ]</span> 整合測試 100% 通過（至少 5 個測試）
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="k">- [ ]</span> 異常處理完整
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">29</span><span class="cl">
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="gu">### 指標評估
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="gu"></span><span class="k">-</span> 職責：2 個（業務協調、異常處理）
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="k">-</span> 行數：~65 行
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="k">-</span> 檔案：1 個
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="k">-</span> 測試：5 個
</span></span><span class="line"><span class="ln">35</span><span class="cl">→ Level 2（中等）
</span></span><span class="line"><span class="ln">36</span><span class="cl">
</span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="gu">### 預估時間
</span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="gu"></span>35-45 分鐘</span></span></code></pre></div><hr>
<h4 id="ticket-5-實作-searchcontrollerlayer-2">Ticket 5: 實作 SearchController（Layer 2）</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #305: 實作 SearchController
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 層級：Layer 2 (Behavior)
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu">### 策略：策略 4（整合連接）
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gu">### 依賴：Ticket #304
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu">### 業務需求
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="gu"></span>REQ-SEARCH-001: 書籍搜尋功能
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu">### 目標
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu"></span>實作 <span class="sb">`SearchController`</span>，管理搜尋狀態
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 建立 <span class="sb">`lib/presentation/controllers/search_controller.dart`</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">2.</span> 注入 <span class="sb">`SearchBookUseCase`</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">3.</span> 實作狀態管理
</span></span><span class="line"><span class="ln">17</span><span class="cl">   <span class="k">-</span> isLoading（載入中）
</span></span><span class="line"><span class="ln">18</span><span class="cl">   <span class="k">-</span> searchResults（搜尋結果）
</span></span><span class="line"><span class="ln">19</span><span class="cl">   <span class="k">-</span> errorMessage（錯誤訊息）
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">4.</span> 實作 <span class="sb">`search(String query, SearchType type)`</span> 方法
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">5.</span> 撰寫單元測試
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">6.</span> 確保測試通過
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> SearchController 正確注入 UseCase
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="k">- [ ]</span> 狀態管理正確（loading / success / error）
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="k">- [ ]</span> 單元測試 100% 通過（至少 5 個測試）
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">29</span><span class="cl">
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="gu">### 指標評估
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="gu"></span><span class="k">-</span> 職責：2 個（狀態管理、UseCase 整合）
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="k">-</span> 行數：~70 行
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="k">-</span> 檔案：1 個
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="k">-</span> 測試：5 個
</span></span><span class="line"><span class="ln">35</span><span class="cl">→ Level 2（中等）
</span></span><span class="line"><span class="ln">36</span><span class="cl">
</span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="gu">### 預估時間
</span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="gu"></span>40 分鐘</span></span></code></pre></div><hr>
<h4 id="ticket-6-實作搜尋-ui-元件layer-1">Ticket 6: 實作搜尋 UI 元件（Layer 1）</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #306: 實作 SearchBar 和 SearchResultList
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 層級：Layer 1 (UI)
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu">### 策略：策略 2（具體實作）
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gu">### 依賴：Ticket #305
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu">### 業務需求
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="gu"></span>REQ-SEARCH-001: 書籍搜尋功能
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu">### 目標
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu"></span>實作搜尋介面的 UI 元件
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 建立 <span class="sb">`lib/presentation/widgets/search_bar.dart`</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">2.</span> 實作 SearchBar Widget
</span></span><span class="line"><span class="ln">16</span><span class="cl">   <span class="k">-</span> TextField 輸入框
</span></span><span class="line"><span class="ln">17</span><span class="cl">   <span class="k">-</span> SearchType 選擇器
</span></span><span class="line"><span class="ln">18</span><span class="cl">   <span class="k">-</span> 搜尋按鈕
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">3.</span> 建立 <span class="sb">`lib/presentation/widgets/search_result_list.dart`</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">4.</span> 實作 SearchResultList Widget
</span></span><span class="line"><span class="ln">21</span><span class="cl">   <span class="k">-</span> 顯示搜尋結果
</span></span><span class="line"><span class="ln">22</span><span class="cl">   <span class="k">-</span> 載入中狀態
</span></span><span class="line"><span class="ln">23</span><span class="cl">   <span class="k">-</span> 空結果提示
</span></span><span class="line"><span class="ln">24</span><span class="cl">   <span class="k">-</span> 錯誤提示
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="k">5.</span> 撰寫 Widget 測試
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="k">6.</span> 確保測試通過
</span></span><span class="line"><span class="ln">27</span><span class="cl">
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> SearchBar UI 正確顯示和互動
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="k">- [ ]</span> SearchResultList 正確顯示各種狀態
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="k">- [ ]</span> Widget 測試 100% 通過（至少 6 個測試）
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">33</span><span class="cl">
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="gu">### 指標評估
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="gu"></span><span class="k">-</span> 職責：2 個（SearchBar、SearchResultList）
</span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="k">-</span> 行數：~90 行
</span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="k">-</span> 檔案：2 個
</span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="k">-</span> 測試：6 個
</span></span><span class="line"><span class="ln">39</span><span class="cl">→ Level 2（中等）
</span></span><span class="line"><span class="ln">40</span><span class="cl">
</span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="gu">### 預估時間
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="gu"></span>50-60 分鐘</span></span></code></pre></div><hr>
<h4 id="拆分結果總結-1">拆分結果總結</h4>
<p><strong>拆分前後對比</strong>：</p>
<table>
  <thead>
      <tr>
          <th>項目</th>
          <th>拆分前（God Ticket）</th>
          <th>拆分後（6 個 Ticket）</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>職責數量</td>
          <td>10 個</td>
          <td>平均 1.8 個</td>
      </tr>
      <tr>
          <td>程式碼行數</td>
          <td>~400 行</td>
          <td>平均 61 行</td>
      </tr>
      <tr>
          <td>檔案數量</td>
          <td>10 個</td>
          <td>平均 1.2 個</td>
      </tr>
      <tr>
          <td>測試數量</td>
          <td>25 個</td>
          <td>平均 4.7 個</td>
      </tr>
      <tr>
          <td>複雜度等級</td>
          <td>Level 4</td>
          <td>5 個 Level 1-2<br>1 個 Level 3</td>
      </tr>
      <tr>
          <td>預估總時間</td>
          <td>N/A（無法估算）</td>
          <td>210-250 分鐘</td>
      </tr>
  </tbody>
</table>
<p><strong>拆分效益分析</strong>：</p>
<ol>
<li>
<p><strong>風險大幅降低</strong>：</p>
<ul>
<li>從 1 個無法管理的 God Ticket → 6 個可控 Ticket</li>
<li>單一 Ticket 失敗不影響其他 Ticket</li>
</ul>
</li>
<li>
<p><strong>支援並行開發</strong>：</p>
<ul>
<li>Phase 1: Ticket #301, #302 可並行</li>
<li>Phase 2: Ticket #303 執行</li>
<li>Phase 3: Ticket #304 執行</li>
<li>Phase 4: Ticket #305, #306 可部分並行</li>
</ul>
</li>
<li>
<p><strong>Review 效率提升</strong>：</p>
<ul>
<li>每次 Review 平均 61 行（vs 400 行）</li>
<li>Review 時間縮短 85%</li>
</ul>
</li>
<li>
<p><strong>測試獨立性</strong>：</p>
<ul>
<li>每層可獨立測試</li>
<li>Mock 依賴簡單明確</li>
</ul>
</li>
</ol>
<hr>
<h3 id="72-常見錯誤與解決方案">7.2 常見錯誤與解決方案</h3>
<h4 id="錯誤-1過度拆分">錯誤 1：過度拆分</h4>
<p><strong>問題描述</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">將 Level 2（中等）Ticket 拆分為多個 Level 1 Ticket，
</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">原始 Ticket（Level 2）：實作 Rating Value Object
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">-</span> 職責：2 個（建立 + 驗證）
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">-</span> 行數：50 行
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">-</span> 測試：5 個
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">過度拆分為 2 個 Ticket：
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">-</span> Ticket A：實作 Rating 建構子（Level 1，25 行）
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">-</span> Ticket B：實作 Rating 驗證邏輯（Level 1，25 行）
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">問題：
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">-</span> 兩個 Ticket 高度耦合，必須連續執行
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">-</span> 增加了額外的管理成本
</span></span><span class="line"><span class="ln">19</span><span class="cl">- 沒有實質的風險降低</span></span></code></pre></div><p><strong>解決方案</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">保持原始 Level 2 Ticket 不拆分
</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"><span class="k">-</span> Level 1-2 Ticket 通常不需要拆分
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="k">-</span> 只有 Level 3-4 才需要評估拆分
</span></span><span class="line"><span class="ln">7</span><span class="cl">- 拆分後的子 Ticket 應該相對獨立</span></span></code></pre></div><hr>
<h4 id="錯誤-2拆分後依賴過於複雜">錯誤 2：拆分後依賴過於複雜</h4>
<p><strong>問題描述</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">拆分方式導致依賴關係過於複雜，
</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">拆分為 8 個 Ticket，依賴關係如下：
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">Ticket 1 → Ticket 2 → Ticket 4
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">Ticket 1 → Ticket 3 → Ticket 5
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">Ticket 2 → Ticket 6
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">Ticket 3 → Ticket 6
</span></span><span class="line"><span class="ln">10</span><span class="cl">Ticket 5 → Ticket 7
</span></span><span class="line"><span class="ln">11</span><span class="cl">Ticket 6 → Ticket 7 → Ticket 8
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">問題：
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">-</span> 依賴網絡複雜，難以追蹤
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">-</span> 執行順序不明確
</span></span><span class="line"><span class="ln">17</span><span class="cl">- 容易遺漏依賴</span></span></code></pre></div><p><strong>解決方案</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">使用線性或樹狀依賴結構
</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">Phase 1（並行）:
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">├─ Ticket 1, Ticket 2
</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">Phase 2（並行）:
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">├─ Ticket 3, Ticket 4（依賴 Phase 1）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">Phase 3:
</span></span><span class="line"><span class="ln">11</span><span class="cl">└─ Ticket 5（依賴 Phase 2）
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">Phase 4:
</span></span><span class="line"><span class="ln">14</span><span class="cl">└─ Ticket 6（依賴 Phase 3）
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">判斷準則：
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">-</span> 優先使用 Phase 分組（線性依賴）
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">-</span> 同 Phase 內的 Ticket 可並行
</span></span><span class="line"><span class="ln">20</span><span class="cl">- 避免跨 Phase 的複雜依賴</span></span></code></pre></div><hr>
<h4 id="錯誤-3拆分邊界不清晰">錯誤 3：拆分邊界不清晰</h4>
<p><strong>問題描述</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">拆分後的子 Ticket 職責重疊或邊界模糊，
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">導致實作時不知道該在哪個 Ticket 完成。
</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">原始：實作完整 BookRepository
</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"><span class="k">-</span> Ticket A：實作 getBookByIsbn
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">-</span> Ticket B：實作 Data Mapper
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">-</span> Ticket C：實作異常處理
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">問題：
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">-</span> Ticket A 需要 Data Mapper（依賴 Ticket B）
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">-</span> Ticket A 需要異常處理（依賴 Ticket C）
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">-</span> 但 Ticket A 應該先完成？
</span></span><span class="line"><span class="ln">17</span><span class="cl">- 邊界模糊，無法獨立完成</span></span></code></pre></div><p><strong>解決方案</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">確保每個子 Ticket 可獨立完成
</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"><span class="k">-</span> Ticket A：實作 getBookByIsbn（含 Mapper + 異常處理）
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">-</span> Ticket B：實作 saveBook（含 Mapper + 異常處理）
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">-</span> Ticket C：實作 deleteBook（含異常處理）
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">判斷準則：
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">-</span> 每個子 Ticket 應該是「最小可交付單元」
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">-</span> 避免將共用邏輯獨立為 Ticket
</span></span><span class="line"><span class="ln">13</span><span class="cl">- 共用邏輯應整合到使用它的 Ticket 中</span></span></code></pre></div><hr>
<h3 id="73-拆分最佳實踐">7.3 拆分最佳實踐</h3>
<h4 id="實踐-1優先選擇架構分層拆分">實踐 1：優先選擇架構分層拆分</h4>
<p><strong>原則</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">當 Ticket 跨越多個架構層級時，
</span></span><span class="line"><span class="ln">2</span><span class="cl">優先按照 Clean Architecture 分層拆分。
</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">依賴方向清晰（內層不依賴外層）
</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">符合單一職責原則</span></span></code></pre></div><p><strong>範例</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">原始任務：實作書籍評分功能（跨越 4 層）
</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 class="k">-</span> Ticket A：評分輸入功能（UI + Controller + UseCase）
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">-</span> Ticket B：評分儲存功能（Repository + Mapper）
</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">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">-</span> Ticket A 跨越 3 層，依賴關係複雜
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">-</span> 測試困難（需要 Mock 多層）
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">正確：按架構分層拆分
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">-</span> Ticket 1：Rating Domain 模型（Layer 5）
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">-</span> Ticket 2：IRatingRepository + Impl（Layer 4-5）
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">-</span> Ticket 3：RateBookUseCase（Layer 3）
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">-</span> Ticket 4：RatingController + Widget（Layer 1-2）
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl">優點：
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">-</span> 每個 Ticket 職責單一
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">-</span> 依賴方向清楚
</span></span><span class="line"><span class="ln">22</span><span class="cl">- 測試獨立簡單</span></span></code></pre></div><hr>
<h4 id="實踐-2保持線性依賴順序">實踐 2：保持線性依賴順序</h4>
<p><strong>原則</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">優先設計線性或樹狀依賴結構，
</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">線性依賴：Ticket 1 → Ticket 2 → Ticket 3 → Ticket 4
</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">   Ticket 1
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">   ├─ Ticket 2
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">   │  └─ Ticket 4
</span></span><span class="line"><span class="ln">10</span><span class="cl">   └─ Ticket 3
</span></span><span class="line"><span class="ln">11</span><span class="cl">      └─ Ticket 5
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">避免結構：
</span></span><span class="line"><span class="ln">14</span><span class="cl">網狀依賴：多個交叉依賴，難以追蹤</span></span></code></pre></div><p><strong>實施方法</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">步驟 1：識別「核心 Ticket」
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="k">-</span> Domain 層 Ticket 通常是核心
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="k">-</span> 其他層依賴 Domain 層
</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">步驟 2：按照 Clean Architecture 依賴方向排序
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">-</span> Layer 5 → Layer 4 → Layer 3 → Layer 2 → Layer 1
</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">步驟 3：標記 Phase
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">-</span> Phase 1：Layer 5（Domain）
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">-</span> Phase 2：Layer 4-5（Repository）
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">-</span> Phase 3：Layer 3（UseCase）
</span></span><span class="line"><span class="ln">12</span><span class="cl">- Phase 4：Layer 1-2（Presentation）</span></span></code></pre></div><hr>
<h4 id="實踐-3每個-ticket-包含完整測試">實踐 3：每個 Ticket 包含完整測試</h4>
<p><strong>原則</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">每個 Ticket 應該包含對應的測試，
</span></span><span class="line"><span class="ln">2</span><span class="cl">不要將測試獨立為單獨 Ticket（除非測試數量 &gt; 10）。
</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">TDD 紅綠燈循環完整
</span></span><span class="line"><span class="ln">6</span><span class="cl">每個 Ticket 交付時就是可測試的
</span></span><span class="line"><span class="ln">7</span><span class="cl">避免「實作完成但測試缺失」的情況</span></span></code></pre></div><p><strong>實施方法</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">在 Ticket 驗收條件中明確測試要求：
</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 class="gu">## Ticket #X: 實作 Rating Value Object
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> Rating 類別實作完整
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">- [ ]</span> 單元測試 100% 通過（至少 3 個測試）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="k">-</span> 測試 1：建立有效評分
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="k">-</span> 測試 2：評分過低拋出異常
</span></span><span class="line"><span class="ln">11</span><span class="cl">  <span class="k">-</span> 測試 3：評分過高拋出異常
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">- [ ]</span> 測試覆蓋率 &gt; 90%
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">例外情況（測試獨立 Ticket）：
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">-</span> 測試用例 &gt; 10 個（測試複雜度 Level 4）
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">-</span> 補充現有程式碼的測試（非新功能）
</span></span><span class="line"><span class="ln">19</span><span class="cl">- 整合測試（跨多層驗證）</span></span></code></pre></div><hr>
<h4 id="實踐-4明確標記層級和策略">實踐 4：明確標記層級和策略</h4>
<p><strong>原則</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">每個 Ticket 標題應包含：
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="k">1.</span> [Layer X] 標籤（標示架構層級）
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="k">2.</span> 策略類型（Interface / 實作 / 整合）
</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">格式：
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="gu">## Ticket #NNN: [Layer X] {動詞} {目標}
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">範例：
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">好的標題：
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">-</span> [Layer 4] 定義 IBookRepository 介面
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">-</span> [Layer 5] 實作 SQLiteBookRepository
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">-</span> [Layer 3] 實作 GetBookUseCase
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">不好的標題：
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">-</span> 書籍資料存取功能（沒有層級、目標不明確）
</span></span><span class="line"><span class="ln">18</span><span class="cl">- 實作 Repository（沒有層級、範圍不清楚）</span></span></code></pre></div><p><strong>實施方法</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">在 Ticket 範本中包含：
</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 class="gu">### 層級：Layer X (層級名稱)
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu">### 策略：策略 N（策略名稱）
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gu"></span>
</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 class="gu">## Ticket #101: [Layer 4] 定義 IBookRepository 介面
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">### 層級：Layer 4 (Domain Interfaces)
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu">### 策略：策略 1（Interface 定義）
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu">### 目標
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu"></span>建立 <span class="sb">`IBookRepository`</span> 介面，定義書籍資料存取的契約
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">這樣可以：
</span></span><span class="line"><span class="ln">16</span><span class="cl">快速識別 Ticket 的架構位置
</span></span><span class="line"><span class="ln">17</span><span class="cl">理解 Ticket 的目的（定義 / 實作 / 整合）
</span></span><span class="line"><span class="ln">18</span><span class="cl">追蹤依賴關係（內層 → 外層）</span></span></code></pre></div><hr>
<h2 id="附錄-a術語表">附錄 A：術語表</h2>
<table>
  <thead>
      <tr>
          <th>術語</th>
          <th>英文</th>
          <th>定義</th>
          <th>範例</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>職責</strong></td>
          <td>Responsibility</td>
          <td>Ticket 需要完成的獨立功能點或邊界條件</td>
          <td>定義介面、實作方法、處理異常</td>
      </tr>
      <tr>
          <td><strong>複雜度等級</strong></td>
          <td>Complexity Level</td>
          <td>基於 4 個量化指標計算的任務複雜度（Level 1-4）</td>
          <td>Level 2（中等）</td>
      </tr>
      <tr>
          <td><strong>God Ticket</strong></td>
          <td>God Ticket</td>
          <td>範圍過大、無法管理的任務（Level 4）</td>
          <td>包含 8 個檔案、300 行程式碼的任務</td>
      </tr>
      <tr>
          <td><strong>單層修改原則</strong></td>
          <td>Single Layer Modification</td>
          <td>每個 Ticket 應只修改單一架構層級</td>
          <td>Ticket 只修改 Domain 層</td>
      </tr>
      <tr>
          <td><strong>依賴規則</strong></td>
          <td>Dependency Rule</td>
          <td>Clean Architecture 的依賴方向規則（外層→內層）</td>
          <td>UseCase 依賴 Repository Interface</td>
      </tr>
      <tr>
          <td><strong>最小可交付單元</strong></td>
          <td>Minimum Deliverable Unit</td>
          <td>可獨立完成、測試、交付的最小功能單元</td>
          <td>實作單一 Value Object</td>
      </tr>
      <tr>
          <td><strong>指標整合評估</strong></td>
          <td>Integrated Indicator Assessment</td>
          <td>取 4 個指標中最高的複雜度等級</td>
          <td>max(Level 2, Level 3, Level 1, Level 2) = Level 3</td>
      </tr>
      <tr>
          <td><strong>拆分策略</strong></td>
          <td>Splitting Strategy</td>
          <td>將大任務拆分為小任務的標準方法</td>
          <td>按架構分層拆分</td>
      </tr>
      <tr>
          <td><strong>Phase</strong></td>
          <td>Phase</td>
          <td>依賴順序的執行階段</td>
          <td>Phase 1（Domain 層）→ Phase 2（Repository 層）</td>
      </tr>
  </tbody>
</table>
<hr>
<h2 id="附錄-b拆分決策模板">附錄 B：拆分決策模板</h2>
<p><strong>模板用途</strong>：用於記錄 Ticket 拆分的決策過程</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gh"># Ticket 拆分決策記錄
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gh"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">## 基本資訊
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span><span class="k">-</span> **原始任務**：[任務描述]
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">-</span> **業務需求**：[需求編號]
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">-</span> **評估日期**：YYYY-MM-DD
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">-</span> **評估人員**：[姓名]
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">## 指標評估
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu">### 職責數量
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu"></span><span class="k">-</span> **職責列表**：
</span></span><span class="line"><span class="ln">13</span><span class="cl">  <span class="k">1.</span> [職責 1]
</span></span><span class="line"><span class="ln">14</span><span class="cl">  <span class="k">2.</span> [職責 2]
</span></span><span class="line"><span class="ln">15</span><span class="cl">  ...
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">-</span> **總計**：X 個
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">-</span> **等級**：Level X
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="gu">### 程式碼行數
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="gu"></span><span class="k">-</span> **預估行數**：X 行
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">-</span> **依據**：[參考類似任務或經驗估算]
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">-</span> **等級**：Level X
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="gu">### 涉及檔案
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="gu"></span><span class="k">-</span> **檔案列表**：
</span></span><span class="line"><span class="ln">26</span><span class="cl">  <span class="k">1.</span> [檔案路徑 1]
</span></span><span class="line"><span class="ln">27</span><span class="cl">  <span class="k">2.</span> [檔案路徑 2]
</span></span><span class="line"><span class="ln">28</span><span class="cl">  ...
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="k">-</span> **總計**：X 個
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="k">-</span> **等級**：Level X
</span></span><span class="line"><span class="ln">31</span><span class="cl">
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="gu">### 測試用例數
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="gu"></span><span class="k">-</span> **測試列表**：
</span></span><span class="line"><span class="ln">34</span><span class="cl">  <span class="k">1.</span> [測試案例 1]
</span></span><span class="line"><span class="ln">35</span><span class="cl">  <span class="k">2.</span> [測試案例 2]
</span></span><span class="line"><span class="ln">36</span><span class="cl">  ...
</span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="k">-</span> **總計**：X 個
</span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="k">-</span> **等級**：Level X
</span></span><span class="line"><span class="ln">39</span><span class="cl">
</span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="gu">## 複雜度結論
</span></span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="gu"></span><span class="k">-</span> **最高等級**：Level X
</span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="k">-</span> **最終判定**：[簡單 / 中等 / 複雜 / 必須拆分]
</span></span><span class="line"><span class="ln">43</span><span class="cl">
</span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="gu">## 拆分決策
</span></span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="gu">### 決策結果
</span></span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 不拆分（Level 1-2）
</span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="k">- [ ]</span> 評估拆分（Level 3）
</span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="k">- [ ]</span> 強制拆分（Level 4）
</span></span><span class="line"><span class="ln">50</span><span class="cl">
</span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="gu">### 拆分原因（如適用）
</span></span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="gu"></span>[說明為什麼需要拆分]
</span></span><span class="line"><span class="ln">53</span><span class="cl">
</span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="gu">### 拆分策略（如適用）
</span></span></span><span class="line"><span class="ln">55</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 按架構分層拆分（優先）
</span></span><span class="line"><span class="ln">56</span><span class="cl"><span class="k">- [ ]</span> 按職責拆分（次之）
</span></span><span class="line"><span class="ln">57</span><span class="cl"><span class="k">- [ ]</span> 按檔案拆分（最後）
</span></span><span class="line"><span class="ln">58</span><span class="cl">
</span></span><span class="line"><span class="ln">59</span><span class="cl"><span class="gu">## 拆分方案（如適用）
</span></span></span><span class="line"><span class="ln">60</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">61</span><span class="cl"><span class="gu">### 子 Ticket 列表
</span></span></span><span class="line"><span class="ln">62</span><span class="cl"><span class="gu"></span><span class="k">1.</span> <span class="gs">**Ticket A**</span>：[標題]
</span></span><span class="line"><span class="ln">63</span><span class="cl">   <span class="k">-</span> 層級：Layer X
</span></span><span class="line"><span class="ln">64</span><span class="cl">   <span class="k">-</span> 指標：職責 X、行數 X、檔案 X、測試 X
</span></span><span class="line"><span class="ln">65</span><span class="cl">   <span class="k">-</span> 等級：Level X
</span></span><span class="line"><span class="ln">66</span><span class="cl">
</span></span><span class="line"><span class="ln">67</span><span class="cl"><span class="k">2.</span> <span class="gs">**Ticket B**</span>：[標題]
</span></span><span class="line"><span class="ln">68</span><span class="cl">   <span class="k">-</span> 層級：Layer X
</span></span><span class="line"><span class="ln">69</span><span class="cl">   <span class="k">-</span> 指標：職責 X、行數 X、檔案 X、測試 X
</span></span><span class="line"><span class="ln">70</span><span class="cl">   <span class="k">-</span> 等級：Level X
</span></span><span class="line"><span class="ln">71</span><span class="cl">
</span></span><span class="line"><span class="ln">72</span><span class="cl"><span class="gu">### 依賴關係
</span></span></span><span class="line"><span class="ln">73</span><span class="cl"><span class="gu"></span>```text
</span></span><span class="line"><span class="ln">74</span><span class="cl">[依賴關係圖或順序說明]</span></span></code></pre></div><h3 id="執行順序">執行順序</h3>
<ul>
<li>Phase 1：[Ticket A, Ticket B]</li>
<li>Phase 2：[Ticket C]</li>
<li>&hellip;</li>
</ul>
<h2 id="驗證清單">驗證清單</h2>
<h3 id="拆分前檢查">拆分前檢查</h3>
<ul>
<li><input disabled="" type="checkbox"> 已計算所有 4 個指標</li>
<li><input disabled="" type="checkbox"> 已確定複雜度等級</li>
<li><input disabled="" type="checkbox"> 已確定是否拆分</li>
</ul>
<h3 id="拆分後檢查如適用">拆分後檢查（如適用）</h3>
<ul>
<li><input disabled="" type="checkbox"> 所有子 Ticket 已設計</li>
<li><input disabled="" type="checkbox"> 所有子 Ticket ≤ Level 3</li>
<li><input disabled="" type="checkbox"> 依賴關係明確</li>
<li><input disabled="" type="checkbox"> 功能完整性確認</li>
</ul>
<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><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">## 附錄 C：與其他方法論的整合指引
</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">### C.1 與 TDD 四階段方法論整合
</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">**整合點**：
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">```markdown
</span></span><span class="line"><span class="ln">11</span><span class="cl">本方法論位於 TDD Phase 1（設計階段）
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">TDD 流程整合：
</span></span><span class="line"><span class="ln">14</span><span class="cl">Phase 1: Ticket 設計
</span></span><span class="line"><span class="ln">15</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">16</span><span class="cl">  使用本方法論評估 Ticket 複雜度
</span></span><span class="line"><span class="ln">17</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">18</span><span class="cl">  如為 Level 3-4，執行拆分
</span></span><span class="line"><span class="ln">19</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">20</span><span class="cl">  所有 Ticket 確認為 Level 1-2
</span></span><span class="line"><span class="ln">21</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">22</span><span class="cl">Phase 2: 測試設計（sage-test-architect）
</span></span><span class="line"><span class="ln">23</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">24</span><span class="cl">Phase 3: 實作（pepper → parsley）
</span></span><span class="line"><span class="ln">25</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">26</span><span class="cl">Phase 4: 重構（cinnamon-refactor-owl）</span></span></code></pre></div><p><strong>協作方式</strong>：</p>
<ul>
<li>Phase 1 完成時，所有 Ticket 都應該 ≤ Level 3</li>
<li>理想狀態：所有 Ticket 都是 Level 1-2</li>
<li>如有 Level 3 Ticket，應標記為「需要額外 Review」</li>
</ul>
<hr>
<h3 id="c2-與層級隔離派工方法論整合">C.2 與層級隔離派工方法論整合</h3>
<p><strong>整合點</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">本方法論提供「拆分標準」
</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"><span class="k">1.</span> 使用本方法論拆分 Ticket（按架構分層）
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="k">2.</span> 使用層級隔離派工方法論決定執行順序
</span></span><span class="line"><span class="ln">7</span><span class="cl">   <span class="k">-</span> 優先派發內層 Ticket（Layer 5）
</span></span><span class="line"><span class="ln">8</span><span class="cl">   <span class="k">-</span> 逐步派發外層 Ticket（Layer 1）
</span></span><span class="line"><span class="ln">9</span><span class="cl">3. 確保依賴順序符合 Clean Architecture 規則</span></span></code></pre></div><p><strong>範例</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">拆分結果（本方法論）：
</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 class="k">-</span> Ticket 1: Layer 5 Domain
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="k">-</span> Ticket 2: Layer 4-5 Repository
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">-</span> Ticket 3: Layer 3 UseCase
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">-</span> Ticket 4: Layer 1-2 Presentation
</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">派工順序（層級隔離派工方法論）：
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">Week 1:
</span></span><span class="line"><span class="ln">10</span><span class="cl">  ├─ 派發 Ticket 1（Layer 5）給 Developer A
</span></span><span class="line"><span class="ln">11</span><span class="cl">  ├─ Ticket 1 完成後，派發 Ticket 2（Layer 4-5）給 Developer A
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">Week 2:
</span></span><span class="line"><span class="ln">14</span><span class="cl">  ├─ Ticket 2 完成後，派發 Ticket 3（Layer 3）給 Developer B
</span></span><span class="line"><span class="ln">15</span><span class="cl">  └─ 可同時派發 Ticket 4（Layer 1-2）給 Developer C（部分並行）</span></span></code></pre></div><hr>
<h3 id="c3-與-code-smell-品質閘門檢測方法論整合">C.3 與 Code Smell 品質閘門檢測方法論整合</h3>
<p><strong>整合點</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">本方法論在 Ticket 設計階段執行（Design Time）
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">Code Smell 品質閘門在 Ticket 執行後檢測（Runtime）
</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">設計階段（本方法論）：
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  ├─ 計算 4 個指標
</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">  ├─ 決定是否拆分
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  └─ 確保 Ticket ≤ Level 3
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">執行階段（Code Smell 檢測）：
</span></span><span class="line"><span class="ln">12</span><span class="cl">  ├─ C1 檢測：God Ticket（檔案數、層級跨度）
</span></span><span class="line"><span class="ln">13</span><span class="cl">  ├─ C2 檢測：Incomplete Ticket（缺失元素）
</span></span><span class="line"><span class="ln">14</span><span class="cl">  └─ C3 檢測：Ambiguous Responsibility（職責不明）
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">如果 Code Smell 檢測失敗：
</span></span><span class="line"><span class="ln">17</span><span class="cl">  ├─ C1 失敗 → 返回本方法論重新拆分
</span></span><span class="line"><span class="ln">18</span><span class="cl">  ├─ C2 失敗 → 補充缺失元素
</span></span><span class="line"><span class="ln">19</span><span class="cl">  └─ C3 失敗 → 明確職責定義</span></span></code></pre></div><p><strong>防範措施</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">如果在設計階段使用本方法論：
</span></span><span class="line"><span class="ln">2</span><span class="cl">C1 God Ticket 失敗率應該降至 0%
</span></span><span class="line"><span class="ln">3</span><span class="cl">C3 Ambiguous Responsibility 失敗率應該降至接近 0%
</span></span><span class="line"><span class="ln">4</span><span class="cl">C2 Incomplete Ticket 仍需要 Code Smell 檢測補強</span></span></code></pre></div><hr>
<h3 id="c4-與-clean-architecture-實作方法論整合">C.4 與 Clean Architecture 實作方法論整合</h3>
<p><strong>整合點</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">本方法論的「架構分層拆分策略」（第三章）
</span></span><span class="line"><span class="ln">2</span><span class="cl">完全基於 Clean Architecture 實作方法論的五層架構。
</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">本方法論 → 依賴 Clean Architecture 實作方法論
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="k">-</span> 採用相同的五層架構定義
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="k">-</span> 遵循相同的依賴規則
</span></span><span class="line"><span class="ln">8</span><span class="cl">- 使用相同的檔案路徑規範</span></span></code></pre></div><p><strong>參考章節對應</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">本方法論第三章「Clean Architecture 分層拆分策略」
</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">Clean Architecture 實作方法論：
</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"><span class="k">-</span> 第一章：五層架構定義
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="k">-</span> 第二章：依賴規則
</span></span><span class="line"><span class="ln">7</span><span class="cl">- 第三章：檔案組織規範</span></span></code></pre></div><hr>
<h2 id="總結">總結</h2>
<h3 id="方法論核心價值">方法論核心價值</h3>
<p>本方法論提供了<strong>量化、客觀、可執行</strong>的 Ticket 拆分標準，解決了傳統拆分方法的主觀性和不一致性問題。</p>
<h3 id="快速開始指引">快速開始指引</h3>
<p><strong>第一步</strong>：評估 Ticket 複雜度</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">1.</span> 計算 4 個指標（職責、行數、檔案、測試）
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="k">2.</span> 取最高複雜度等級
</span></span><span class="line"><span class="ln">3</span><span class="cl">3. 判斷是否需要拆分</span></span></code></pre></div><p><strong>第二步</strong>：執行拆分（如需要）</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">1.</span> 選擇拆分策略（優先：架構分層）
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="k">2.</span> 設計子 Ticket
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="k">3.</span> 重新評估每個子 Ticket
</span></span><span class="line"><span class="ln">4</span><span class="cl">4. 確認所有子 Ticket ≤ Level 3</span></span></code></pre></div><p><strong>第三步</strong>：驗證和記錄</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">1.</span> 使用檢查清單驗證（第六章）
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="k">2.</span> 記錄決策過程（附錄 B 模板）
</span></span><span class="line"><span class="ln">3</span><span class="cl">3. 進入 TDD Phase 2（測試設計）</span></span></code></pre></div><h3 id="相關方法論引用">相關方法論引用</h3>
<p>完整的 Ticket 設計派工流程請參考：</p>
<ul>
<li><a href="/blog/record/ticket-%E8%A8%AD%E8%A8%88%E6%B4%BE%E5%B7%A5%E6%96%B9%E6%B3%95%E8%AB%96/" data-link-title="Ticket 設計派工方法論" data-link-desc="我們如何用 Ticket 機制解決大型開發任務的協作困境：從工作日誌臃腫到積極派發的實踐歷程。">Ticket 設計派工方法論</a> - 主方法論</li>
<li><a href="/blog/record/clean-architecture-%E5%AF%A6%E4%BD%9C%E6%8C%87%E5%BC%95/" data-link-title="Clean Architecture 實作指引" data-link-desc="我們在 AI 協作開發中引入 Clean Architecture 作為任務分派的核心判斷框架。這篇文章整理了四層架構的設計順序、實作順序，以及我們實際執行時的關鍵檢查點。">Clean Architecture 實作方法論</a> - 架構基礎</li>
<li><a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> - 派工策略</li>
<li>TDD 四階段方法論 - 開發流程</li>
</ul>
<hr>
]]></content:encoded></item><item><title>Code Smell 檢查清單</title><link>https://tarrragon.github.io/blog/record/code-smell-%E6%AA%A2%E6%9F%A5%E6%B8%85%E5%96%AE/</link><pubDate>Sat, 11 Oct 2025 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/code-smell-%E6%AA%A2%E6%9F%A5%E6%B8%85%E5%96%AE/</guid><description>&lt;h2 id="基本資訊">基本資訊&lt;/h2>
&lt;p>&lt;strong>目的&lt;/strong>: 提供基於 Ticket 粒度的 Code Smell 檢測標準和檢查清單&lt;/p>
&lt;p>&lt;strong>與其他方法論的關係&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>引用&lt;a href="https://tarrragon.github.io/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論&lt;/a> 的五層架構定義&lt;/li>
&lt;li>配合 TDD 四階段流程使用&lt;/li>
&lt;li>整合到 Hook 系統自動化檢測&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>核心理念&lt;/strong>:
Code Smell 檢查清單是基於層級隔離原則的程式品質檢測工具，從 Ticket 設計階段就能發現潛在的架構問題，實現「預防勝於治療」的品質管理策略。&lt;/p>
&lt;hr>
&lt;h2 id="第一章code-smell-概述和分類">第一章：Code Smell 概述和分類&lt;/h2>
&lt;h3 id="11-什麼是-code-smell">1.1 什麼是 Code Smell&lt;/h3>
&lt;p>&lt;strong>定義&lt;/strong>: Code Smell（程式異味）是指程式碼中表面上看似正常，但實際上暗示設計問題或潛在缺陷的特徵。&lt;/p>
&lt;p>&lt;strong>核心特性&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>不是 Bug&lt;/strong>: Code Smell 不會導致程式崩潰或功能錯誤&lt;/li>
&lt;li>&lt;strong>設計問題&lt;/strong>: 暗示程式架構或設計上的缺陷&lt;/li>
&lt;li>&lt;strong>可檢測&lt;/strong>: 透過明確的指標可以識別&lt;/li>
&lt;li>&lt;strong>可修正&lt;/strong>: 透過重構可以消除&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>與 Bug 的區別&lt;/strong>:&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">Bug（程式錯誤）:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">- 導致功能失敗或程式崩潰
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">- 需要立即修正
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">- 透過測試失敗發現
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">Code Smell（程式異味）:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">- 程式功能正常運作
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">- 暗示設計問題，未來可能導致維護困難
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">- 透過程式碼檢視或靜態分析發現
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">- 透過重構改善&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>為什麼 Code Smell 重要&lt;/strong>:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>降低維護成本&lt;/strong>: 及早發現設計問題，避免技術債務累積&lt;/li>
&lt;li>&lt;strong>提升程式碼品質&lt;/strong>: 改善可讀性、可測試性、可擴展性&lt;/li>
&lt;li>&lt;strong>預防未來問題&lt;/strong>: 在問題惡化前進行修正&lt;/li>
&lt;li>&lt;strong>團隊協作&lt;/strong>: 提供統一的品質標準和溝通語言&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h3 id="12-為什麼需要-code-smell-檢查清單">1.2 為什麼需要 Code Smell 檢查清單&lt;/h3>
&lt;p>&lt;strong>傳統問題&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>依賴個人經驗判斷 Code Smell（主觀且不一致）&lt;/li>
&lt;li>問題發現太晚（實作完成後才發現設計缺陷）&lt;/li>
&lt;li>缺少量化標準（難以判斷是否需要重構）&lt;/li>
&lt;li>修正成本高（架構問題需要大規模修改）&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>檢查清單優勢&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>標準化&lt;/strong>: 提供統一的檢測標準，避免主觀判斷&lt;/li>
&lt;li>&lt;strong>及早發現&lt;/strong>: 從 Ticket 設計階段就能發現問題&lt;/li>
&lt;li>&lt;strong>量化指標&lt;/strong>: 明確的數字標準（如行數、層級跨度）&lt;/li>
&lt;li>&lt;strong>降低成本&lt;/strong>: 設計階段修正成本遠低於實作後修正&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Ticket 粒度檢測的價值&lt;/strong>:&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">設計階段檢測 vs 實作階段檢測：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">設計階段（Ticket 粒度）:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">- 修正成本: 低（只需要調整設計）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">- 影響範圍: 小（尚未實作程式碼）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">- 風險: 低（無需修改既有程式碼）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">實作階段（Code Review）:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">- 修正成本: 中（需要重寫部分程式碼）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">- 影響範圍: 中（可能影響多個檔案）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">- 風險: 中（需要回歸測試）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">維護階段（上線後）:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">- 修正成本: 高（需要大規模重構）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">- 影響範圍: 大（可能影響多個模組）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">- 風險: 高（可能引入新 Bug）&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h3 id="13-code-smell-分類體系">1.3 Code Smell 分類體系&lt;/h3>
&lt;p>基於&lt;a href="https://tarrragon.github.io/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論&lt;/a> 的五層架構，本檢查清單將 Code Smell 分為三大類：&lt;/p>
&lt;h4 id="分類-a跨層級-code-smells違反層級隔離原則">&lt;strong>分類 A：跨層級 Code Smells&lt;/strong>（違反層級隔離原則）&lt;/h4>
&lt;p>這類 Code Smell 涉及多個架構層級，違反層級隔離和單層修改原則：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>A1. Shotgun Surgery&lt;/strong>（散彈槍手術）- 單一變更需要修改多個層級&lt;/li>
&lt;li>&lt;strong>A2. Feature Envy&lt;/strong>（功能嫉妒）- 外層過度依賴內層實作細節&lt;/li>
&lt;li>&lt;strong>A3. Inappropriate Intimacy&lt;/strong>（不當親密關係）- 層級間過度耦合&lt;/li>
&lt;li>&lt;strong>A4. Leaky Abstraction&lt;/strong>（抽象滲漏）- 內層實作細節洩漏到外層&lt;/li>
&lt;/ul>
&lt;h4 id="分類-b單層級-code-smells違反單一職責原則">&lt;strong>分類 B：單層級 Code Smells&lt;/strong>（違反單一職責原則）&lt;/h4>
&lt;p>這類 Code Smell 發生在單一層級內，違反單一職責原則（SRP）：&lt;/p></description><content:encoded><![CDATA[<h2 id="基本資訊">基本資訊</h2>
<p><strong>目的</strong>: 提供基於 Ticket 粒度的 Code Smell 檢測標準和檢查清單</p>
<p><strong>與其他方法論的關係</strong>:</p>
<ul>
<li>引用<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a>  的五層架構定義</li>
<li>配合 TDD 四階段流程使用</li>
<li>整合到 Hook 系統自動化檢測</li>
</ul>
<p><strong>核心理念</strong>:
Code Smell 檢查清單是基於層級隔離原則的程式品質檢測工具，從 Ticket 設計階段就能發現潛在的架構問題，實現「預防勝於治療」的品質管理策略。</p>
<hr>
<h2 id="第一章code-smell-概述和分類">第一章：Code Smell 概述和分類</h2>
<h3 id="11-什麼是-code-smell">1.1 什麼是 Code Smell</h3>
<p><strong>定義</strong>: Code Smell（程式異味）是指程式碼中表面上看似正常，但實際上暗示設計問題或潛在缺陷的特徵。</p>
<p><strong>核心特性</strong>:</p>
<ul>
<li><strong>不是 Bug</strong>: Code Smell 不會導致程式崩潰或功能錯誤</li>
<li><strong>設計問題</strong>: 暗示程式架構或設計上的缺陷</li>
<li><strong>可檢測</strong>: 透過明確的指標可以識別</li>
<li><strong>可修正</strong>: 透過重構可以消除</li>
</ul>
<p><strong>與 Bug 的區別</strong>:</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">Bug（程式錯誤）:
</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">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">Code Smell（程式異味）:
</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">- 暗示設計問題，未來可能導致維護困難
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- 透過程式碼檢視或靜態分析發現
</span></span><span class="line"><span class="ln">10</span><span class="cl">- 透過重構改善</span></span></code></pre></div><p><strong>為什麼 Code Smell 重要</strong>:</p>
<ol>
<li><strong>降低維護成本</strong>: 及早發現設計問題，避免技術債務累積</li>
<li><strong>提升程式碼品質</strong>: 改善可讀性、可測試性、可擴展性</li>
<li><strong>預防未來問題</strong>: 在問題惡化前進行修正</li>
<li><strong>團隊協作</strong>: 提供統一的品質標準和溝通語言</li>
</ol>
<hr>
<h3 id="12-為什麼需要-code-smell-檢查清單">1.2 為什麼需要 Code Smell 檢查清單</h3>
<p><strong>傳統問題</strong>:</p>
<ul>
<li>依賴個人經驗判斷 Code Smell（主觀且不一致）</li>
<li>問題發現太晚（實作完成後才發現設計缺陷）</li>
<li>缺少量化標準（難以判斷是否需要重構）</li>
<li>修正成本高（架構問題需要大規模修改）</li>
</ul>
<p><strong>檢查清單優勢</strong>:</p>
<ul>
<li><strong>標準化</strong>: 提供統一的檢測標準，避免主觀判斷</li>
<li><strong>及早發現</strong>: 從 Ticket 設計階段就能發現問題</li>
<li><strong>量化指標</strong>: 明確的數字標準（如行數、層級跨度）</li>
<li><strong>降低成本</strong>: 設計階段修正成本遠低於實作後修正</li>
</ul>
<p><strong>Ticket 粒度檢測的價值</strong>:</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">設計階段檢測 vs 實作階段檢測：
</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">設計階段（Ticket 粒度）:
</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">- 影響範圍: 小（尚未實作程式碼）
</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">實作階段（Code Review）:
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- 修正成本: 中（需要重寫部分程式碼）
</span></span><span class="line"><span class="ln">10</span><span class="cl">- 影響範圍: 中（可能影響多個檔案）
</span></span><span class="line"><span class="ln">11</span><span class="cl">- 風險: 中（需要回歸測試）
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">維護階段（上線後）:
</span></span><span class="line"><span class="ln">14</span><span class="cl">- 修正成本: 高（需要大規模重構）
</span></span><span class="line"><span class="ln">15</span><span class="cl">- 影響範圍: 大（可能影響多個模組）
</span></span><span class="line"><span class="ln">16</span><span class="cl">- 風險: 高（可能引入新 Bug）</span></span></code></pre></div><hr>
<h3 id="13-code-smell-分類體系">1.3 Code Smell 分類體系</h3>
<p>基於<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a>  的五層架構，本檢查清單將 Code Smell 分為三大類：</p>
<h4 id="分類-a跨層級-code-smells違反層級隔離原則"><strong>分類 A：跨層級 Code Smells</strong>（違反層級隔離原則）</h4>
<p>這類 Code Smell 涉及多個架構層級，違反層級隔離和單層修改原則：</p>
<ul>
<li><strong>A1. Shotgun Surgery</strong>（散彈槍手術）- 單一變更需要修改多個層級</li>
<li><strong>A2. Feature Envy</strong>（功能嫉妒）- 外層過度依賴內層實作細節</li>
<li><strong>A3. Inappropriate Intimacy</strong>（不當親密關係）- 層級間過度耦合</li>
<li><strong>A4. Leaky Abstraction</strong>（抽象滲漏）- 內層實作細節洩漏到外層</li>
</ul>
<h4 id="分類-b單層級-code-smells違反單一職責原則"><strong>分類 B：單層級 Code Smells</strong>（違反單一職責原則）</h4>
<p>這類 Code Smell 發生在單一層級內，違反單一職責原則（SRP）：</p>
<ul>
<li><strong>B1. Divergent Change</strong>（發散式變更）- 單一類別承擔多個職責</li>
<li><strong>B2. Large Class</strong>（大類別）- 類別過大，職責不清</li>
<li><strong>B3. Long Method</strong>（長方法）- 方法過長，難以理解</li>
<li><strong>B4. Dead Code</strong>（死程式碼）- 永遠不會執行的程式碼</li>
</ul>
<h4 id="分類-cticket-粒度相關-code-smells"><strong>分類 C：Ticket 粒度相關 Code Smells</strong></h4>
<p>這類 Code Smell 與 Ticket 設計和粒度相關：</p>
<ul>
<li><strong>C1. God Ticket</strong>（全能 Ticket）- Ticket 範圍過大，修改過多檔案</li>
<li><strong>C2. Incomplete Ticket</strong>（不完整 Ticket）- Ticket 缺少必要測試或文件</li>
<li><strong>C3. Ambiguous Responsibility</strong>（職責模糊 Ticket）- Ticket 職責定義不明確</li>
</ul>
<h4 id="分類樹狀結構">分類樹狀結構</h4>





<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">Code Smell 分類體系（基於[層級隔離派工方法論](/record/layered-ticket-methodology/) 第 2.2 節五層架構定義）
</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">A. 跨層級 Code Smells（違反層級隔離）
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">   ├─ A1. Shotgun Surgery（散彈槍手術）
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">   ├─ A2. Feature Envy（功能嫉妒）
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">   ├─ A3. Inappropriate Intimacy（不當親密關係）
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">   └─ A4. Leaky Abstraction（抽象滲漏）
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">B. 單層級 Code Smells（違反單一職責）
</span></span><span class="line"><span class="ln">10</span><span class="cl">   ├─ B1. Divergent Change（發散式變更）
</span></span><span class="line"><span class="ln">11</span><span class="cl">   ├─ B2. Large Class（大類別）
</span></span><span class="line"><span class="ln">12</span><span class="cl">   ├─ B3. Long Method（長方法）
</span></span><span class="line"><span class="ln">13</span><span class="cl">   └─ B4. Dead Code（死程式碼）
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">C. Ticket 粒度相關 Code Smells
</span></span><span class="line"><span class="ln">16</span><span class="cl">   ├─ C1. God Ticket（全能 Ticket）
</span></span><span class="line"><span class="ln">17</span><span class="cl">   ├─ C2. Incomplete Ticket（不完整 Ticket）
</span></span><span class="line"><span class="ln">18</span><span class="cl">   └─ C3. Ambiguous Responsibility（職責模糊 Ticket）</span></span></code></pre></div><hr>
<h3 id="14-與層級隔離派工方法論-的關係">1.4 與<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 的關係</h3>
<p><strong>互補關係</strong>:</p>
<ul>
<li>
<p><strong><a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a></strong>: 定義「應該怎麼做」（正面原則）</p>
<ul>
<li>五層架構定義（Layer 1-5）</li>
<li>單層修改原則</li>
<li>Ticket 粒度標準</li>
</ul>
</li>
<li>
<p><strong>本檢查清單</strong>: 定義「不應該怎麼做」（負面模式識別）</p>
<ul>
<li>Code Smell 檢測方法</li>
<li>違規模式識別</li>
<li>重構策略</li>
</ul>
</li>
</ul>
<p><strong>引用關係</strong>:
本檢查清單引用<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a>  的以下章節：</p>
<ul>
<li><strong>2.2 節</strong>: 五層架構完整定義</li>
<li><strong>2.3 節</strong>: 依賴方向規則</li>
<li><strong>3.1 節</strong>: 單層修改原則定義</li>
<li><strong>5.2 節</strong>: Ticket 粒度量化指標</li>
<li><strong>6.2 節</strong>: 檔案路徑分析法</li>
<li><strong>6.5 節</strong>: 違規模式識別</li>
</ul>
<p><strong>無重複定義</strong>:
本文件不重複定義五層架構，所有層級定義都引用<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 第 2.2 節。</p>
<hr>
<h2 id="第二章基於層級隔離的-code-smell-定義">第二章：基於層級隔離的 Code Smell 定義</h2>
<h3 id="21-a-類-code-smell跨層級問題">2.1 A 類 Code Smell（跨層級問題）</h3>
<h4 id="211-a1-shotgun-surgery散彈槍手術">2.1.1 A1. Shotgun Surgery（散彈槍手術）</h4>
<p><strong>定義</strong>:
單一邏輯變更需要同時修改多個架構層級的程式碼，違反「單層修改原則」（<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 第 3.1 節）。</p>
<p><strong>特徵識別</strong>:</p>
<ol>
<li>一個小需求需要修改 UI、Behavior、UseCase、Domain 多層</li>
<li>層級間缺乏適當的抽象介面</li>
<li>變更影響範圍不可控</li>
<li>檔案修改數量 &gt; 5 個且跨 2 個以上層級</li>
</ol>
<p><strong>與<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 的關聯</strong>:</p>
<ul>
<li>違反<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">單層修改原則</a>（3.1 節）</li>
<li>違反<a href="/blog/record/clean-architecture-%E5%AF%A6%E4%BD%9C%E6%8C%87%E5%BC%95/" data-link-title="Clean Architecture 實作指引" data-link-desc="我們在 AI 協作開發中引入 Clean Architecture 作為任務分派的核心判斷框架。這篇文章整理了四層架構的設計順序、實作順序，以及我們實際執行時的關鍵檢查點。">從外而內實作順序</a>（4.1 節）</li>
<li>未遵循<a href="/blog/record/ticket-%E8%A8%AD%E8%A8%88%E6%B4%BE%E5%B7%A5%E6%96%B9%E6%B3%95%E8%AB%96/" data-link-title="Ticket 設計派工方法論" data-link-desc="本方法論提供完整的 Ticket 設計和派工機制，解決大型開發任務的協作效率問題，降低實作偏差風險">Ticket 粒度標準</a>（5.2 節）</li>
</ul>
<p><strong>範例說明</strong>:</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><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">反例 Shotgun Surgery 模式：
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">- Layer 1 (UI): BookDetailWidget 新增 publisher Text
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">- Layer 2 (Behavior): BookDetailController 新增 publisher 屬性
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">- Layer 3 (UseCase): GetBookDetailUseCase 新增 publisher 參數
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">- Layer 5 (Domain): Book Entity 新增 publisher 欄位
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">問題分析：
</span></span><span class="line"><span class="ln">10</span><span class="cl">- 修改 4 個層級（Layer 1, 2, 3, 5）
</span></span><span class="line"><span class="ln">11</span><span class="cl">- 修改至少 4 個檔案
</span></span><span class="line"><span class="ln">12</span><span class="cl">- 違反單層修改原則
</span></span><span class="line"><span class="ln">13</span><span class="cl">- 風險：任一層級修改錯誤都會影響整個功能
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">正例 正確做法（引入 Facade）：
</span></span><span class="line"><span class="ln">16</span><span class="cl">- Phase 1 [Layer 5]: Book Entity 新增 publisher 欄位
</span></span><span class="line"><span class="ln">17</span><span class="cl">- Phase 2 [Layer 3]: BookDetailFacade 更新回傳資料
</span></span><span class="line"><span class="ln">18</span><span class="cl">- Phase 3 [Layer 2]: Presenter 轉換新增 publisher
</span></span><span class="line"><span class="ln">19</span><span class="cl">- Phase 4 [Layer 1]: UI 顯示 publisher
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl">改善效果：
</span></span><span class="line"><span class="ln">22</span><span class="cl">- 每個 Phase 只修改單一層級
</span></span><span class="line"><span class="ln">23</span><span class="cl">- 變更影響範圍可控
</span></span><span class="line"><span class="ln">24</span><span class="cl">- 風險降低</span></span></code></pre></div><p><strong>好壞對比程式碼</strong>:</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">// 反例：Shotgun Surgery：新增欄位需要修改 4 層
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1">// Layer 5 (Domain)
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">Book</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="kd">final</span> <span class="kt">String</span> <span class="n">title</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  <span class="kd">final</span> <span class="n">ISBN</span> <span class="n">isbn</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="kd">final</span> <span class="kt">String</span> <span class="n">publisher</span><span class="p">;</span> <span class="c1">// 新增欄位
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1">// Layer 3 (UseCase)
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">GetBookDetailUseCase</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">&gt;</span> <span class="n">execute</span><span class="p">(</span><span class="kt">String</span> <span class="n">id</span><span class="p">)</span> <span class="kd">async</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="kd">final</span> <span class="n">book</span> <span class="o">=</span> <span class="kd">await</span> <span class="n">repository</span><span class="p">.</span><span class="n">findById</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="c1">// 需要處理 publisher
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"></span>    <span class="k">return</span> <span class="n">book</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="c1">// Layer 2 (Behavior)
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookDetailController</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">  <span class="kt">String</span><span class="o">?</span> <span class="n">publisher</span><span class="p">;</span> <span class="c1">// 新增屬性
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">  <span class="kt">void</span> <span class="n">loadBookDetail</span><span class="p">(</span><span class="kt">String</span> <span class="n">id</span><span class="p">)</span> <span class="kd">async</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="kd">final</span> <span class="n">book</span> <span class="o">=</span> <span class="kd">await</span> <span class="n">getBookDetailUseCase</span><span class="p">.</span><span class="n">execute</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">    <span class="n">publisher</span> <span class="o">=</span> <span class="n">book</span><span class="p">.</span><span class="n">publisher</span><span class="p">;</span> <span class="c1">// 新增處理
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="c1">// Layer 1 (UI)
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookDetailWidget</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">  <span class="n">Widget</span> <span class="n">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">    <span class="k">return</span> <span class="n">Column</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">      <span class="nl">children:</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">        <span class="n">Text</span><span class="p">(</span><span class="n">controller</span><span class="p">.</span><span class="n">title</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">        <span class="n">Text</span><span class="p">(</span><span class="n">controller</span><span class="p">.</span><span class="n">publisher</span> <span class="o">??</span> <span class="s1">&#39;&#39;</span><span class="p">),</span> <span class="c1">// 新增顯示
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="c1"></span>      <span class="p">],</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl">
</span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="c1">// 正例：引入 Facade 隔離變更
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="c1">// Layer 4 (Domain Interface)
</span></span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="c1"></span><span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">IBookDetailFacade</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="n">BookDetailViewModel</span><span class="o">&gt;</span> <span class="n">getBookDetail</span><span class="p">(</span><span class="kt">String</span> <span class="n">id</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">47</span><span class="cl">
</span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="c1">// Layer 3 (UseCase - Facade Implementation)
</span></span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookDetailFacade</span> <span class="kd">implements</span> <span class="n">IBookDetailFacade</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">50</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="n">BookDetailViewModel</span><span class="o">&gt;</span> <span class="n">getBookDetail</span><span class="p">(</span><span class="kt">String</span> <span class="n">id</span><span class="p">)</span> <span class="kd">async</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">51</span><span class="cl">    <span class="kd">final</span> <span class="n">book</span> <span class="o">=</span> <span class="kd">await</span> <span class="n">bookRepository</span><span class="p">.</span><span class="n">findById</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">52</span><span class="cl">    <span class="k">return</span> <span class="n">BookPresenter</span><span class="p">.</span><span class="n">toViewModel</span><span class="p">(</span><span class="n">book</span><span class="p">);</span> <span class="c1">// 統一轉換
</span></span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">55</span><span class="cl">
</span></span><span class="line"><span class="ln">56</span><span class="cl"><span class="c1">// Layer 2 (Behavior - Presenter)
</span></span></span><span class="line"><span class="ln">57</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookPresenter</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">58</span><span class="cl">  <span class="kd">static</span> <span class="n">BookDetailViewModel</span> <span class="n">toViewModel</span><span class="p">(</span><span class="n">Book</span> <span class="n">book</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">59</span><span class="cl">    <span class="k">return</span> <span class="n">BookDetailViewModel</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">60</span><span class="cl">      <span class="nl">title:</span> <span class="n">book</span><span class="p">.</span><span class="n">title</span><span class="p">.</span><span class="n">value</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">61</span><span class="cl">      <span class="nl">isbn:</span> <span class="n">book</span><span class="p">.</span><span class="n">isbn</span><span class="p">.</span><span class="n">value</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">62</span><span class="cl">      <span class="nl">publisher:</span> <span class="n">book</span><span class="p">.</span><span class="n">publisher</span><span class="p">,</span> <span class="c1">// 新增欄位在這裡處理
</span></span></span><span class="line"><span class="ln">63</span><span class="cl"><span class="c1"></span>    <span class="p">);</span>
</span></span><span class="line"><span class="ln">64</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">65</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">66</span><span class="cl">
</span></span><span class="line"><span class="ln">67</span><span class="cl"><span class="c1">// Layer 1 (UI) - 無需修改
</span></span></span><span class="line"><span class="ln">68</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookDetailWidget</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">69</span><span class="cl">  <span class="n">Widget</span> <span class="n">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">70</span><span class="cl">    <span class="k">return</span> <span class="n">Column</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">71</span><span class="cl">      <span class="nl">children:</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">72</span><span class="cl">        <span class="n">Text</span><span class="p">(</span><span class="n">viewModel</span><span class="p">.</span><span class="n">title</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">73</span><span class="cl">        <span class="n">Text</span><span class="p">(</span><span class="n">viewModel</span><span class="p">.</span><span class="n">isbn</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">74</span><span class="cl">        <span class="n">Text</span><span class="p">(</span><span class="n">viewModel</span><span class="p">.</span><span class="n">publisher</span><span class="p">),</span> <span class="c1">// 直接使用 ViewModel
</span></span></span><span class="line"><span class="ln">75</span><span class="cl"><span class="c1"></span>      <span class="p">],</span>
</span></span><span class="line"><span class="ln">76</span><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="ln">77</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">78</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">79</span><span class="cl">
</span></span><span class="line"><span class="ln">80</span><span class="cl"><span class="err">改善效果：</span>
</span></span><span class="line"><span class="ln">81</span><span class="cl"><span class="o">-</span> <span class="err">未來新增欄位只需要修改</span> <span class="n">Layer</span> <span class="m">3</span> <span class="p">(</span><span class="n">Facade</span><span class="p">)</span> <span class="err">和</span> <span class="n">Layer</span> <span class="m">2</span> <span class="p">(</span><span class="n">Presenter</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">82</span><span class="cl"><span class="o">-</span> <span class="n">Layer</span> <span class="m">1</span> <span class="p">(</span><span class="n">UI</span><span class="p">)</span> <span class="err">和</span> <span class="n">Layer</span> <span class="m">5</span> <span class="p">(</span><span class="n">Domain</span><span class="p">)</span> <span class="err">的修改影響已隔離</span></span></span></code></pre></div><hr>
<h4 id="212-a2-feature-envy功能嫉妒">2.1.2 A2. Feature Envy（功能嫉妒）</h4>
<p><strong>定義</strong>:
某層級過度依賴其他層級的實作細節，而非依賴抽象介面。外層直接存取內層的內部狀態，缺乏適當的 DTO 或 ViewModel 轉換。</p>
<p><strong>特徵識別</strong>:</p>
<ol>
<li>外層直接存取內層的內部狀態（如 <code>book.isbn.value</code>）</li>
<li>缺乏適當的 DTO 或 ViewModel 轉換</li>
<li>跨層級的緊耦合</li>
<li>UI 層直接 import Domain Entity</li>
<li>外層存取內層內部欄位次數 &gt; 3 次</li>
</ol>
<p><strong>與<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 的關聯</strong>:</p>
<ul>
<li>違反「依賴倒置原則」（2.3 節）</li>
<li>違反 Layer 2 的「資料轉換職責」（2.2 節 Layer 2 定義）</li>
<li>缺少 Presenter 轉換層</li>
</ul>
<p><strong>範例說明</strong>:</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">// 反例：Feature Envy：UI 直接存取 Domain Entity
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="k">import</span> <span class="s1">&#39;package:book_overview_app/domains/library/entities/book.dart&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1">// 反例：UI 層不應 import Domain Entity
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="kd">class</span> <span class="nc">BookDetailWidget</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="kd">final</span> <span class="n">Book</span> <span class="n">book</span><span class="p">;</span> <span class="c1">// 反例：直接依賴 Domain Entity
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="n">Widget</span> <span class="n">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="k">return</span> <span class="n">Column</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">      <span class="nl">children:</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">        <span class="n">Text</span><span class="p">(</span><span class="n">book</span><span class="p">.</span><span class="n">title</span><span class="p">.</span><span class="n">value</span><span class="p">),</span>        <span class="c1">// 反例：存取內部欄位
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"></span>        <span class="n">Text</span><span class="p">(</span><span class="n">book</span><span class="p">.</span><span class="n">isbn</span><span class="p">.</span><span class="n">value</span><span class="p">),</span>         <span class="c1">// 反例：存取內部欄位
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"></span>        <span class="n">Text</span><span class="p">(</span><span class="n">book</span><span class="p">.</span><span class="n">author</span><span class="p">.</span><span class="n">name</span><span class="p">),</span>        <span class="c1">// 反例：存取內部欄位
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"></span>        <span class="n">Text</span><span class="p">(</span><span class="n">book</span><span class="p">.</span><span class="n">isNewRelease</span><span class="p">()</span> <span class="o">?</span> <span class="s1">&#39;新書&#39;</span> <span class="o">:</span> <span class="s1">&#39;&#39;</span><span class="p">),</span> <span class="c1">// 反例：呼叫 Domain 方法
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1"></span>      <span class="p">],</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="err">問題分析：</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="o">-</span> <span class="n">UI</span> <span class="err">層</span> <span class="k">import</span> <span class="n">Domain</span> <span class="n">Entity</span><span class="err">（違反依賴方向）</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="err">-</span> <span class="n">UI</span> <span class="err">直接存取</span> <span class="n">Entity</span> <span class="err">內部欄位（緊耦合）</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="err">-</span> <span class="n">UI</span> <span class="err">呼叫</span> <span class="n">Domain</span> <span class="err">業務方法（職責混亂）</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="err">-</span> <span class="n">Domain</span> <span class="err">修改會影響</span> <span class="n">UI</span><span class="err">（高風險）</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="err">//</span> <span class="err">正例：透過</span> <span class="n">ViewModel</span> <span class="err">轉換</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="err">//</span> <span class="n">Layer</span> <span class="err">2:</span> <span class="err">定義</span> <span class="n">ViewModel</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="n">import</span> <span class="s1">&#39;package:book_overview_app/presentation/view_models/book_view_model.dart&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="kd">class</span> <span class="nc">BookViewModel</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">  <span class="kd">final</span> <span class="kt">String</span> <span class="n">title</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">  <span class="kd">final</span> <span class="kt">String</span> <span class="n">isbn</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">  <span class="kd">final</span> <span class="kt">String</span> <span class="n">author</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">  <span class="kd">final</span> <span class="kt">bool</span> <span class="n">isNew</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">
</span></span><span class="line"><span class="ln">38</span><span class="cl">  <span class="n">BookViewModel</span><span class="p">({</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">    <span class="kd">required</span> <span class="k">this</span><span class="p">.</span><span class="n">title</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl">    <span class="kd">required</span> <span class="k">this</span><span class="p">.</span><span class="n">isbn</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">    <span class="kd">required</span> <span class="k">this</span><span class="p">.</span><span class="n">author</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">42</span><span class="cl">    <span class="kd">required</span> <span class="k">this</span><span class="p">.</span><span class="n">isNew</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl">
</span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="c1">// Layer 2: Presenter 轉換（資料轉換職責）
</span></span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookPresenter</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl">  <span class="kd">static</span> <span class="n">BookViewModel</span> <span class="n">toViewModel</span><span class="p">(</span><span class="n">Book</span> <span class="n">book</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl">    <span class="k">return</span> <span class="n">BookViewModel</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">50</span><span class="cl">      <span class="nl">title:</span> <span class="n">book</span><span class="p">.</span><span class="n">title</span><span class="p">.</span><span class="n">value</span><span class="p">,</span>      <span class="c1">// 提取內部欄位
</span></span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="c1"></span>      <span class="nl">isbn:</span> <span class="n">book</span><span class="p">.</span><span class="n">isbn</span><span class="p">.</span><span class="n">value</span><span class="p">,</span>        <span class="c1">// 提取內部欄位
</span></span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="c1"></span>      <span class="nl">author:</span> <span class="n">book</span><span class="p">.</span><span class="n">author</span><span class="p">.</span><span class="n">name</span><span class="p">,</span>     <span class="c1">// 提取內部欄位
</span></span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="c1"></span>      <span class="nl">isNew:</span> <span class="n">book</span><span class="p">.</span><span class="n">isNewRelease</span><span class="p">(),</span>   <span class="c1">// 執行 Domain 方法
</span></span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="c1"></span>    <span class="p">);</span>
</span></span><span class="line"><span class="ln">55</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">56</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">57</span><span class="cl">
</span></span><span class="line"><span class="ln">58</span><span class="cl"><span class="c1">// Layer 1: UI 使用 ViewModel
</span></span></span><span class="line"><span class="ln">59</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookDetailWidget</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">60</span><span class="cl">  <span class="kd">final</span> <span class="n">BookViewModel</span> <span class="n">viewModel</span><span class="p">;</span> <span class="c1">// 正例：依賴 ViewModel
</span></span></span><span class="line"><span class="ln">61</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">62</span><span class="cl">  <span class="n">Widget</span> <span class="n">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">63</span><span class="cl">    <span class="k">return</span> <span class="n">Column</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">64</span><span class="cl">      <span class="nl">children:</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">65</span><span class="cl">        <span class="n">Text</span><span class="p">(</span><span class="n">viewModel</span><span class="p">.</span><span class="n">title</span><span class="p">),</span>    <span class="c1">// 正例：使用轉換後的資料
</span></span></span><span class="line"><span class="ln">66</span><span class="cl"><span class="c1"></span>        <span class="n">Text</span><span class="p">(</span><span class="n">viewModel</span><span class="p">.</span><span class="n">isbn</span><span class="p">),</span>     <span class="c1">// 正例：使用轉換後的資料
</span></span></span><span class="line"><span class="ln">67</span><span class="cl"><span class="c1"></span>        <span class="n">Text</span><span class="p">(</span><span class="n">viewModel</span><span class="p">.</span><span class="n">author</span><span class="p">),</span>   <span class="c1">// 正例：使用轉換後的資料
</span></span></span><span class="line"><span class="ln">68</span><span class="cl"><span class="c1"></span>        <span class="n">Text</span><span class="p">(</span><span class="n">viewModel</span><span class="p">.</span><span class="n">isNew</span> <span class="o">?</span> <span class="s1">&#39;新書&#39;</span> <span class="o">:</span> <span class="s1">&#39;&#39;</span><span class="p">),</span> <span class="c1">// 正例：使用轉換後的狀態
</span></span></span><span class="line"><span class="ln">69</span><span class="cl"><span class="c1"></span>      <span class="p">],</span>
</span></span><span class="line"><span class="ln">70</span><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="ln">71</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">72</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">73</span><span class="cl">
</span></span><span class="line"><span class="ln">74</span><span class="cl"><span class="err">改善效果：</span>
</span></span><span class="line"><span class="ln">75</span><span class="cl"><span class="o">-</span> <span class="n">UI</span> <span class="err">層不依賴</span> <span class="n">Domain</span> <span class="n">Entity</span><span class="err">（降低耦合）</span>
</span></span><span class="line"><span class="ln">76</span><span class="cl"><span class="o">-</span> <span class="n">Presenter</span> <span class="err">集中處理資料轉換（符合</span> <span class="n">Layer</span> <span class="m">2</span> <span class="err">職責）</span>
</span></span><span class="line"><span class="ln">77</span><span class="cl"><span class="o">-</span> <span class="n">Domain</span> <span class="err">修改不影響</span> <span class="n">UI</span><span class="err">（只需調整</span> <span class="n">Presenter</span><span class="err">）</span>
</span></span><span class="line"><span class="ln">78</span><span class="cl"><span class="o">-</span> <span class="err">測試更容易（</span><span class="n">Mock</span> <span class="n">ViewModel</span> <span class="err">即可）</span></span></span></code></pre></div><hr>
<h4 id="213-a3-inappropriate-intimacy不當親密關係">2.1.3 A3. Inappropriate Intimacy（不當親密關係）</h4>
<p><strong>定義</strong>:
層級間過度耦合，內層知道外層的存在或依賴外層，違反依賴方向規則。</p>
<p><strong>特徵識別</strong>:</p>
<ol>
<li>Domain 層依賴 UseCase 或 UI 層</li>
<li>依賴方向錯誤（內層依賴外層）</li>
<li>存在循環依賴</li>
<li>Domain Entity 包含 UI 或 Infrastructure 的 import</li>
</ol>
<p><strong>與<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 的關聯</strong>:</p>
<ul>
<li>違反「依賴方向規則」（2.3 節）</li>
<li>違反「Layer 5 不依賴任何層級」原則</li>
<li>正確依賴方向：Layer 1 → Layer 2 → Layer 3 → Layer 4 ← Layer 5</li>
</ul>
<p><strong>範例說明</strong>:</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">// 反例：Inappropriate Intimacy：Domain 依賴 UseCase
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1">// Layer 5 (Domain)
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"></span><span class="k">import</span> <span class="s1">&#39;package:book_overview_app/application/use_cases/add_book_to_favorite_use_case.dart&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1">// 反例：Domain 不應 import UseCase
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="kd">class</span> <span class="nc">Book</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="kd">final</span> <span class="kt">String</span> <span class="n">id</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="kd">final</span> <span class="n">Title</span> <span class="n">title</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="kd">final</span> <span class="n">AddBookToFavoriteUseCase</span> <span class="n">favoriteUseCase</span><span class="p">;</span> <span class="c1">// 反例：Domain 依賴 UseCase
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="kt">void</span> <span class="n">addToFavorite</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="n">favoriteUseCase</span><span class="p">.</span><span class="n">execute</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="n">id</span><span class="p">);</span> <span class="c1">// 反例：Domain 不應呼叫 UseCase
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="err">問題分析：</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="o">-</span> <span class="n">Domain</span> <span class="err">依賴外層（</span><span class="n">UseCase</span><span class="err">）</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="o">-</span> <span class="err">違反依賴方向規則</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="o">-</span> <span class="n">Domain</span> <span class="err">失去獨立性和可重用性</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="o">-</span> <span class="err">測試困難（</span><span class="n">Domain</span> <span class="err">測試需要</span> <span class="n">Mock</span> <span class="n">UseCase</span><span class="err">）</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="c1">// 正例：Domain 只定義業務邏輯
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="c1">// Layer 5 (Domain) - 獨立且純淨
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">Book</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">  <span class="kd">final</span> <span class="kt">String</span> <span class="n">id</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">  <span class="kd">final</span> <span class="n">Title</span> <span class="n">title</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">  <span class="kt">bool</span> <span class="n">isFavorited</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span> <span class="c1">// 正例：只記錄狀態
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">  <span class="kt">void</span> <span class="n">markAsFavorite</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="n">isFavorited</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span> <span class="c1">// 正例：只處理業務邏輯
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">
</span></span><span class="line"><span class="ln">35</span><span class="cl">  <span class="kt">void</span> <span class="n">unmarkFromFavorite</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="n">isFavorited</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">
</span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="c1">// Layer 3 (UseCase) - 協調業務流程
</span></span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">AddBookToFavoriteUseCase</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">42</span><span class="cl">  <span class="kd">final</span> <span class="n">IBookRepository</span> <span class="n">bookRepository</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl">  <span class="kd">final</span> <span class="n">IFavoriteRepository</span> <span class="n">favoriteRepository</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">44</span><span class="cl">
</span></span><span class="line"><span class="ln">45</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">execute</span><span class="p">(</span><span class="kt">String</span> <span class="n">bookId</span><span class="p">)</span> <span class="kd">async</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">46</span><span class="cl">    <span class="c1">// 1. 取得書籍
</span></span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="c1"></span>    <span class="kd">final</span> <span class="n">book</span> <span class="o">=</span> <span class="kd">await</span> <span class="n">bookRepository</span><span class="p">.</span><span class="n">findById</span><span class="p">(</span><span class="n">bookId</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl">
</span></span><span class="line"><span class="ln">49</span><span class="cl">    <span class="c1">// 2. 執行 Domain 方法
</span></span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="c1"></span>    <span class="n">book</span><span class="p">.</span><span class="n">markAsFavorite</span><span class="p">();</span> <span class="c1">// 正例：UseCase 呼叫 Domain 方法
</span></span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">52</span><span class="cl">    <span class="c1">// 3. 儲存狀態
</span></span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="c1"></span>    <span class="kd">await</span> <span class="n">bookRepository</span><span class="p">.</span><span class="n">save</span><span class="p">(</span><span class="n">book</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">54</span><span class="cl">    <span class="kd">await</span> <span class="n">favoriteRepository</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">bookId</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">55</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">56</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">57</span><span class="cl">
</span></span><span class="line"><span class="ln">58</span><span class="cl"><span class="c1">// Layer 2 (Behavior/Controller) - 觸發 UseCase
</span></span></span><span class="line"><span class="ln">59</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookDetailController</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">60</span><span class="cl">  <span class="kd">final</span> <span class="n">AddBookToFavoriteUseCase</span> <span class="n">addToFavoriteUseCase</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">61</span><span class="cl">
</span></span><span class="line"><span class="ln">62</span><span class="cl">  <span class="kt">void</span> <span class="n">onFavoriteButtonPressed</span><span class="p">(</span><span class="kt">String</span> <span class="n">bookId</span><span class="p">)</span> <span class="kd">async</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">63</span><span class="cl">    <span class="kd">await</span> <span class="n">addToFavoriteUseCase</span><span class="p">.</span><span class="n">execute</span><span class="p">(</span><span class="n">bookId</span><span class="p">);</span> <span class="c1">// 正例：呼叫方向
</span></span></span><span class="line"><span class="ln">64</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln">65</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">66</span><span class="cl">
</span></span><span class="line"><span class="ln">67</span><span class="cl"><span class="err">改善效果：</span>
</span></span><span class="line"><span class="ln">68</span><span class="cl"><span class="o">-</span> <span class="n">Domain</span> <span class="err">獨立且純淨（不依賴外層）</span>
</span></span><span class="line"><span class="ln">69</span><span class="cl"><span class="o">-</span> <span class="err">依賴方向正確（</span><span class="n">Layer</span> <span class="m">2</span> <span class="err">→</span> <span class="n">Layer</span> <span class="m">3</span> <span class="err">→</span> <span class="n">Layer</span> <span class="m">5</span><span class="err">）</span>
</span></span><span class="line"><span class="ln">70</span><span class="cl"><span class="o">-</span> <span class="n">Domain</span> <span class="err">可重用性高</span>
</span></span><span class="line"><span class="ln">71</span><span class="cl"><span class="o">-</span> <span class="err">測試容易（</span><span class="n">Domain</span> <span class="err">無外部依賴）</span></span></span></code></pre></div><hr>
<h4 id="214-a4-leaky-abstraction抽象滲漏">2.1.4 A4. Leaky Abstraction（抽象滲漏）</h4>
<p><strong>定義</strong>:
內層的實作細節透過介面洩漏到外層，介面不夠抽象。</p>
<p><strong>特徵識別</strong>:</p>
<ol>
<li>Repository 介面包含資料庫特定參數（如 SQL 語句）</li>
<li>Domain Event 包含 UI 特定資料（如 Widget 狀態）</li>
<li>抽象介面不夠抽象，包含實作關鍵字</li>
<li>介面方法名稱洩漏實作細節</li>
</ol>
<p><strong>與<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 的關聯</strong>:</p>
<ul>
<li>違反 Layer 4「介面契約」的職責定義（2.2 節）</li>
<li>介面應該隱藏實作細節</li>
</ul>
<p><strong>範例說明</strong>:</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">// 反例：Leaky Abstraction：介面洩漏實作細節
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1">// Layer 4 (Domain Interface)
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"></span><span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">IBookRepository</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">&gt;</span> <span class="n">findBySql</span><span class="p">(</span><span class="kt">String</span> <span class="n">sql</span><span class="p">);</span>        <span class="c1">// 反例：洩漏 SQL 實作
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span>  <span class="n">Future</span><span class="o">&lt;</span><span class="n">List</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">&gt;&gt;</span> <span class="n">queryWithCursor</span><span class="p">(</span><span class="n">Cursor</span> <span class="n">cursor</span><span class="p">);</span> <span class="c1">// 反例：洩漏資料庫 Cursor
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"></span>  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">saveToSqlite</span><span class="p">(</span><span class="n">Book</span> <span class="n">book</span><span class="p">);</span>      <span class="c1">// 反例：洩漏 SQLite 實作
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="err">問題分析：</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="o">-</span> <span class="err">介面包含「</span><span class="n">SQL</span><span class="err">」、「</span><span class="n">Cursor</span><span class="err">」、「</span><span class="n">Sqlite</span><span class="err">」等實作關鍵字</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="o">-</span> <span class="err">外層（</span><span class="n">UseCase</span><span class="err">）需要知道使用</span> <span class="n">SQL</span> <span class="err">資料庫</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="o">-</span> <span class="err">無法更換實作（綁定</span> <span class="n">SQLite</span><span class="err">）</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="o">-</span> <span class="err">違反介面契約原則</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1">// 正例：抽象介面
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1">// Layer 4 (Domain Interface) - 抽象且純淨
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="c1"></span><span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">IBookRepository</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">&gt;</span> <span class="n">findById</span><span class="p">(</span><span class="kt">String</span> <span class="n">id</span><span class="p">);</span>          <span class="c1">// 正例：隱藏實作細節
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="c1"></span>  <span class="n">Future</span><span class="o">&lt;</span><span class="n">List</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">&gt;&gt;</span> <span class="n">findByAuthor</span><span class="p">(</span><span class="kt">String</span> <span class="n">author</span><span class="p">);</span> <span class="c1">// 正例：業務概念
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="c1"></span>  <span class="n">Future</span><span class="o">&lt;</span><span class="n">List</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">&gt;&gt;</span> <span class="n">findAll</span><span class="p">();</span>              <span class="c1">// 正例：簡單明確
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="c1"></span>  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">save</span><span class="p">(</span><span class="n">Book</span> <span class="n">book</span><span class="p">);</span>              <span class="c1">// 正例：抽象操作
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="c1"></span>  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">delete</span><span class="p">(</span><span class="kt">String</span> <span class="n">id</span><span class="p">);</span>            <span class="c1">// 正例：抽象操作
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="c1">// Layer 5 (Infrastructure) - 具體實作可替換
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">SqliteBookRepository</span> <span class="kd">implements</span> <span class="n">IBookRepository</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">  <span class="err">@</span><span class="n">override</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">&gt;</span> <span class="n">findById</span><span class="p">(</span><span class="kt">String</span> <span class="n">id</span><span class="p">)</span> <span class="kd">async</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">    <span class="c1">// SQL 實作細節在這裡
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="c1"></span>    <span class="kd">final</span> <span class="n">result</span> <span class="o">=</span> <span class="kd">await</span> <span class="n">db</span><span class="p">.</span><span class="n">query</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">      <span class="s1">&#39;books&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">      <span class="nl">where:</span> <span class="s1">&#39;id = ?&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">      <span class="nl">whereArgs:</span> <span class="p">[</span><span class="n">id</span><span class="p">],</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">    <span class="k">return</span> <span class="n">Book</span><span class="p">.</span><span class="n">fromJson</span><span class="p">(</span><span class="n">result</span><span class="p">.</span><span class="n">first</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl">
</span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="c1">// Layer 5 (Infrastructure) - 另一種實作
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">FirestoreBookRepository</span> <span class="kd">implements</span> <span class="n">IBookRepository</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl">  <span class="err">@</span><span class="n">override</span>
</span></span><span class="line"><span class="ln">44</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">&gt;</span> <span class="n">findById</span><span class="p">(</span><span class="kt">String</span> <span class="n">id</span><span class="p">)</span> <span class="kd">async</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl">    <span class="c1">// Firestore 實作細節在這裡
</span></span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="c1"></span>    <span class="kd">final</span> <span class="n">doc</span> <span class="o">=</span> <span class="kd">await</span> <span class="n">firestore</span><span class="p">.</span><span class="n">collection</span><span class="p">(</span><span class="s1">&#39;books&#39;</span><span class="p">).</span><span class="n">doc</span><span class="p">(</span><span class="n">id</span><span class="p">).</span><span class="kd">get</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">47</span><span class="cl">    <span class="k">return</span> <span class="n">Book</span><span class="p">.</span><span class="n">fromJson</span><span class="p">(</span><span class="n">doc</span><span class="p">.</span><span class="n">data</span><span class="p">()</span><span class="o">!</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">50</span><span class="cl">
</span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="err">改善效果：</span>
</span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="o">-</span> <span class="err">介面抽象且純淨（不包含實作細節）</span>
</span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="o">-</span> <span class="err">可輕鬆更換實作（</span><span class="n">SQLite</span> <span class="err">→</span> <span class="n">Firestore</span><span class="err">）</span>
</span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="o">-</span> <span class="n">UseCase</span> <span class="err">不需要知道資料庫實作</span>
</span></span><span class="line"><span class="ln">55</span><span class="cl"><span class="o">-</span> <span class="err">符合依賴倒置原則</span></span></span></code></pre></div><hr>
<h3 id="22-b-類-code-smell單層級問題">2.2 B 類 Code Smell（單層級問題）</h3>
<h4 id="221-b1-divergent-change發散式變更">2.2.1 B1. Divergent Change（發散式變更）</h4>
<p><strong>定義</strong>:
單一類別因不同原因需要修改，違反 Single Responsibility Principle（SRP）。</p>
<p><strong>特徵識別</strong>:</p>
<ol>
<li>一個 Controller 同時負責多個頁面的邏輯</li>
<li>一個 UseCase 處理多個不相關的業務流程</li>
<li>變更原因不單一（有 2+ 個變更原因）</li>
<li>類別方法可以明確分組（2+ 個分組）</li>
</ol>
<p><strong>與<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 的關聯</strong>:</p>
<ul>
<li>違反「單層修改原則」的 SRP 理論依據（3.2 節）</li>
<li>違反「變更原因單一」要求（3.1 節）</li>
</ul>
<p><strong>範例說明</strong>:</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">// 反例：Divergent Change：單一 Controller 承擔多個職責
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kd">class</span> <span class="nc">BookController</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="c1">// 群組 A：列表頁面邏輯（3 個方法）
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span>  <span class="n">List</span><span class="o">&lt;</span><span class="n">BookViewModel</span><span class="o">&gt;</span> <span class="n">bookList</span> <span class="o">=</span> <span class="p">[];</span>
</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 class="kt">void</span> <span class="n">loadBookList</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="c1">// 載入書籍列表
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">  <span class="kt">void</span> <span class="n">refreshBookList</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="c1">// 重新整理列表
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">  <span class="kt">void</span> <span class="n">sortBookList</span><span class="p">(</span><span class="kt">String</span> <span class="n">sortBy</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="c1">// 排序列表
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl">  <span class="c1">// 群組 B：詳情頁面邏輯（3 個方法）
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="c1"></span>  <span class="n">BookViewModel</span><span class="o">?</span> <span class="n">bookDetail</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl">  <span class="kt">void</span> <span class="n">loadBookDetail</span><span class="p">(</span><span class="kt">String</span> <span class="n">id</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">    <span class="c1">// 載入書籍詳情
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl">  <span class="kt">void</span> <span class="n">updateBookDetail</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">    <span class="c1">// 更新書籍詳情
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">
</span></span><span class="line"><span class="ln">30</span><span class="cl">  <span class="kt">void</span> <span class="n">deleteBook</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">    <span class="c1">// 刪除書籍
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">
</span></span><span class="line"><span class="ln">34</span><span class="cl">  <span class="c1">// 群組 C：搜尋邏輯（2 個方法）
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="c1"></span>  <span class="n">List</span><span class="o">&lt;</span><span class="n">BookViewModel</span><span class="o">&gt;</span> <span class="n">searchResults</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">
</span></span><span class="line"><span class="ln">37</span><span class="cl">  <span class="kt">void</span> <span class="n">searchBooks</span><span class="p">(</span><span class="kt">String</span> <span class="n">query</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">    <span class="c1">// 搜尋書籍
</span></span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl">
</span></span><span class="line"><span class="ln">41</span><span class="cl">  <span class="kt">void</span> <span class="n">clearSearchResults</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">42</span><span class="cl">    <span class="c1">// 清空搜尋結果
</span></span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl">
</span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="err">問題分析：</span>
</span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="o">-</span> <span class="m">3</span> <span class="err">個方法群組（列表、詳情、搜尋）</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="o">-</span> <span class="m">3</span> <span class="err">種變更原因（列表變更、詳情變更、搜尋變更）</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="o">-</span> <span class="err">類別名稱過於籠統（</span><span class="n">BookController</span><span class="err">）</span>
</span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="o">-</span> <span class="err">違反</span> <span class="n">SRP</span> <span class="err">原則</span>
</span></span><span class="line"><span class="ln">51</span><span class="cl">
</span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="c1">// 正例：拆分為多個單一職責 Controller
</span></span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="c1">// Controller 1：只負責列表
</span></span></span><span class="line"><span class="ln">55</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookListController</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">56</span><span class="cl">  <span class="n">List</span><span class="o">&lt;</span><span class="n">BookViewModel</span><span class="o">&gt;</span> <span class="n">bookList</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="ln">57</span><span class="cl">
</span></span><span class="line"><span class="ln">58</span><span class="cl">  <span class="kt">void</span> <span class="n">loadBookList</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">59</span><span class="cl">  <span class="kt">void</span> <span class="n">refreshBookList</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">60</span><span class="cl">  <span class="kt">void</span> <span class="n">sortBookList</span><span class="p">(</span><span class="kt">String</span> <span class="n">sortBy</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">61</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">62</span><span class="cl">
</span></span><span class="line"><span class="ln">63</span><span class="cl"><span class="c1">// Controller 2：只負責詳情
</span></span></span><span class="line"><span class="ln">64</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookDetailController</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">65</span><span class="cl">  <span class="n">BookViewModel</span><span class="o">?</span> <span class="n">bookDetail</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">66</span><span class="cl">
</span></span><span class="line"><span class="ln">67</span><span class="cl">  <span class="kt">void</span> <span class="n">loadBookDetail</span><span class="p">(</span><span class="kt">String</span> <span class="n">id</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">68</span><span class="cl">  <span class="kt">void</span> <span class="n">updateBookDetail</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">69</span><span class="cl">  <span class="kt">void</span> <span class="n">deleteBook</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">70</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">71</span><span class="cl">
</span></span><span class="line"><span class="ln">72</span><span class="cl"><span class="c1">// Controller 3：只負責搜尋
</span></span></span><span class="line"><span class="ln">73</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookSearchController</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">74</span><span class="cl">  <span class="n">List</span><span class="o">&lt;</span><span class="n">BookViewModel</span><span class="o">&gt;</span> <span class="n">searchResults</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="ln">75</span><span class="cl">
</span></span><span class="line"><span class="ln">76</span><span class="cl">  <span class="kt">void</span> <span class="n">searchBooks</span><span class="p">(</span><span class="kt">String</span> <span class="n">query</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">77</span><span class="cl">  <span class="kt">void</span> <span class="n">clearSearchResults</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">78</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">79</span><span class="cl">
</span></span><span class="line"><span class="ln">80</span><span class="cl"><span class="err">改善效果：</span>
</span></span><span class="line"><span class="ln">81</span><span class="cl"><span class="o">-</span> <span class="err">每個</span> <span class="n">Controller</span> <span class="err">只有</span> <span class="m">1</span> <span class="err">個變更原因</span>
</span></span><span class="line"><span class="ln">82</span><span class="cl"><span class="o">-</span> <span class="err">職責明確且單一</span>
</span></span><span class="line"><span class="ln">83</span><span class="cl"><span class="o">-</span> <span class="err">可讀性提升</span>
</span></span><span class="line"><span class="ln">84</span><span class="cl"><span class="o">-</span> <span class="err">測試更容易（測試範圍更小）</span></span></span></code></pre></div><hr>
<h4 id="222-b2-large-class大類別">2.2.2 B2. Large Class（大類別）</h4>
<p><strong>定義</strong>:
類別過大，包含過多方法和屬性，職責不清。</p>
<p><strong>特徵識別（量化標準，引用<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 第 5.2 節）</strong>:</p>
<ul>
<li>總行數: &gt; 300 行</li>
<li>public 方法: &gt; 15 個</li>
<li>屬性: &gt; 12 個</li>
<li>類別職責無法用一句話清楚描述</li>
</ul>
<p><strong>範例說明</strong>:</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">// 反例：Large Class：職責過多（500+ 行）
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kd">class</span> <span class="nc">BookService</span> <span class="p">{</span> <span class="c1">// 總行數：500+ 行
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"></span>  <span class="c1">// 新增書籍（20 個方法）
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span>  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">addBook</span><span class="p">(</span><span class="n">Book</span> <span class="n">book</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">addMultipleBooks</span><span class="p">(</span><span class="n">List</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">&gt;</span> <span class="n">books</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">importBooksFromCsv</span><span class="p">(</span><span class="kt">String</span> <span class="n">filePath</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="c1">// ... 17 個其他方法
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="c1">// 查詢書籍（15 個方法）
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"></span>  <span class="n">Future</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">&gt;</span> <span class="n">findBook</span><span class="p">(</span><span class="kt">String</span> <span class="n">id</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="n">List</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">&gt;&gt;</span> <span class="n">findBooksByAuthor</span><span class="p">(</span><span class="kt">String</span> <span class="n">author</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="n">List</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">&gt;&gt;</span> <span class="n">searchBooks</span><span class="p">(</span><span class="kt">String</span> <span class="n">query</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">  <span class="c1">// ... 12 個其他方法
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="c1">// 統計分析（10 個方法）
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c1"></span>  <span class="n">Future</span><span class="o">&lt;</span><span class="n">BookStats</span><span class="o">&gt;</span> <span class="n">getStatistics</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="n">Map</span><span class="o">&lt;</span><span class="kt">String</span><span class="p">,</span> <span class="kt">int</span><span class="o">&gt;&gt;</span> <span class="n">getBooksByGenre</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="n">List</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">&gt;&gt;</span> <span class="n">getMostPopular</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">  <span class="c1">// ... 7 個其他方法
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">  <span class="c1">// 匯出報表（8 個方法）
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="c1"></span>  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">exportReport</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">exportToPdf</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">exportToExcel</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">  <span class="c1">// ... 5 個其他方法
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="err">問題分析：</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="o">-</span> <span class="err">總行數</span><span class="o">:</span> <span class="m">500</span><span class="o">+</span> <span class="err">行（超過</span> <span class="m">300</span> <span class="err">行標準）</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="o">-</span> <span class="n">public</span> <span class="err">方法</span><span class="o">:</span> <span class="m">53</span> <span class="err">個（超過</span> <span class="m">15</span> <span class="err">個標準）</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="o">-</span> <span class="m">4</span> <span class="err">種不同職責（新增、查詢、統計、匯出）</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="o">-</span> <span class="err">違反</span> <span class="n">SRP</span> <span class="err">原則</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">
</span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="c1">// 正例：拆分為多個職責明確的 Service
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="c1">// Service 1：書籍管理（新增、更新、刪除）
</span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookManagementService</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">addBook</span><span class="p">(</span><span class="n">Book</span> <span class="n">book</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">updateBook</span><span class="p">(</span><span class="n">Book</span> <span class="n">book</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">deleteBook</span><span class="p">(</span><span class="kt">String</span> <span class="n">id</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl">
</span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="c1">// Service 2：書籍查詢
</span></span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookQueryService</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">46</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">&gt;</span> <span class="n">findById</span><span class="p">(</span><span class="kt">String</span> <span class="n">id</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">47</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="n">List</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">&gt;&gt;</span> <span class="n">findByAuthor</span><span class="p">(</span><span class="kt">String</span> <span class="n">author</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="n">List</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">&gt;&gt;</span> <span class="n">search</span><span class="p">(</span><span class="kt">String</span> <span class="n">query</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">50</span><span class="cl">
</span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="c1">// Service 3：書籍統計
</span></span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookStatisticsService</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">53</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="n">BookStats</span><span class="o">&gt;</span> <span class="n">getStatistics</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">54</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="n">Map</span><span class="o">&lt;</span><span class="kt">String</span><span class="p">,</span> <span class="kt">int</span><span class="o">&gt;&gt;</span> <span class="n">getBooksByGenre</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">55</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="n">List</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">&gt;&gt;</span> <span class="n">getMostPopular</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">56</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">57</span><span class="cl">
</span></span><span class="line"><span class="ln">58</span><span class="cl"><span class="c1">// Service 4：報表匯出
</span></span></span><span class="line"><span class="ln">59</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookReportService</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">60</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">exportToPdf</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">61</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">exportToExcel</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">62</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">exportToCsv</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">63</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">64</span><span class="cl">
</span></span><span class="line"><span class="ln">65</span><span class="cl"><span class="err">改善效果：</span>
</span></span><span class="line"><span class="ln">66</span><span class="cl"><span class="o">-</span> <span class="err">每個</span> <span class="n">Service</span> <span class="o">&lt;</span> <span class="m">200</span> <span class="err">行</span>
</span></span><span class="line"><span class="ln">67</span><span class="cl"><span class="o">-</span> <span class="err">職責單一且明確</span>
</span></span><span class="line"><span class="ln">68</span><span class="cl"><span class="o">-</span> <span class="err">可測試性提升</span>
</span></span><span class="line"><span class="ln">69</span><span class="cl"><span class="o">-</span> <span class="err">可維護性提升</span></span></span></code></pre></div><hr>
<h4 id="223-b3-long-method長方法">2.2.3 B3. Long Method（長方法）</h4>
<p><strong>定義</strong>:
方法過長，難以理解和測試。</p>
<p><strong>特徵識別（量化標準）</strong>:</p>
<ul>
<li>方法行數: &gt; 50 行</li>
<li>巢狀層級: &gt; 3 層</li>
<li>邏輯區塊: &gt; 4 個（用註解分隔）</li>
<li>方法名稱包含「And」（如 <code>validateAndSaveBook</code>）</li>
</ul>
<p><strong>範例說明</strong>:</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">// 反例：Long Method：80 行方法
</span></span></span><span class="line"><span class="ln">  2</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">  3</span><span class="cl"><span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">processBookOrder</span><span class="p">(</span><span class="n">Order</span> <span class="n">order</span><span class="p">)</span> <span class="kd">async</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">  4</span><span class="cl">  <span class="c1">// 驗證訂單（20 行）
</span></span></span><span class="line"><span class="ln">  5</span><span class="cl"><span class="c1"></span>  <span class="k">if</span> <span class="p">(</span><span class="n">order</span><span class="p">.</span><span class="n">items</span><span class="p">.</span><span class="n">isEmpty</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">  6</span><span class="cl">    <span class="k">throw</span> <span class="n">ValidationException</span><span class="p">(</span><span class="s1">&#39;訂單不能為空&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">  7</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">  8</span><span class="cl">
</span></span><span class="line"><span class="ln">  9</span><span class="cl">  <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="n">item</span> <span class="k">in</span> <span class="n">order</span><span class="p">.</span><span class="n">items</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 10</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">item</span><span class="p">.</span><span class="n">quantity</span> <span class="o">&lt;=</span> <span class="m">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 11</span><span class="cl">      <span class="k">throw</span> <span class="n">ValidationException</span><span class="p">(</span><span class="s1">&#39;數量必須大於 0&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 12</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 13</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">item</span><span class="p">.</span><span class="n">price</span> <span class="o">&lt;</span> <span class="m">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 14</span><span class="cl">      <span class="k">throw</span> <span class="n">ValidationException</span><span class="p">(</span><span class="s1">&#39;價格不能為負數&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 15</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 16</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 17</span><span class="cl">
</span></span><span class="line"><span class="ln"> 18</span><span class="cl">  <span class="c1">// 計算價格（20 行）
</span></span></span><span class="line"><span class="ln"> 19</span><span class="cl"><span class="c1"></span>  <span class="kt">double</span> <span class="n">total</span> <span class="o">=</span> <span class="m">0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 20</span><span class="cl">  <span class="kt">double</span> <span class="n">discount</span> <span class="o">=</span> <span class="m">0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 21</span><span class="cl">
</span></span><span class="line"><span class="ln"> 22</span><span class="cl">  <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="n">item</span> <span class="k">in</span> <span class="n">order</span><span class="p">.</span><span class="n">items</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 23</span><span class="cl">    <span class="n">total</span> <span class="o">+=</span> <span class="n">item</span><span class="p">.</span><span class="n">price</span> <span class="o">*</span> <span class="n">item</span><span class="p">.</span><span class="n">quantity</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 24</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 25</span><span class="cl">
</span></span><span class="line"><span class="ln"> 26</span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">total</span> <span class="o">&gt;</span> <span class="m">1000</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 27</span><span class="cl">    <span class="n">discount</span> <span class="o">=</span> <span class="n">total</span> <span class="o">*</span> <span class="m">0.1</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 28</span><span class="cl">  <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">total</span> <span class="o">&gt;</span> <span class="m">500</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 29</span><span class="cl">    <span class="n">discount</span> <span class="o">=</span> <span class="n">total</span> <span class="o">*</span> <span class="m">0.05</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 30</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 31</span><span class="cl">
</span></span><span class="line"><span class="ln"> 32</span><span class="cl">  <span class="n">total</span> <span class="o">-=</span> <span class="n">discount</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 33</span><span class="cl">
</span></span><span class="line"><span class="ln"> 34</span><span class="cl">  <span class="c1">// 庫存檢查（20 行）
</span></span></span><span class="line"><span class="ln"> 35</span><span class="cl"><span class="c1"></span>  <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="n">item</span> <span class="k">in</span> <span class="n">order</span><span class="p">.</span><span class="n">items</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 36</span><span class="cl">    <span class="kd">final</span> <span class="n">stock</span> <span class="o">=</span> <span class="kd">await</span> <span class="n">inventoryRepository</span><span class="p">.</span><span class="n">getStock</span><span class="p">(</span><span class="n">item</span><span class="p">.</span><span class="n">bookId</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 37</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">stock</span> <span class="o">&lt;</span> <span class="n">item</span><span class="p">.</span><span class="n">quantity</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 38</span><span class="cl">      <span class="k">throw</span> <span class="n">InsufficientStockException</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 39</span><span class="cl">        <span class="s1">&#39;書籍 </span><span class="si">${</span><span class="n">item</span><span class="p">.</span><span class="n">bookId</span><span class="si">}</span><span class="s1"> 庫存不足&#39;</span>
</span></span><span class="line"><span class="ln"> 40</span><span class="cl">      <span class="p">);</span>
</span></span><span class="line"><span class="ln"> 41</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 42</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 43</span><span class="cl">
</span></span><span class="line"><span class="ln"> 44</span><span class="cl">  <span class="c1">// 建立訂單（20 行）
</span></span></span><span class="line"><span class="ln"> 45</span><span class="cl"><span class="c1"></span>  <span class="n">order</span><span class="p">.</span><span class="n">total</span> <span class="o">=</span> <span class="n">total</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 46</span><span class="cl">  <span class="n">order</span><span class="p">.</span><span class="n">discount</span> <span class="o">=</span> <span class="n">discount</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 47</span><span class="cl">  <span class="n">order</span><span class="p">.</span><span class="n">status</span> <span class="o">=</span> <span class="n">OrderStatus</span><span class="p">.</span><span class="n">pending</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 48</span><span class="cl">  <span class="n">order</span><span class="p">.</span><span class="n">createdAt</span> <span class="o">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">now</span><span class="p">();</span>
</span></span><span class="line"><span class="ln"> 49</span><span class="cl">
</span></span><span class="line"><span class="ln"> 50</span><span class="cl">  <span class="kd">await</span> <span class="n">repository</span><span class="p">.</span><span class="n">save</span><span class="p">(</span><span class="n">order</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 51</span><span class="cl">
</span></span><span class="line"><span class="ln"> 52</span><span class="cl">  <span class="c1">// 扣除庫存
</span></span></span><span class="line"><span class="ln"> 53</span><span class="cl"><span class="c1"></span>  <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="n">item</span> <span class="k">in</span> <span class="n">order</span><span class="p">.</span><span class="n">items</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 54</span><span class="cl">    <span class="kd">await</span> <span class="n">inventoryRepository</span><span class="p">.</span><span class="n">reduceStock</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 55</span><span class="cl">      <span class="n">item</span><span class="p">.</span><span class="n">bookId</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 56</span><span class="cl">      <span class="n">item</span><span class="p">.</span><span class="n">quantity</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 57</span><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="ln"> 58</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 59</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln"> 60</span><span class="cl">
</span></span><span class="line"><span class="ln"> 61</span><span class="cl"><span class="err">問題分析：</span>
</span></span><span class="line"><span class="ln"> 62</span><span class="cl"><span class="o">-</span> <span class="err">方法行數</span><span class="o">:</span> <span class="m">80</span> <span class="err">行（超過</span> <span class="m">50</span> <span class="err">行標準）</span>
</span></span><span class="line"><span class="ln"> 63</span><span class="cl"><span class="o">-</span> <span class="m">4</span> <span class="err">個邏輯區塊（驗證、計算、庫存、建立）</span>
</span></span><span class="line"><span class="ln"> 64</span><span class="cl"><span class="o">-</span> <span class="err">巢狀層級</span><span class="o">:</span> <span class="m">3</span> <span class="err">層（</span><span class="k">for</span> <span class="o">+</span> <span class="k">if</span><span class="err">）</span>
</span></span><span class="line"><span class="ln"> 65</span><span class="cl"><span class="o">-</span> <span class="err">難以理解和測試</span>
</span></span><span class="line"><span class="ln"> 66</span><span class="cl">
</span></span><span class="line"><span class="ln"> 67</span><span class="cl"><span class="c1">// 正例：拆分為多個小方法
</span></span></span><span class="line"><span class="ln"> 68</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 69</span><span class="cl"><span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">processBookOrder</span><span class="p">(</span><span class="n">Order</span> <span class="n">order</span><span class="p">)</span> <span class="kd">async</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 70</span><span class="cl">  <span class="n">_validateOrder</span><span class="p">(</span><span class="n">order</span><span class="p">);</span>              <span class="c1">// 步驟 1
</span></span></span><span class="line"><span class="ln"> 71</span><span class="cl"><span class="c1"></span>  <span class="kd">final</span> <span class="n">total</span> <span class="o">=</span> <span class="n">_calculateTotal</span><span class="p">(</span><span class="n">order</span><span class="p">);</span> <span class="c1">// 步驟 2
</span></span></span><span class="line"><span class="ln"> 72</span><span class="cl"><span class="c1"></span>  <span class="kd">await</span> <span class="n">_checkInventory</span><span class="p">(</span><span class="n">order</span><span class="p">);</span>       <span class="c1">// 步驟 3
</span></span></span><span class="line"><span class="ln"> 73</span><span class="cl"><span class="c1"></span>  <span class="kd">await</span> <span class="n">_saveOrder</span><span class="p">(</span><span class="n">order</span><span class="p">,</span> <span class="n">total</span><span class="p">);</span>     <span class="c1">// 步驟 4
</span></span></span><span class="line"><span class="ln"> 74</span><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="ln"> 75</span><span class="cl">
</span></span><span class="line"><span class="ln"> 76</span><span class="cl"><span class="kt">void</span> <span class="n">_validateOrder</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"> 77</span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">order</span><span class="p">.</span><span class="n">items</span><span class="p">.</span><span class="n">isEmpty</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 78</span><span class="cl">    <span class="k">throw</span> <span class="n">ValidationException</span><span class="p">(</span><span class="s1">&#39;訂單不能為空&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 79</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 80</span><span class="cl">
</span></span><span class="line"><span class="ln"> 81</span><span class="cl">  <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="n">item</span> <span class="k">in</span> <span class="n">order</span><span class="p">.</span><span class="n">items</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 82</span><span class="cl">    <span class="n">_validateOrderItem</span><span class="p">(</span><span class="n">item</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 83</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 84</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln"> 85</span><span class="cl">
</span></span><span class="line"><span class="ln"> 86</span><span class="cl"><span class="kt">void</span> <span class="n">_validateOrderItem</span><span class="p">(</span><span class="n">OrderItem</span> <span class="n">item</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 87</span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">item</span><span class="p">.</span><span class="n">quantity</span> <span class="o">&lt;=</span> <span class="m">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 88</span><span class="cl">    <span class="k">throw</span> <span class="n">ValidationException</span><span class="p">(</span><span class="s1">&#39;數量必須大於 0&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 89</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 90</span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">item</span><span class="p">.</span><span class="n">price</span> <span class="o">&lt;</span> <span class="m">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 91</span><span class="cl">    <span class="k">throw</span> <span class="n">ValidationException</span><span class="p">(</span><span class="s1">&#39;價格不能為負數&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 92</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 93</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln"> 94</span><span class="cl">
</span></span><span class="line"><span class="ln"> 95</span><span class="cl"><span class="kt">double</span> <span class="n">_calculateTotal</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"> 96</span><span class="cl">  <span class="kt">double</span> <span class="n">total</span> <span class="o">=</span> <span class="m">0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 97</span><span class="cl">
</span></span><span class="line"><span class="ln"> 98</span><span class="cl">  <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="n">item</span> <span class="k">in</span> <span class="n">order</span><span class="p">.</span><span class="n">items</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 99</span><span class="cl">    <span class="n">total</span> <span class="o">+=</span> <span class="n">item</span><span class="p">.</span><span class="n">price</span> <span class="o">*</span> <span class="n">item</span><span class="p">.</span><span class="n">quantity</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">100</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">101</span><span class="cl">
</span></span><span class="line"><span class="ln">102</span><span class="cl">  <span class="kd">final</span> <span class="n">discount</span> <span class="o">=</span> <span class="n">_calculateDiscount</span><span class="p">(</span><span class="n">total</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">103</span><span class="cl">  <span class="k">return</span> <span class="n">total</span> <span class="o">-</span> <span class="n">discount</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">104</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">105</span><span class="cl">
</span></span><span class="line"><span class="ln">106</span><span class="cl"><span class="kt">double</span> <span class="n">_calculateDiscount</span><span class="p">(</span><span class="kt">double</span> <span class="n">total</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">107</span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">total</span> <span class="o">&gt;</span> <span class="m">1000</span><span class="p">)</span> <span class="k">return</span> <span class="n">total</span> <span class="o">*</span> <span class="m">0.1</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">108</span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="n">total</span> <span class="o">&gt;</span> <span class="m">500</span><span class="p">)</span> <span class="k">return</span> <span class="n">total</span> <span class="o">*</span> <span class="m">0.05</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">109</span><span class="cl">  <span class="k">return</span> <span class="m">0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">110</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">111</span><span class="cl">
</span></span><span class="line"><span class="ln">112</span><span class="cl"><span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">_checkInventory</span><span class="p">(</span><span class="n">Order</span> <span class="n">order</span><span class="p">)</span> <span class="kd">async</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">113</span><span class="cl">  <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="n">item</span> <span class="k">in</span> <span class="n">order</span><span class="p">.</span><span class="n">items</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">114</span><span class="cl">    <span class="kd">final</span> <span class="n">stock</span> <span class="o">=</span> <span class="kd">await</span> <span class="n">inventoryRepository</span><span class="p">.</span><span class="n">getStock</span><span class="p">(</span><span class="n">item</span><span class="p">.</span><span class="n">bookId</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">115</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">stock</span> <span class="o">&lt;</span> <span class="n">item</span><span class="p">.</span><span class="n">quantity</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">116</span><span class="cl">      <span class="k">throw</span> <span class="n">InsufficientStockException</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">117</span><span class="cl">        <span class="s1">&#39;書籍 </span><span class="si">${</span><span class="n">item</span><span class="p">.</span><span class="n">bookId</span><span class="si">}</span><span class="s1"> 庫存不足&#39;</span>
</span></span><span class="line"><span class="ln">118</span><span class="cl">      <span class="p">);</span>
</span></span><span class="line"><span class="ln">119</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">120</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">121</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">122</span><span class="cl">
</span></span><span class="line"><span class="ln">123</span><span class="cl"><span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">_saveOrder</span><span class="p">(</span><span class="n">Order</span> <span class="n">order</span><span class="p">,</span> <span class="kt">double</span> <span class="n">total</span><span class="p">)</span> <span class="kd">async</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">124</span><span class="cl">  <span class="n">order</span><span class="p">.</span><span class="n">total</span> <span class="o">=</span> <span class="n">total</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">125</span><span class="cl">  <span class="n">order</span><span class="p">.</span><span class="n">status</span> <span class="o">=</span> <span class="n">OrderStatus</span><span class="p">.</span><span class="n">pending</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">126</span><span class="cl">  <span class="n">order</span><span class="p">.</span><span class="n">createdAt</span> <span class="o">=</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">now</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">127</span><span class="cl">
</span></span><span class="line"><span class="ln">128</span><span class="cl">  <span class="kd">await</span> <span class="n">repository</span><span class="p">.</span><span class="n">save</span><span class="p">(</span><span class="n">order</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">129</span><span class="cl">  <span class="kd">await</span> <span class="n">_reduceInventory</span><span class="p">(</span><span class="n">order</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">130</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">131</span><span class="cl">
</span></span><span class="line"><span class="ln">132</span><span class="cl"><span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">_reduceInventory</span><span class="p">(</span><span class="n">Order</span> <span class="n">order</span><span class="p">)</span> <span class="kd">async</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">133</span><span class="cl">  <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="n">item</span> <span class="k">in</span> <span class="n">order</span><span class="p">.</span><span class="n">items</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">134</span><span class="cl">    <span class="kd">await</span> <span class="n">inventoryRepository</span><span class="p">.</span><span class="n">reduceStock</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">135</span><span class="cl">      <span class="n">item</span><span class="p">.</span><span class="n">bookId</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">136</span><span class="cl">      <span class="n">item</span><span class="p">.</span><span class="n">quantity</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">137</span><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="ln">138</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">139</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">140</span><span class="cl">
</span></span><span class="line"><span class="ln">141</span><span class="cl"><span class="err">改善效果：</span>
</span></span><span class="line"><span class="ln">142</span><span class="cl"><span class="o">-</span> <span class="err">主方法只有</span> <span class="m">4</span> <span class="err">行（清楚的流程）</span>
</span></span><span class="line"><span class="ln">143</span><span class="cl"><span class="o">-</span> <span class="err">每個私有方法</span> <span class="o">&lt;</span> <span class="m">20</span> <span class="err">行</span>
</span></span><span class="line"><span class="ln">144</span><span class="cl"><span class="o">-</span> <span class="err">邏輯分離且可測試</span>
</span></span><span class="line"><span class="ln">145</span><span class="cl"><span class="o">-</span> <span class="err">可讀性大幅提升</span></span></span></code></pre></div><hr>
<h4 id="224-b4-dead-code死程式碼">2.2.4 B4. Dead Code（死程式碼）</h4>
<p><strong>定義</strong>:
永遠不會執行或使用的程式碼。</p>
<p><strong>特徵識別</strong>:</p>
<ul>
<li>未被呼叫的方法</li>
<li>無法到達的程式碼分支</li>
<li>註解掉的程式碼超過 1 週</li>
<li><code>dart analyze</code> 顯示 <code>unused_element</code> 警告</li>
</ul>
<p><strong>自動化檢測方法</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 檢測 unused 警告</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">dart analyze <span class="p">|</span> grep <span class="s2">&#34;unused&#34;</span>
</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 class="c1"># 檢測程式碼覆蓋率</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">flutter <span class="nb">test</span> --coverage
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">genhtml coverage/lcov.info -o coverage/html
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 檢查 coverage/html 中 0% 覆蓋率的程式碼</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"># 搜尋註解掉的程式碼</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">grep -r <span class="s2">&#34;^[[:space:]]*//.*{&#34;</span> lib/</span></span></code></pre></div><p><strong>範例說明</strong>:</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">// 反例：Dead Code 範例
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kd">class</span> <span class="nc">BookService</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="c1">// 未使用的方法（dart analyze 會警告）
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span>  <span class="kt">void</span> <span class="n">unusedMethod</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="n">print</span><span class="p">(</span><span class="s1">&#39;這個方法從未被呼叫&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="c1">// 無法到達的程式碼
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"></span>  <span class="kt">void</span> <span class="n">processBook</span><span class="p">(</span><span class="n">Book</span> <span class="n">book</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="k">return</span><span class="p">;</span> <span class="c1">// 提前返回
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="c1">// 反例：以下程式碼永遠不會執行
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"></span>    <span class="n">print</span><span class="p">(</span><span class="s1">&#39;處理書籍&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="n">saveBook</span><span class="p">(</span><span class="n">book</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl">  <span class="c1">// 註解掉的程式碼（已過時）
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="c1"></span>  <span class="c1">// void oldImplementation() {
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="c1"></span>  <span class="c1">//   // 舊的實作方式，已被新方法取代
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="c1"></span>  <span class="c1">//   // 但程式碼保留了 2 個月
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="c1"></span>  <span class="c1">// }
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">  <span class="c1">// 未使用的變數
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="c1"></span>  <span class="kd">final</span> <span class="kt">String</span> <span class="n">unusedVariable</span> <span class="o">=</span> <span class="s1">&#39;never used&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="c1">// 正例：移除 Dead Code
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="kd">class</span> <span class="nc">BookService</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">  <span class="c1">// 只保留實際使用的方法
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="c1"></span>  <span class="kt">void</span> <span class="n">processBook</span><span class="p">(</span><span class="n">Book</span> <span class="n">book</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">    <span class="n">print</span><span class="p">(</span><span class="s1">&#39;處理書籍&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">    <span class="n">saveBook</span><span class="p">(</span><span class="n">book</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">
</span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="err">改善效果：</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="o">-</span> <span class="err">程式碼簡潔</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="o">-</span> <span class="err">降低維護成本</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="o">-</span> <span class="err">無混淆和誤導</span></span></span></code></pre></div><hr>
<h3 id="23-c-類-code-smellticket-粒度問題">2.3 C 類 Code Smell（Ticket 粒度問題）</h3>
<h4 id="231-c1-god-ticket全能-ticket">2.3.1 C1. God Ticket（全能 Ticket）</h4>
<p><strong>定義</strong>:
單一 Ticket 修改過多檔案和層級，範圍失控。</p>
<p><strong>特徵識別（引用<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 第 5.2 節量化標準）</strong>:</p>
<ul>
<li>修改檔案數 &gt; 10 個</li>
<li>跨 3 個以上架構層級</li>
<li>預估工時 &gt; 16 小時（2 天）</li>
</ul>
<p><strong>與<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 的關聯</strong>:</p>
<ul>
<li>違反「Ticket 粒度標準」（5.2 節）</li>
<li>違反「單層修改原則」（3.1 節）</li>
</ul>
<p><strong>檢測方法</strong>:</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">步驟 1: 計算 Ticket 涉及的檔案數
</span></span><span class="line"><span class="ln">2</span><span class="cl">步驟 2: 判斷檔案所屬的層級
</span></span><span class="line"><span class="ln">3</span><span class="cl">步驟 3: 計算跨幾個層級
</span></span><span class="line"><span class="ln">4</span><span class="cl">  ├─ &gt; 3 層級 → God Ticket
</span></span><span class="line"><span class="ln">5</span><span class="cl">  └─ 1 層級 → 良好 Ticket</span></span></code></pre></div><p><strong>範例說明</strong>:</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">Ticket: 新增「書籍評分」完整功能
</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">檔案清單（12 個檔案）:
</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">1. lib/presentation/widgets/book_detail_widget.dart (Layer 1)
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">2. lib/presentation/widgets/rating_widget.dart (Layer 1)
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">3. lib/presentation/controllers/book_detail_controller.dart (Layer 2)
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">4. lib/presentation/controllers/rating_controller.dart (Layer 2)
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">5. lib/application/use_cases/rate_book_use_case.dart (Layer 3)
</span></span><span class="line"><span class="ln">10</span><span class="cl">6. lib/application/use_cases/get_book_rating_use_case.dart (Layer 3)
</span></span><span class="line"><span class="ln">11</span><span class="cl">7. lib/domain/entities/book.dart (Layer 5)
</span></span><span class="line"><span class="ln">12</span><span class="cl">8. lib/domain/entities/rating.dart (Layer 5)
</span></span><span class="line"><span class="ln">13</span><span class="cl">9. lib/domain/value_objects/rating_value.dart (Layer 5)
</span></span><span class="line"><span class="ln">14</span><span class="cl">10. lib/infrastructure/repositories/book_repository_impl.dart (Layer 5)
</span></span><span class="line"><span class="ln">15</span><span class="cl">11. lib/infrastructure/repositories/rating_repository_impl.dart (Layer 5)
</span></span><span class="line"><span class="ln">16</span><span class="cl">12. lib/infrastructure/database/rating_table.dart (Layer 5)
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl">分析結果:
</span></span><span class="line"><span class="ln">19</span><span class="cl">- 檔案數: 12 個（&gt; 10 個標準）
</span></span><span class="line"><span class="ln">20</span><span class="cl">- 層級跨度: 4 層（Layer 1, 2, 3, 5）
</span></span><span class="line"><span class="ln">21</span><span class="cl">- 預估工時: 24 小時（&gt; 16 小時標準）
</span></span><span class="line"><span class="ln">22</span><span class="cl">- 判斷: God Ticket
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl">建議拆分（引用[層級隔離派工方法論](/record/layered-ticket-methodology/) 第 5.4 節拆分指引）:
</span></span><span class="line"><span class="ln">25</span><span class="cl">- Ticket 1 [Layer 5]: Rating Value Object 和 Entity 設計
</span></span><span class="line"><span class="ln">26</span><span class="cl">- Ticket 2 [Layer 5]: Rating Repository 實作
</span></span><span class="line"><span class="ln">27</span><span class="cl">- Ticket 3 [Layer 3]: RateBookUseCase 實作
</span></span><span class="line"><span class="ln">28</span><span class="cl">- Ticket 4 [Layer 3]: GetBookRatingUseCase 實作
</span></span><span class="line"><span class="ln">29</span><span class="cl">- Ticket 5 [Layer 2]: Controller 整合 UseCase
</span></span><span class="line"><span class="ln">30</span><span class="cl">- Ticket 6 [Layer 1]: UI 新增評分元件
</span></span><span class="line"><span class="ln">31</span><span class="cl">
</span></span><span class="line"><span class="ln">32</span><span class="cl">改善效果:
</span></span><span class="line"><span class="ln">33</span><span class="cl">- 每個 Ticket 只修改 1-3 個檔案
</span></span><span class="line"><span class="ln">34</span><span class="cl">- 每個 Ticket 只涉及 1 個層級
</span></span><span class="line"><span class="ln">35</span><span class="cl">- 預估工時: 每個 Ticket 2-4 小時
</span></span><span class="line"><span class="ln">36</span><span class="cl">- 風險可控</span></span></code></pre></div><hr>
<h4 id="232-c2-incomplete-ticket不完整-ticket">2.3.2 C2. Incomplete Ticket（不完整 Ticket）</h4>
<p><strong>定義</strong>:
Ticket 缺少必要的測試、文件或驗收條件。</p>
<p><strong>特徵識別</strong>:</p>
<ul>
<li>沒有測試檔案（Phase 2 缺失）</li>
<li>沒有驗收條件（Phase 1 設計不完整）</li>
<li>沒有更新相關文件</li>
<li>沒有完整的 TDD 四階段記錄</li>
</ul>
<p><strong>檢測方法（基於 TDD 四階段要求）</strong>:</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">完整 Ticket 必須包含:
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">- Phase 1: 功能設計完成
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">- Phase 2: 測試設計完成（測試檔案存在）
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">- Phase 3: 實作完成（程式碼檔案）
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">- Phase 4: 重構評估完成
</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">Incomplete Ticket 特徵:
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">- 缺少測試檔案（Phase 2 未完成）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- 缺少驗收條件（Phase 1 設計不完整）
</span></span><span class="line"><span class="ln">10</span><span class="cl">- 缺少工作日誌（無法追蹤進度）</span></span></code></pre></div><p><strong>檢測流程（Code Review 階段）</strong>:</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">步驟 1: 檢查 git diff 中的檔案
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  ├─ 是否包含 test/ 目錄的檔案？
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  └─ 測試檔案數量 vs 程式碼檔案數量比例
</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">步驟 2: 檢查 Ticket 描述
</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">步驟 3: 檢查工作日誌
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  ├─ docs/work-logs/vX.Y.Z-*.md 是否存在？
</span></span><span class="line"><span class="ln">10</span><span class="cl">  └─ 是否完成 Phase 1-4 記錄？</span></span></code></pre></div><hr>
<h4 id="233-c3-ambiguous-responsibility職責模糊-ticket">2.3.3 C3. Ambiguous Responsibility（職責模糊 Ticket）</h4>
<p><strong>定義</strong>:
Ticket 的職責定義不明確，無法判斷屬於哪一層級。</p>
<p><strong>特徵識別</strong>:</p>
<ul>
<li>Ticket 標題沒有標明層級（如 [Layer X]）</li>
<li>描述中混合多個層級的職責</li>
<li>驗收條件跨多個層級</li>
</ul>
<p><strong>檢測方法</strong>:</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">職責明確 Ticket 格式:
</span></span><span class="line"><span class="ln">2</span><span class="cl">標題: [Layer X] 清楚的功能描述
</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">
</span></span><span class="line"><span class="ln">6</span><span class="cl">職責模糊 Ticket 特徵:
</span></span><span class="line"><span class="ln">7</span><span class="cl">標題: 沒有 [Layer X] 標示
</span></span><span class="line"><span class="ln">8</span><span class="cl">描述: 混合多個層級的職責
</span></span><span class="line"><span class="ln">9</span><span class="cl">驗證: 跨多個層級的驗證</span></span></code></pre></div><p><strong>範例說明</strong>:</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">反例 職責模糊 Ticket:
</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">
</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">- 「所有功能」範圍不明確
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- 驗收條件過於籠統
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">正例 職責明確 Ticket:
</span></span><span class="line"><span class="ln">12</span><span class="cl">標題: [Layer 2] 實作書籍詳情頁面事件處理
</span></span><span class="line"><span class="ln">13</span><span class="cl">描述: 實作 BookDetailController 的事件處理方法，
</span></span><span class="line"><span class="ln">14</span><span class="cl">      整合 GetBookDetailUseCase 並轉換為 ViewModel
</span></span><span class="line"><span class="ln">15</span><span class="cl">驗收:
</span></span><span class="line"><span class="ln">16</span><span class="cl">  - BookDetailController.loadBookDetail() 呼叫 UseCase
</span></span><span class="line"><span class="ln">17</span><span class="cl">  - BookPresenter.toViewModel() 正確轉換資料
</span></span><span class="line"><span class="ln">18</span><span class="cl">  - 錯誤處理正確顯示錯誤訊息
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl">改善效果:
</span></span><span class="line"><span class="ln">21</span><span class="cl">- 層級明確（Layer 2）
</span></span><span class="line"><span class="ln">22</span><span class="cl">- 職責清楚（事件處理 + 資料轉換）
</span></span><span class="line"><span class="ln">23</span><span class="cl">- 驗收條件可驗證</span></span></code></pre></div><hr>
<h2 id="第三章ticket-粒度檢測方法">第三章：Ticket 粒度檢測方法</h2>
<h3 id="31-檢測時機和流程">3.1 檢測時機和流程</h3>
<p><strong>核心理念</strong>: 從 Ticket 設計階段就能發現 Code Smell，比實作完成後才發現更有效率。</p>
<p><strong>檢測時機對應 TDD 四階段</strong>:</p>
<table>
  <thead>
      <tr>
          <th>階段</th>
          <th>檢測時機</th>
          <th>檢測重點</th>
          <th>對應 Code Smell</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Phase 1 設計階段</strong></td>
          <td>Ticket 設計完成時</td>
          <td>Ticket 粒度和層級定位</td>
          <td>C1, C2, C3, A1</td>
      </tr>
      <tr>
          <td><strong>Phase 2 測試設計</strong></td>
          <td>測試設計完成時</td>
          <td>測試範圍是否限定在單一層級</td>
          <td>C2, B1</td>
      </tr>
      <tr>
          <td><strong>Phase 3 實作執行</strong></td>
          <td>程式碼提交時</td>
          <td>實作是否產生 Code Smell</td>
          <td>A2, A3, A4, B2, B3</td>
      </tr>
      <tr>
          <td><strong>Code Review</strong></td>
          <td>PR 提交時</td>
          <td>最終驗證</td>
          <td>所有 Code Smell</td>
      </tr>
      <tr>
          <td><strong>Phase 4 重構階段</strong></td>
          <td>重構評估時</td>
          <td>識別需要重構的 Code Smell</td>
          <td>B1, B2, B3, B4</td>
      </tr>
  </tbody>
</table>
<p><strong>檢測流程總覽</strong>:</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">Ticket 設計（Phase 1）
</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">檢查 Ticket 粒度（C1, C3, A1）
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  ├─ 通過 → 測試設計（Phase 2）
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  └─ 失敗 → 拆分 Ticket
</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">測試設計（Phase 2）
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">檢查測試範圍（C2）
</span></span><span class="line"><span class="ln">10</span><span class="cl">  ├─ 通過 → 實作執行（Phase 3）
</span></span><span class="line"><span class="ln">11</span><span class="cl">  └─ 失敗 → 補充測試
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">實作執行（Phase 3）
</span></span><span class="line"><span class="ln">14</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">15</span><span class="cl">檢查程式碼品質（A2, A3, A4, B2, B3）
</span></span><span class="line"><span class="ln">16</span><span class="cl">  ├─ 通過 → Code Review
</span></span><span class="line"><span class="ln">17</span><span class="cl">  └─ 失敗 → 修正程式碼
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl">Code Review
</span></span><span class="line"><span class="ln">20</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">21</span><span class="cl">全面檢查所有 Code Smell
</span></span><span class="line"><span class="ln">22</span><span class="cl">  ├─ 通過 → 合併 PR
</span></span><span class="line"><span class="ln">23</span><span class="cl">  └─ 失敗 → 重構
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span><span class="line"><span class="ln">25</span><span class="cl">Phase 4 重構評估
</span></span><span class="line"><span class="ln">26</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">27</span><span class="cl">識別需要重構的 Code Smell（B1, B2, B3, B4）
</span></span><span class="line"><span class="ln">28</span><span class="cl">  ├─ 有需要 → 執行重構
</span></span><span class="line"><span class="ln">29</span><span class="cl">  └─ 無需要 → 完成</span></span></code></pre></div><hr>
<h3 id="32-a-類-code-smell-檢測方法跨層級問題">3.2 A 類 Code Smell 檢測方法（跨層級問題）</h3>
<h4 id="321-a1-shotgun-surgery-檢測">3.2.1 A1. Shotgun Surgery 檢測</h4>
<p><strong>檢測指標</strong>:</p>
<ol>
<li><strong>檔案數量指標</strong>: 單一 Ticket 修改的檔案數</li>
<li><strong>層級跨度指標</strong>: Ticket 涉及的層級數量</li>
<li><strong>依賴鏈長度指標</strong>: 從 UI 到 Domain 的依賴鏈長度</li>
</ol>
<p><strong>判斷標準</strong>（引用<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 第 5.2 節）:</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">良好 Ticket（單層修改）:
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">- 檔案數: 1-5 個
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">- 層級跨度: 1 層
</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">
</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">- 檔案數: 6-10 個
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">- 層級跨度: 2 層
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- 依賴鏈: 需要修改 1-2 層
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">Shotgun Surgery（散彈槍手術）:
</span></span><span class="line"><span class="ln">12</span><span class="cl">- 檔案數: &gt; 10 個
</span></span><span class="line"><span class="ln">13</span><span class="cl">- 層級跨度: &gt; 2 層
</span></span><span class="line"><span class="ln">14</span><span class="cl">- 依賴鏈: 需要同步修改多層</span></span></code></pre></div><p><strong>檢測流程</strong>（基於<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 第 6.2 節檔案路徑分析法）:</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">步驟 1: 列出 Ticket 涉及的所有檔案
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">步驟 2: 使用[層級隔離派工方法論](/record/layered-ticket-methodology/) 第 2.4 節的決策樹判斷每個檔案屬於哪一層
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">步驟 3: 統計跨幾個層級
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  ├─ 1 層級 → 良好設計
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  ├─ 2 層級 → 需要檢查是否可拆分
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  └─ &gt; 2 層級 → Shotgun Surgery
</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">步驟 4: 如果檢測到 Shotgun Surgery
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  ├─ 檢查是否為特殊場景（架構遷移、Hotfix）
</span></span><span class="line"><span class="ln">10</span><span class="cl">  ├─ 分析是否可以拆分為多個 Ticket
</span></span><span class="line"><span class="ln">11</span><span class="cl">  └─ 評估架構設計是否有問題（引入 Adapter/Facade）</span></span></code></pre></div><hr>
<h4 id="322-a2-feature-envy-檢測">3.2.2 A2. Feature Envy 檢測</h4>
<p><strong>檢測指標</strong>:</p>
<ol>
<li><strong>直接依賴指標</strong>: 外層是否直接依賴內層的具體類別</li>
<li><strong>欄位存取指標</strong>: 外層存取內層的內部欄位次數</li>
<li><strong>ViewModel 缺失指標</strong>: Layer 2 是否缺少資料轉換</li>
</ol>
<p><strong>判斷標準</strong>:</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><span class="line"><span class="ln"> 2</span><span class="cl">- UI 依賴 ViewModel，不依賴 Domain Entity
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">- Controller 包含 Presenter 轉換邏輯
</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">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">Feature Envy:
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">- UI 直接依賴 Domain Entity
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">- 直接存取 Entity 的內部欄位（如 book.isbn.value）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- 缺少 ViewModel 或 Presenter 轉換層
</span></span><span class="line"><span class="ln">10</span><span class="cl">- 欄位存取次數 &gt; 3 次</span></span></code></pre></div><p><strong>檢測流程</strong>:</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">步驟 1: 檢查 UI Widget 的依賴
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  ├─ 是否直接依賴 Domain Entity？
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  └─ 是否透過 ViewModel？
</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">步驟 2: 檢查 Controller 是否包含 Presenter
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  ├─ 是否有 toViewModel() 轉換方法？
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  └─ 是否直接將 Entity 傳給 UI？
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">步驟 3: 統計內層欄位存取次數
</span></span><span class="line"><span class="ln">10</span><span class="cl">  ├─ 存取 Entity 內部欄位（如 .value）&gt; 3 次 → Feature Envy
</span></span><span class="line"><span class="ln">11</span><span class="cl">  └─ 透過 ViewModel 存取 → 良好設計</span></span></code></pre></div><hr>
<h4 id="323-a3-inappropriate-intimacy-檢測">3.2.3 A3. Inappropriate Intimacy 檢測</h4>
<p><strong>檢測指標</strong>:</p>
<ol>
<li><strong>依賴方向檢查</strong>: 內層是否依賴外層</li>
<li><strong>循環依賴檢查</strong>: 是否存在雙向依賴</li>
<li><strong>Domain 純淨度檢查</strong>: Domain 是否包含 UI 或 Infrastructure 依賴</li>
</ol>
<p><strong>判斷標準</strong>（引用<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 第 2.3 節依賴方向規則）:</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><span class="line"><span class="ln">2</span><span class="cl">Layer 1 → Layer 2 → Layer 3 → Layer 4 ← Layer 5
</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">違反依賴方向（Inappropriate Intimacy）:
</span></span><span class="line"><span class="ln">5</span><span class="cl">- Layer 5 → Layer 3（Domain 依賴 UseCase）
</span></span><span class="line"><span class="ln">6</span><span class="cl">- Layer 5 → Layer 2（Domain 依賴 Controller）
</span></span><span class="line"><span class="ln">7</span><span class="cl">- Layer 3 ← → Layer 5（循環依賴）</span></span></code></pre></div><p><strong>檢測流程</strong>:</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">步驟 1: 檢查 Domain Entity 的 import 語句
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  ├─ 是否 import UseCase？ →
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  ├─ 是否 import Controller？ →
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  └─ 只 import 同層或 Layer 4 介面？ →
</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">步驟 2: 檢查 UseCase 的依賴
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  ├─ 是否依賴 Layer 4 介面？ →
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  └─ 是否依賴 Layer 5 具體類別？ →（應透過介面）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">步驟 3: 使用工具檢測循環依賴
</span></span><span class="line"><span class="ln">11</span><span class="cl">  └─ dart analyze 會報告循環依賴錯誤</span></span></code></pre></div><hr>
<h4 id="324-a4-leaky-abstraction-檢測">3.2.4 A4. Leaky Abstraction 檢測</h4>
<p><strong>檢測指標</strong>:</p>
<ol>
<li><strong>介面純淨度</strong>: 介面是否包含實作細節</li>
<li><strong>參數檢查</strong>: 方法參數是否洩漏實作資訊</li>
<li><strong>回傳類型檢查</strong>: 是否回傳 Infrastructure 特定類型</li>
</ol>
<p><strong>判斷標準</strong>:</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><span class="line"><span class="ln">2</span><span class="cl">- 方法名稱描述「做什麼」，不描述「怎麼做」
</span></span><span class="line"><span class="ln">3</span><span class="cl">- 參數是 Domain 概念，不是技術細節
</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">
</span></span><span class="line"><span class="ln">6</span><span class="cl">Leaky Abstraction:
</span></span><span class="line"><span class="ln">7</span><span class="cl">- 介面包含 SQL、HTTP、Cache 等關鍵字
</span></span><span class="line"><span class="ln">8</span><span class="cl">- 參數包含資料庫特定類型（如 Cursor）
</span></span><span class="line"><span class="ln">9</span><span class="cl">- 回傳類型包含框架特定類型（如 HttpResponse）</span></span></code></pre></div><p><strong>檢測流程</strong>:</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">步驟 1: 檢查 Repository 介面定義
</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">  │  - findBySql() → 洩漏 SQL
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  │  - findById() → 抽象概念
</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">  └─ 參數類型是否為 Domain 類型？
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">     - String sql → 技術細節
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">     - String id → Domain 概念
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">步驟 2: 檢查 Event 定義
</span></span><span class="line"><span class="ln">11</span><span class="cl">  └─ 是否包含 UI 特定資料（如 BuildContext）？ →</span></span></code></pre></div><hr>
<h3 id="33-b-類-code-smell-檢測方法單層級問題">3.3 B 類 Code Smell 檢測方法（單層級問題）</h3>
<h4 id="331-b1-divergent-change-檢測">3.3.1 B1. Divergent Change 檢測</h4>
<p><strong>檢測指標</strong>:</p>
<ol>
<li><strong>類別職責數量</strong>: 類別承擔幾個不同的職責</li>
<li><strong>變更原因數量</strong>: 有幾種不同的原因需要修改此類別</li>
<li><strong>方法分組檢查</strong>: 方法是否可以明確分組</li>
</ol>
<p><strong>判斷標準</strong>:</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><span class="line"><span class="ln">2</span><span class="cl">- 只有 1 個變更原因
</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">
</span></span><span class="line"><span class="ln">6</span><span class="cl">Divergent Change:
</span></span><span class="line"><span class="ln">7</span><span class="cl">- &gt; 2 個變更原因（如「列表變更」和「詳情變更」）
</span></span><span class="line"><span class="ln">8</span><span class="cl">- 方法可以分為 2+ 個明確的群組
</span></span><span class="line"><span class="ln">9</span><span class="cl">- 類別名稱過於籠統（如 BookController、BookService）</span></span></code></pre></div><p><strong>檢測流程</strong>:</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">步驟 1: 分析類別的 public 方法
</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">步驟 2: 統計分組數量
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  ├─ 1 組 → 單一職責
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  ├─ 2 組 → 考慮拆分
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  └─ &gt; 2 組 → Divergent Change
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">步驟 3: 檢查歷史修改記錄
</span></span><span class="line"><span class="ln">10</span><span class="cl">  └─ git log --oneline {file}
</span></span><span class="line"><span class="ln">11</span><span class="cl">  └─ 分析修改原因是否多樣化</span></span></code></pre></div><hr>
<h4 id="332-b2-large-class-檢測">3.3.2 B2. Large Class 檢測</h4>
<p><strong>檢測指標</strong>:</p>
<ol>
<li><strong>程式碼行數</strong>: 類別總行數</li>
<li><strong>方法數量</strong>: public 方法數量</li>
<li><strong>屬性數量</strong>: instance 變數數量</li>
</ol>
<p><strong>判斷標準</strong>（量化指標）:</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><span class="line"><span class="ln"> 2</span><span class="cl">- 總行數: &lt; 200 行
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">- public 方法: &lt; 10 個
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">- 屬性: &lt; 8 個
</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></span><span class="line"><span class="ln"> 7</span><span class="cl">- 總行數: 200-300 行
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">- public 方法: 10-15 個
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- 屬性: 8-12 個
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">Large Class:
</span></span><span class="line"><span class="ln">12</span><span class="cl">- 總行數: &gt; 300 行
</span></span><span class="line"><span class="ln">13</span><span class="cl">- public 方法: &gt; 15 個
</span></span><span class="line"><span class="ln">14</span><span class="cl">- 屬性: &gt; 12 個</span></span></code></pre></div><p><strong>自動化檢測方法</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><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">wc -l lib/presentation/controllers/book_controller.dart
</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 class="c1"># 檢測所有 Controller 檔案大小</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">find lib -name <span class="s2">&#34;*_controller.dart&#34;</span> -exec wc -l <span class="o">{}</span> <span class="se">\;</span> <span class="p">|</span> sort -rn
</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 class="c1"># 使用 dart analyze 檢測複雜度</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="c1"># （需要配置 analysis_options.yaml）</span></span></span></code></pre></div><hr>
<h4 id="333-b3-long-method-檢測">3.3.3 B3. Long Method 檢測</h4>
<p><strong>檢測指標</strong>:</p>
<ol>
<li><strong>方法行數</strong>: 方法內程式碼行數</li>
<li><strong>巢狀層級</strong>: if/for/while 的巢狀深度</li>
<li><strong>區塊數量</strong>: 方法內邏輯區塊數量（用註解分隔）</li>
</ol>
<p><strong>判斷標準</strong>:</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><span class="line"><span class="ln"> 2</span><span class="cl">- 行數: &lt; 30 行
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">- 巢狀層級: &lt; 3 層
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">- 邏輯區塊: &lt; 3 個
</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></span><span class="line"><span class="ln"> 7</span><span class="cl">- 行數: 30-50 行
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">- 巢狀層級: 3 層
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- 邏輯區塊: 3-4 個
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">Long Method:
</span></span><span class="line"><span class="ln">12</span><span class="cl">- 行數: &gt; 50 行
</span></span><span class="line"><span class="ln">13</span><span class="cl">- 巢狀層級: &gt; 3 層
</span></span><span class="line"><span class="ln">14</span><span class="cl">- 邏輯區塊: &gt; 4 個</span></span></code></pre></div><p><strong>檢測流程</strong>:</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">步驟 1: 統計方法行數
</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">步驟 2: 分析巢狀層級
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  └─ 統計最深的 if/for/while 巢狀深度
</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">步驟 3: 識別邏輯區塊
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  └─ 統計註解數量（通常用來分隔邏輯區塊）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  └─ &gt; 3 個註解區塊 → 應該拆分方法
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">步驟 4: 檢查方法命名
</span></span><span class="line"><span class="ln">12</span><span class="cl">  └─ 方法名稱是否包含「And」？ → 可能做太多事情
</span></span><span class="line"><span class="ln">13</span><span class="cl">     - validateAndSaveBook() → 應拆分為 validate() 和 save()</span></span></code></pre></div><hr>
<h4 id="334-b4-dead-code-檢測">3.3.4 B4. Dead Code 檢測</h4>
<p><strong>檢測方法</strong>:</p>
<ol>
<li><strong>使用 dart analyze 檢測 unused 警告</strong></li>
<li><strong>使用 code coverage 工具檢測 0% 覆蓋率程式碼</strong></li>
<li><strong>手動檢查註解掉的程式碼</strong></li>
</ol>
<p><strong>自動化檢測</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 檢測 unused 警告</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">dart analyze <span class="p">|</span> grep <span class="s2">&#34;unused&#34;</span>
</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 class="c1"># 檢測程式碼覆蓋率</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">flutter <span class="nb">test</span> --coverage
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">genhtml coverage/lcov.info -o coverage/html
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 檢查 coverage/html 中 0% 覆蓋率的程式碼</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"># 搜尋註解掉的程式碼</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">grep -r <span class="s2">&#34;^[[:space:]]*//.*{&#34;</span> lib/</span></span></code></pre></div><hr>
<h3 id="34-c-類-code-smell-檢測方法ticket-粒度問題">3.4 C 類 Code Smell 檢測方法（Ticket 粒度問題）</h3>
<h4 id="341-c1-god-ticket-檢測">3.4.1 C1. God Ticket 檢測</h4>
<p><strong>檢測指標</strong>（引用<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 第 5.2 節）:</p>
<ol>
<li><strong>檔案修改數量</strong>: 計算 git diff 涉及的檔案數</li>
<li><strong>層級跨度</strong>: 涉及幾個架構層級</li>
<li><strong>預估工時</strong>: Ticket 的預估完成時間</li>
</ol>
<p><strong>判斷標準</strong>:</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">良好 Ticket 粒度:
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">- 檔案數: 1-5 個
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">- 層級跨度: 1 層
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">- 預估工時: 2-8 小時（1 個工作天內）
</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></span><span class="line"><span class="ln"> 7</span><span class="cl">- 檔案數: 6-10 個
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">- 層級跨度: 2 層
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- 預估工時: 8-16 小時（1-2 天）
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">God Ticket:
</span></span><span class="line"><span class="ln">12</span><span class="cl">- 檔案數: &gt; 10 個
</span></span><span class="line"><span class="ln">13</span><span class="cl">- 層級跨度: &gt; 2 層
</span></span><span class="line"><span class="ln">14</span><span class="cl">- 預估工時: &gt; 16 小時（&gt; 2 天）</span></span></code></pre></div><p><strong>檢測流程</strong>（Ticket 設計階段）:</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">步驟 1: 列出 Ticket 需要修改的檔案清單
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">步驟 2: 統計檔案數量
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">步驟 3: 使用[層級隔離派工方法論](/record/layered-ticket-methodology/) 第 2.4 節決策樹判斷每個檔案的層級
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">步驟 4: 計算層級跨度
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">步驟 5: 評估預估工時
</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">  ├─ 符合良好標準 → 可執行
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  ├─ 符合需要拆分標準 → 建議拆分
</span></span><span class="line"><span class="ln">10</span><span class="cl">  └─ 符合 God Ticket 標準 → 強制拆分</span></span></code></pre></div><hr>
<h4 id="342-c2-incomplete-ticket-檢測">3.4.2 C2. Incomplete Ticket 檢測</h4>
<p><strong>檢測指標</strong>:</p>
<ol>
<li><strong>測試檔案檢查</strong>: 是否有對應的測試檔案</li>
<li><strong>驗收條件檢查</strong>: Ticket 描述是否包含驗收條件</li>
<li><strong>工作日誌檢查</strong>: 是否完成 TDD 四階段記錄</li>
</ol>
<p><strong>判斷標準</strong>（基於 TDD 四階段要求）:</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">完整 Ticket:
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">- Phase 1: 功能設計完成
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">- Phase 2: 測試設計完成（測試檔案存在）
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">- Phase 3: 實作完成（程式碼檔案）
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">- Phase 4: 重構評估完成
</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">Incomplete Ticket:
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">- 缺少測試檔案（Phase 2 未完成）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- 缺少驗收條件（Phase 1 設計不完整）
</span></span><span class="line"><span class="ln">10</span><span class="cl">- 缺少工作日誌（無法追蹤進度）</span></span></code></pre></div><p><strong>檢測流程</strong>（Code Review 階段）:</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">步驟 1: 檢查 git diff 中的檔案
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  ├─ 是否包含 test/ 目錄的檔案？
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  └─ 測試檔案數量 vs 程式碼檔案數量比例
</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">步驟 2: 檢查 Ticket 描述
</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">步驟 3: 檢查工作日誌
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  ├─ docs/work-logs/vX.Y.Z-*.md 是否存在？
</span></span><span class="line"><span class="ln">10</span><span class="cl">  └─ 是否完成 Phase 1-4 記錄？</span></span></code></pre></div><hr>
<h4 id="343-c3-ambiguous-responsibility-檢測">3.4.3 C3. Ambiguous Responsibility 檢測</h4>
<p><strong>檢測指標</strong>:</p>
<ol>
<li><strong>Ticket 標題格式</strong>: 是否包含層級標示</li>
<li><strong>職責描述清晰度</strong>: 是否明確說明修改哪一層</li>
<li><strong>驗收條件對應性</strong>: 驗收條件是否對應單一層級</li>
</ol>
<p><strong>判斷標準</strong>:</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">職責明確 Ticket:
</span></span><span class="line"><span class="ln">2</span><span class="cl">- 標題: [Layer X] 清楚的功能描述
</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">
</span></span><span class="line"><span class="ln">6</span><span class="cl">職責模糊 Ticket:
</span></span><span class="line"><span class="ln">7</span><span class="cl">- 標題: 沒有 [Layer X] 標示
</span></span><span class="line"><span class="ln">8</span><span class="cl">- 描述: 混合多個層級的職責
</span></span><span class="line"><span class="ln">9</span><span class="cl">- 驗收: 跨多個層級的驗證</span></span></code></pre></div><p><strong>檢測流程</strong>（Ticket 設計階段）:</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">步驟 1: 檢查 Ticket 標題格式
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  ├─ 符合 [Layer X] 格式？ →
</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">步驟 2: 分析 Ticket 描述
</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">步驟 3: 檢查驗收條件
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  ├─ 所有驗收條件都屬於同一層級？ →
</span></span><span class="line"><span class="ln">10</span><span class="cl">  └─ 驗收條件跨多層？ →</span></span></code></pre></div><hr>
<h3 id="35-檢測方法總結表">3.5 檢測方法總結表</h3>
<table>
  <thead>
      <tr>
          <th>Code Smell</th>
          <th>檢測時機</th>
          <th>檢測指標</th>
          <th>判斷標準</th>
          <th>引用<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>A1. Shotgun Surgery</strong></td>
          <td>Ticket 設計</td>
          <td>層級跨度</td>
          <td>&gt; 2 層</td>
          <td>3.1 單層修改原則</td>
      </tr>
      <tr>
          <td><strong>A2. Feature Envy</strong></td>
          <td>Code Review</td>
          <td>直接依賴 Domain</td>
          <td>UI 存取 Entity</td>
          <td>2.2 Layer 2 職責</td>
      </tr>
      <tr>
          <td><strong>A3. Inappropriate Intimacy</strong></td>
          <td>Code Review</td>
          <td>依賴方向</td>
          <td>內層依賴外層</td>
          <td>2.3 依賴方向規則</td>
      </tr>
      <tr>
          <td><strong>A4. Leaky Abstraction</strong></td>
          <td>介面設計</td>
          <td>介面純淨度</td>
          <td>包含實作關鍵字</td>
          <td>2.2 Layer 4 職責</td>
      </tr>
      <tr>
          <td><strong>B1. Divergent Change</strong></td>
          <td>Phase 4 重構</td>
          <td>方法分組數</td>
          <td>&gt; 2 組</td>
          <td>3.2 SRP 理論</td>
      </tr>
      <tr>
          <td><strong>B2. Large Class</strong></td>
          <td>Phase 4 重構</td>
          <td>程式碼行數</td>
          <td>&gt; 300 行</td>
          <td>5.2 量化指標</td>
      </tr>
      <tr>
          <td><strong>B3. Long Method</strong></td>
          <td>Phase 3 實作</td>
          <td>方法行數</td>
          <td>&gt; 50 行</td>
          <td>5.2 量化指標</td>
      </tr>
      <tr>
          <td><strong>B4. Dead Code</strong></td>
          <td>Phase 4 重構</td>
          <td>unused 警告</td>
          <td>dart analyze</td>
          <td>-</td>
      </tr>
      <tr>
          <td><strong>C1. God Ticket</strong></td>
          <td>Ticket 設計</td>
          <td>檔案數</td>
          <td>&gt; 10 個</td>
          <td>5.2 Ticket 粒度</td>
      </tr>
      <tr>
          <td><strong>C2. Incomplete Ticket</strong></td>
          <td>Code Review</td>
          <td>測試檔案</td>
          <td>缺少測試</td>
          <td>TDD 四階段</td>
      </tr>
      <tr>
          <td><strong>C3. Ambiguous Responsibility</strong></td>
          <td>Ticket 設計</td>
          <td>標題格式</td>
          <td>無層級標示</td>
          <td>5.3 Ticket 範例</td>
      </tr>
  </tbody>
</table>
<hr>
<h2 id="第四章重構建議和策略">第四章：重構建議和策略</h2>
<h3 id="41-重構模式對應表">4.1 重構模式對應表</h3>
<p>每種 Code Smell 都有對應的標準重構模式（引用 Martin Fowler 的 Refactoring 書籍）:</p>
<table>
  <thead>
      <tr>
          <th>Code Smell</th>
          <th>重構模式</th>
          <th>重構策略</th>
          <th>預期效果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>A1. Shotgun Surgery</strong></td>
          <td>Extract Interface + Introduce Facade</td>
          <td>引入抽象層隔離變更</td>
          <td>單層修改</td>
      </tr>
      <tr>
          <td><strong>A2. Feature Envy</strong></td>
          <td>Move Method + Extract ViewModel</td>
          <td>移動邏輯到正確層級</td>
          <td>職責對齊</td>
      </tr>
      <tr>
          <td><strong>A3. Inappropriate Intimacy</strong></td>
          <td>Introduce Parameter Object</td>
          <td>打破循環依賴</td>
          <td>依賴方向正確</td>
      </tr>
      <tr>
          <td><strong>A4. Leaky Abstraction</strong></td>
          <td>Extract Interface</td>
          <td>重新設計抽象介面</td>
          <td>隱藏實作細節</td>
      </tr>
      <tr>
          <td><strong>B1. Divergent Change</strong></td>
          <td>Extract Class</td>
          <td>拆分為多個單一職責類別</td>
          <td>符合 SRP</td>
      </tr>
      <tr>
          <td><strong>B2. Large Class</strong></td>
          <td>Extract Class + Move Method</td>
          <td>拆分大類別</td>
          <td>降低複雜度</td>
      </tr>
      <tr>
          <td><strong>B3. Long Method</strong></td>
          <td>Extract Method</td>
          <td>拆分長方法</td>
          <td>提升可讀性</td>
      </tr>
      <tr>
          <td><strong>B4. Dead Code</strong></td>
          <td>Remove Dead Code</td>
          <td>直接刪除</td>
          <td>程式碼簡潔</td>
      </tr>
      <tr>
          <td><strong>C1. God Ticket</strong></td>
          <td>Split Ticket</td>
          <td>拆分為多個單層 Ticket</td>
          <td>降低風險</td>
      </tr>
      <tr>
          <td><strong>C2. Incomplete Ticket</strong></td>
          <td>Add Missing Tests</td>
          <td>補充測試和文件</td>
          <td>完整性</td>
      </tr>
      <tr>
          <td><strong>C3. Ambiguous Responsibility</strong></td>
          <td>Clarify Responsibility</td>
          <td>明確層級和職責</td>
          <td>職責清晰</td>
      </tr>
  </tbody>
</table>
<hr>
<h3 id="42-重構策略詳細說明">4.2 重構策略詳細說明</h3>
<h4 id="421-a1-shotgun-surgery--extract-interface--introduce-facade">4.2.1 A1. Shotgun Surgery → Extract Interface + Introduce Facade</h4>
<p><strong>問題</strong>: 單一變更需要同時修改多個層級</p>
<p><strong>重構步驟</strong>:</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">步驟 1: 分析變更的共同點
</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">步驟 2: 引入 Facade 層
</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></span><span class="line"><span class="ln"> 7</span><span class="cl">步驟 3: 重構為單層修改
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  ├─ Layer 4: 定義 Facade 介面
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  ├─ Layer 3: 實作 Facade
</span></span><span class="line"><span class="ln">10</span><span class="cl">  └─ Layer 2: 呼叫 Facade
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">步驟 4: 驗證重構結果
</span></span><span class="line"><span class="ln">13</span><span class="cl">  └─ 未來相同變更只需要修改 Facade 實作</span></span></code></pre></div><p><strong>完整範例</strong>:</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">// 反例：Before: 新增欄位需要修改 4 層
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1">// Layer 1: UI 新增 Widget
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1">// Layer 2: Controller 新增屬性
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1">// Layer 3: UseCase 新增參數
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1">// Layer 5: Entity 新增欄位
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1">// 正例：After: 引入 BookDetailFacade
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1">// Layer 4: 定義介面
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"></span><span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">IBookDetailFacade</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="n">BookDetailViewModel</span><span class="o">&gt;</span> <span class="n">getBookDetail</span><span class="p">(</span><span class="kt">String</span> <span class="n">id</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1">// Layer 3: 實作 Facade（統一處理資料整合）
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookDetailFacade</span> <span class="kd">implements</span> <span class="n">IBookDetailFacade</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="kd">final</span> <span class="n">IBookRepository</span> <span class="n">bookRepository</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">  <span class="kd">final</span> <span class="n">IRatingRepository</span> <span class="n">ratingRepository</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="n">BookDetailViewModel</span><span class="o">&gt;</span> <span class="n">getBookDetail</span><span class="p">(</span><span class="kt">String</span> <span class="n">id</span><span class="p">)</span> <span class="kd">async</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="kd">final</span> <span class="n">book</span> <span class="o">=</span> <span class="kd">await</span> <span class="n">bookRepository</span><span class="p">.</span><span class="n">findById</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="kd">final</span> <span class="n">rating</span> <span class="o">=</span> <span class="kd">await</span> <span class="n">ratingRepository</span><span class="p">.</span><span class="n">findByBookId</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="k">return</span> <span class="n">BookPresenter</span><span class="p">.</span><span class="n">toViewModel</span><span class="p">(</span><span class="n">book</span><span class="p">,</span> <span class="n">rating</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="c1">// 重構效果:
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="c1">// 未來新增欄位只需要修改 Facade 實作（Layer 3）
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="c1"></span><span class="o">//</span> <span class="n">Layer</span> <span class="m">1</span><span class="p">,</span> <span class="m">2</span><span class="p">,</span> <span class="m">5</span> <span class="err">都不需要修改</span></span></span></code></pre></div><hr>
<h4 id="422-a2-feature-envy--move-method--extract-viewmodel">4.2.2 A2. Feature Envy → Move Method + Extract ViewModel</h4>
<p><strong>問題</strong>: 外層過度依賴內層的實作細節</p>
<p><strong>重構步驟</strong>:</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">步驟 1: 識別 Feature Envy 位置
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  └─ 外層存取內層內部欄位 &gt; 3 次
</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">步驟 2: 引入 ViewModel
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  └─ Layer 2 建立 ViewModel 類別
</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">步驟 3: 建立 Presenter 轉換方法
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  └─ Layer 2 實作 toViewModel(Entity) → ViewModel
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">步驟 4: 重構 UI 依賴
</span></span><span class="line"><span class="ln">11</span><span class="cl">  └─ UI 改為依賴 ViewModel，不依賴 Entity</span></span></code></pre></div><hr>
<h4 id="423-b1-divergent-change--extract-class">4.2.3 B1. Divergent Change → Extract Class</h4>
<p><strong>問題</strong>: 單一類別承擔多個職責</p>
<p><strong>重構步驟</strong>:</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">步驟 1: 分析方法分組
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  └─ 將 public 方法按職責分組
</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">步驟 2: 為每個分組建立新類別
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  └─ 拆分為 BookListController, BookDetailController, BookSearchController
</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">步驟 3: 移動方法到新類別
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  └─ Move Method 重構
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">步驟 4: 更新依賴關係
</span></span><span class="line"><span class="ln">11</span><span class="cl">  └─ 更新 Widget 的依賴</span></span></code></pre></div><hr>
<h4 id="424-b3-long-method--extract-method">4.2.4 B3. Long Method → Extract Method</h4>
<p><strong>問題</strong>: 方法過長（&gt; 50 行）</p>
<p><strong>重構步驟</strong>:</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">步驟 1: 識別邏輯區塊
</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">步驟 2: 為每個區塊建立私有方法
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  └─ Extract Method 重構
</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">步驟 3: 重新命名方法
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  └─ 使用動詞片語描述方法功能
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">步驟 4: 驗證重構結果
</span></span><span class="line"><span class="ln">11</span><span class="cl">  └─ 主方法 &lt; 30 行
</span></span><span class="line"><span class="ln">12</span><span class="cl">  └─ 每個私有方法 &lt; 20 行</span></span></code></pre></div><hr>
<h3 id="43-重構優先級評估標準">4.3 重構優先級評估標準</h3>
<p><strong>評估維度</strong>:</p>
<ol>
<li><strong>影響範圍</strong>: 影響多少檔案和層級（1-5 分）</li>
<li><strong>業務風險</strong>: 是否影響核心業務流程（1-5 分）</li>
<li><strong>累積速度</strong>: 不修正會多快惡化（1-5 分）</li>
</ol>
<p><strong>優先級評估公式</strong>:</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">優先級分數 = (影響範圍 × 3) + (業務風險 × 2) + (累積速度 × 1)
</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">1 分: 單一檔案
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">2 分: 2-3 個檔案
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">3 分: 4-5 個檔案（單層）
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">4 分: 6-10 個檔案（跨 2 層）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">5 分: &gt; 10 個檔案（跨 3+ 層）
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">業務風險評分:
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">1 分: 輔助功能（UI 優化）
</span></span><span class="line"><span class="ln">14</span><span class="cl">2 分: 次要功能（搜尋）
</span></span><span class="line"><span class="ln">15</span><span class="cl">3 分: 常用功能（列表顯示）
</span></span><span class="line"><span class="ln">16</span><span class="cl">4 分: 重要功能（新增書籍）
</span></span><span class="line"><span class="ln">17</span><span class="cl">5 分: 核心功能（資料同步）
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl">累積速度評分:
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl">1 分: 已穩定，不再惡化
</span></span><span class="line"><span class="ln">22</span><span class="cl">2 分: 偶爾新增（每季 1 次）
</span></span><span class="line"><span class="ln">23</span><span class="cl">3 分: 定期新增（每月 1-2 次）
</span></span><span class="line"><span class="ln">24</span><span class="cl">4 分: 頻繁新增（每週 1 次）
</span></span><span class="line"><span class="ln">25</span><span class="cl">5 分: 持續惡化（每天都在新增）
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl">優先級判斷:
</span></span><span class="line"><span class="ln">28</span><span class="cl">分數 &gt; 20 → 高優先級（立即修正）
</span></span><span class="line"><span class="ln">29</span><span class="cl">分數 10-20 → 中優先級（排入下個版本）
</span></span><span class="line"><span class="ln">30</span><span class="cl">分數 &lt; 10 → 低優先級（重構階段處理）</span></span></code></pre></div><p><strong>優先級矩陣範例</strong>:</p>
<table>
  <thead>
      <tr>
          <th>Code Smell</th>
          <th>影響範圍</th>
          <th>業務風險</th>
          <th>累積速度</th>
          <th>分數</th>
          <th>優先級</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Inappropriate Intimacy</td>
          <td>4</td>
          <td>5</td>
          <td>3</td>
          <td>26</td>
          <td>高</td>
      </tr>
      <tr>
          <td>Shotgun Surgery</td>
          <td>5</td>
          <td>4</td>
          <td>2</td>
          <td>25</td>
          <td>高</td>
      </tr>
      <tr>
          <td>God Ticket</td>
          <td>5</td>
          <td>3</td>
          <td>3</td>
          <td>24</td>
          <td>高</td>
      </tr>
      <tr>
          <td>Feature Envy</td>
          <td>3</td>
          <td>3</td>
          <td>3</td>
          <td>15</td>
          <td>中</td>
      </tr>
      <tr>
          <td>Large Class</td>
          <td>2</td>
          <td>3</td>
          <td>4</td>
          <td>16</td>
          <td>中</td>
      </tr>
      <tr>
          <td>Long Method</td>
          <td>1</td>
          <td>2</td>
          <td>3</td>
          <td>8</td>
          <td>低</td>
      </tr>
      <tr>
          <td>Dead Code</td>
          <td>1</td>
          <td>1</td>
          <td>1</td>
          <td>4</td>
          <td>低</td>
      </tr>
  </tbody>
</table>
<hr>
<h3 id="44-重構風險控制策略">4.4 重構風險控制策略</h3>
<p><strong>風險控制原則</strong>:</p>
<ol>
<li><strong>測試覆蓋率要求</strong>: 重構前必須確保測試覆蓋率 100%</li>
<li><strong>漸進式重構</strong>: 每次只重構一個 Code Smell</li>
<li><strong>回滾計畫</strong>: 準備 git revert 的回滾點</li>
</ol>
<p><strong>漸進式重構流程</strong>:</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">步驟 1: 建立 feature branch
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  └─ git checkout -b refactor/fix-shotgun-surgery
</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">步驟 2: 確保測試 100% 通過
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  └─ flutter test（重構前基準）
</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">步驟 3: 執行重構
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  └─ 每完成一個小步驟都執行測試
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">步驟 4: 提交重構結果
</span></span><span class="line"><span class="ln">11</span><span class="cl">  └─ git commit -m &#34;refactor: extract BookDetailFacade&#34;
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">步驟 5: Code Review
</span></span><span class="line"><span class="ln">14</span><span class="cl">  └─ 確保重構符合層級隔離原則
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">步驟 6: 合併到主線
</span></span><span class="line"><span class="ln">17</span><span class="cl">  └─ git merge --no-ff refactor/fix-shotgun-surgery</span></span></code></pre></div><p><strong>測試覆蓋率監控</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><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">flutter <span class="nb">test</span> --coverage
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"># 記錄覆蓋率基準（如 85%）</span>
</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"><span class="c1"># 重構後</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">flutter <span class="nb">test</span> --coverage
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># 確保覆蓋率不降低（≥ 85%）</span></span></span></code></pre></div><hr>
<h2 id="第五章開發階段檢查清單">第五章：開發階段檢查清單</h2>
<h3 id="51-phase-1-設計階段檢查清單ticket-設計">5.1 Phase 1 設計階段檢查清單（Ticket 設計）</h3>
<p><strong>目標</strong>: 在設計階段就發現 Code Smell，避免實作後才修正</p>
<p><strong>檢查項目</strong>:</p>
<h4 id="層級定位檢查">層級定位檢查</h4>
<ul>
<li><input disabled="" type="checkbox"> Ticket 標題包含層級標示（如 [Layer 2]）</li>
<li><input disabled="" type="checkbox"> 職責描述清楚說明修改哪一層</li>
<li><input disabled="" type="checkbox"> 使用<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 第 2.4 節決策樹確認層級定位正確</li>
</ul>
<p><strong>單層修改檢查</strong>（引用<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 第 3.1 節）</p>
<ul>
<li><input disabled="" type="checkbox"> 所有檔案都屬於同一層級</li>
<li><input disabled="" type="checkbox"> 變更原因單一且明確</li>
<li><input disabled="" type="checkbox"> 不需要同步修改其他層級</li>
</ul>
<p><strong>Ticket 粒度檢查</strong>（引用<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 第 5.2 節）</p>
<ul>
<li><input disabled="" type="checkbox"> 檔案數: 1-5 個</li>
<li><input disabled="" type="checkbox"> 預估工時: 2-8 小時（1 個工作天內）</li>
<li><input disabled="" type="checkbox"> 如果超過標準，規劃拆分策略</li>
</ul>
<h5 id="code-smell-預防檢查">Code Smell 預防檢查</h5>
<ul>
<li><input disabled="" type="checkbox"> 檢查是否有 Shotgun Surgery 風險（層級跨度 &gt; 1）</li>
<li><input disabled="" type="checkbox"> 檢查是否有 God Ticket 風險（檔案數 &gt; 5）</li>
<li><input disabled="" type="checkbox"> 檢查是否有 Ambiguous Responsibility 風險（職責不明確）</li>
</ul>
<h6 id="依賴關係檢查">依賴關係檢查</h6>
<ul>
<li><input disabled="" type="checkbox"> 依賴的內層介面已存在（或同時設計）</li>
<li><input disabled="" type="checkbox"> 依賴方向正確（外層→內層）</li>
<li><input disabled="" type="checkbox"> 不存在循環依賴</li>
</ul>
<hr>
<h3 id="52-phase-2-測試設計階段檢查清單">5.2 Phase 2 測試設計階段檢查清單</h3>
<p><strong>目標</strong>: 確保測試範圍限定在單一層級</p>
<p><strong>檢查項目</strong>:</p>
<p><strong>測試範圍檢查</strong>（引用<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 第 6.4 節）</p>
<ul>
<li><input disabled="" type="checkbox"> 測試只驗證該層級的職責</li>
<li><input disabled="" type="checkbox"> 不需要啟動其他層級（使用 Mock）</li>
<li><input disabled="" type="checkbox"> 測試檔案路徑對應層級結構</li>
</ul>
<h4 id="測試獨立性檢查">測試獨立性檢查</h4>
<ul>
<li><input disabled="" type="checkbox"> 測試不依賴外部資源（資料庫、網路）</li>
<li><input disabled="" type="checkbox"> 測試可以獨立執行（不依賴其他測試）</li>
<li><input disabled="" type="checkbox"> 使用 Mock/Stub 隔離依賴</li>
</ul>
<h5 id="測試完整性檢查">測試完整性檢查</h5>
<ul>
<li><input disabled="" type="checkbox"> 正常流程測試（Happy Path）</li>
<li><input disabled="" type="checkbox"> 異常流程測試（Error Cases）</li>
<li><input disabled="" type="checkbox"> 邊界條件測試（Boundary Conditions）</li>
</ul>
<h6 id="code-smell-檢查">Code Smell 檢查</h6>
<ul>
<li><input disabled="" type="checkbox"> 檢查是否有 Incomplete Ticket 風險（缺少測試）</li>
<li><input disabled="" type="checkbox"> 測試覆蓋率目標設定（100%）</li>
</ul>
<hr>
<h3 id="53-phase-3-實作階段檢查清單">5.3 Phase 3 實作階段檢查清單</h3>
<p><strong>目標</strong>: 確保實作符合層級隔離原則，不產生 Code Smell</p>
<p><strong>檢查項目</strong>:</p>
<h4 id="程式碼品質檢查">程式碼品質檢查</h4>
<ul>
<li><input disabled="" type="checkbox"> 方法行數 &lt; 50 行（避免 Long Method）</li>
<li><input disabled="" type="checkbox"> 類別行數 &lt; 300 行（避免 Large Class）</li>
<li><input disabled="" type="checkbox"> 巢狀層級 &lt; 3 層</li>
<li><input disabled="" type="checkbox"> 使用 package 導入格式（避免相對路徑）</li>
</ul>
<h5 id="層級隔離檢查">層級隔離檢查</h5>
<ul>
<li><input disabled="" type="checkbox"> import 語句只引用內層或同層</li>
<li><input disabled="" type="checkbox"> 不存在內層依賴外層的情況</li>
<li><input disabled="" type="checkbox"> 使用介面依賴，不依賴具體實作</li>
</ul>
<h6 id="code-smell-檢查-1">Code Smell 檢查</h6>
<ul>
<li><input disabled="" type="checkbox"> 檢查是否有 Feature Envy（UI 直接存取 Domain）</li>
<li><input disabled="" type="checkbox"> 檢查是否有 Inappropriate Intimacy（依賴方向錯誤）</li>
<li><input disabled="" type="checkbox"> 檢查是否有 Leaky Abstraction（介面洩漏實作）</li>
<li><input disabled="" type="checkbox"> 檢查是否有 Divergent Change（方法可分組）</li>
</ul>
<h6 id="測試執行檢查">測試執行檢查</h6>
<ul>
<li><input disabled="" type="checkbox"> 所有測試 100% 通過</li>
<li><input disabled="" type="checkbox"> dart analyze 無錯誤和警告</li>
<li><input disabled="" type="checkbox"> 程式碼覆蓋率達到 100%</li>
</ul>
<hr>
<h3 id="54-phase-4-重構階段檢查清單">5.4 Phase 4 重構階段檢查清單</h3>
<p><strong>目標</strong>: 識別需要重構的 Code Smell</p>
<p><strong>檢查項目</strong>:</p>
<h4 id="code-smell-掃描">Code Smell 掃描</h4>
<ul>
<li><input disabled="" type="checkbox"> 使用 dart analyze 檢測 unused 警告（Dead Code）</li>
<li><input disabled="" type="checkbox"> 檢查方法行數和類別行數（Long Method, Large Class）</li>
<li><input disabled="" type="checkbox"> 檢查方法分組（Divergent Change）</li>
<li><input disabled="" type="checkbox"> 檢查依賴方向（Inappropriate Intimacy）</li>
</ul>
<h5 id="重構優先級評估">重構優先級評估</h5>
<ul>
<li><input disabled="" type="checkbox"> 計算影響範圍（1-5）</li>
<li><input disabled="" type="checkbox"> 評估業務風險（1-5）</li>
<li><input disabled="" type="checkbox"> 評估累積速度（1-5）</li>
<li><input disabled="" type="checkbox"> 計算優先級分數</li>
</ul>
<h6 id="重構執行檢查">重構執行檢查</h6>
<ul>
<li><input disabled="" type="checkbox"> 重構前測試覆蓋率基準</li>
<li><input disabled="" type="checkbox"> 漸進式重構（每次一個 Code Smell）</li>
<li><input disabled="" type="checkbox"> 重構後測試覆蓋率不降低</li>
<li><input disabled="" type="checkbox"> Code Review 確認重構正確性</li>
</ul>
<h6 id="重構完成檢查">重構完成檢查</h6>
<ul>
<li><input disabled="" type="checkbox"> Code Smell 已修正</li>
<li><input disabled="" type="checkbox"> 所有測試通過</li>
<li><input disabled="" type="checkbox"> 工作日誌記錄重構決策</li>
</ul>
<hr>
<h6 id="第一批次撰寫完成第一章到第五章">第一批次撰寫完成（第一章到第五章）</h6>
<hr>
<h2 id="第六章code-review-檢查清單">第六章：Code Review 檢查清單</h2>
<h3 id="61-快速檢查5-分鐘">6.1 快速檢查（5 分鐘）</h3>
<p><strong>目標</strong>: 快速識別 PR 中的明顯 Code Smell</p>
<p><strong>檢查項目</strong>:</p>
<p><strong>層級隔離快速檢查</strong>（引用<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 第 6.2 節）</p>
<ul>
<li>
<p><input disabled="" type="checkbox"> <strong>檔案路徑檢查</strong>: 所有修改檔案都屬於同一層級？</p>
<ul>
<li>使用<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 第 2.4 節決策樹快速判斷</li>
<li>如果跨多層 → 檢查是否有 Shotgun Surgery</li>
</ul>
</li>
<li>
<p><input disabled="" type="checkbox"> <strong>import 語句檢查</strong>: 依賴方向正確？</p>
<ul>
<li>檢查是否有內層依賴外層（Inappropriate Intimacy）</li>
<li>檢查是否有 UI 直接 import Domain Entity（Feature Envy）</li>
</ul>
</li>
<li>
<p><input disabled="" type="checkbox"> <strong>測試檔案檢查</strong>: 測試路徑對應層級結構？</p>
<ul>
<li>test/ 目錄結構是否對應 lib/ 結構</li>
<li>測試檔案數量是否與程式碼檔案數量相當</li>
</ul>
</li>
</ul>
<h4 id="ticket-粒度快速檢查">Ticket 粒度快速檢查</h4>
<ul>
<li>
<p><input disabled="" type="checkbox"> <strong>檔案數量 &lt; 5 個？</strong></p>
<ul>
<li>
<blockquote>
<p>5 個檔案 → 可能是 God Ticket</p></blockquote>
</li>
<li>
<blockquote>
<p>10 個檔案 → 強烈建議拆分</p></blockquote>
</li>
</ul>
</li>
<li>
<p><input disabled="" type="checkbox"> <strong>程式碼變更行數合理（&lt; 500 行）？</strong></p>
<ul>
<li>變更行數過多可能暗示 Ticket 範圍過大</li>
</ul>
</li>
</ul>
<h5 id="明顯-code-smell-檢查">明顯 Code Smell 檢查</h5>
<ul>
<li>
<p><input disabled="" type="checkbox"> <strong>UI 層是否包含業務邏輯？</strong></p>
<ul>
<li>檢查 Widget 中是否有業務規則判斷</li>
<li>檢查是否有業務計算邏輯</li>
</ul>
</li>
<li>
<p><input disabled="" type="checkbox"> <strong>Domain 層是否依賴外層？</strong></p>
<ul>
<li>檢查 Domain Entity 的 import 語句</li>
<li>確認沒有依賴 UseCase 或 Controller</li>
</ul>
</li>
<li>
<p><input disabled="" type="checkbox"> <strong>是否有註解掉的程式碼？</strong></p>
<ul>
<li>註解掉的程式碼應該刪除，不應保留</li>
</ul>
</li>
</ul>
<hr>
<h3 id="62-深度檢查15-分鐘">6.2 深度檢查（15 分鐘）</h3>
<p><strong>目標</strong>: 全面檢查所有類別的 Code Smell</p>
<h4 id="a-類-code-smell-檢查跨層級">A 類 Code Smell 檢查（跨層級）</h4>
<h5 id="shotgun-surgery-檢查">Shotgun Surgery 檢查</h5>
<ul>
<li><input disabled="" type="checkbox"> 統計 PR 修改的檔案數和層級跨度</li>
<li><input disabled="" type="checkbox"> 檢查是否有單一變更需要修改多個層級</li>
<li><input disabled="" type="checkbox"> 評估是否應該引入 Facade 隔離變更</li>
</ul>
<h6 id="feature-envy-檢查">Feature Envy 檢查</h6>
<ul>
<li>
<p><input disabled="" type="checkbox"> 檢查 UI 是否直接依賴 Entity</p>
<ul>
<li>搜尋 <code>import .*/domains/.*/entities/</code></li>
<li>確認 UI 使用 ViewModel</li>
</ul>
</li>
<li>
<p><input disabled="" type="checkbox"> 統計外層存取內層內部欄位次數</p>
<ul>
<li>超過 3 次 → Feature Envy</li>
<li>建議引入 Presenter 轉換</li>
</ul>
</li>
</ul>
<h6 id="inappropriate-intimacy-檢查">Inappropriate Intimacy 檢查</h6>
<ul>
<li>
<p><input disabled="" type="checkbox"> 檢查依賴方向是否正確</p>
<ul>
<li>Domain 不應依賴外層</li>
<li>UseCase 應依賴介面，不依賴具體實作</li>
</ul>
</li>
<li>
<p><input disabled="" type="checkbox"> 檢查是否有循環依賴</p>
<ul>
<li>執行 <code>dart analyze</code> 確認</li>
</ul>
</li>
</ul>
<h6 id="leaky-abstraction-檢查">Leaky Abstraction 檢查</h6>
<ul>
<li>
<p><input disabled="" type="checkbox"> 檢查 Repository 介面是否洩漏實作細節</p>
<ul>
<li>方法名稱不應包含 SQL、HTTP、Cache 等關鍵字</li>
<li>參數類型應該是 Domain 概念</li>
</ul>
</li>
<li>
<p><input disabled="" type="checkbox"> 檢查 Event 定義是否包含 UI 特定資料</p>
<ul>
<li>不應包含 BuildContext 等 UI 類型</li>
</ul>
</li>
</ul>
<h6 id="b-類-code-smell-檢查單層級">B 類 Code Smell 檢查（單層級）</h6>
<h6 id="divergent-change-檢查">Divergent Change 檢查</h6>
<ul>
<li><input disabled="" type="checkbox"> 分析類別方法是否可以分組
<ul>
<li>
<blockquote>
<p>2 個群組 → Divergent Change</p></blockquote>
</li>
<li>建議拆分為多個單一職責類別</li>
</ul>
</li>
</ul>
<h6 id="large-class-檢查">Large Class 檢查</h6>
<ul>
<li>
<p><input disabled="" type="checkbox"> 檢查類別行數是否超過 300 行</p>
<ul>
<li>使用 <code>wc -l {file}</code> 檢查</li>
<li>超過標準 → 建議拆分</li>
</ul>
</li>
<li>
<p><input disabled="" type="checkbox"> 檢查 public 方法數量是否超過 15 個</p>
</li>
<li>
<p><input disabled="" type="checkbox"> 檢查屬性數量是否超過 12 個</p>
</li>
</ul>
<h6 id="long-method-檢查">Long Method 檢查</h6>
<ul>
<li>
<p><input disabled="" type="checkbox"> 檢查方法行數是否超過 50 行</p>
<ul>
<li>超過標準 → 建議 Extract Method</li>
</ul>
</li>
<li>
<p><input disabled="" type="checkbox"> 檢查巢狀層級是否超過 3 層</p>
<ul>
<li>過深巢狀 → 難以理解和測試</li>
</ul>
</li>
<li>
<p><input disabled="" type="checkbox"> 檢查方法名稱是否包含「And」</p>
<ul>
<li>如 <code>validateAndSave</code> → 應拆分</li>
</ul>
</li>
</ul>
<h6 id="dead-code-檢查">Dead Code 檢查</h6>
<ul>
<li><input disabled="" type="checkbox"> 執行 <code>dart analyze | grep &quot;unused&quot;</code>
<ul>
<li>檢查是否有 unused 警告</li>
<li>確認所有警告都已處理</li>
</ul>
</li>
</ul>
<h6 id="測試完整性檢查-1">測試完整性檢查</h6>
<ul>
<li>
<p><input disabled="" type="checkbox"> <strong>測試覆蓋率是否達到 100%？</strong></p>
<ul>
<li>執行 <code>flutter test --coverage</code></li>
<li>檢查 coverage 報告</li>
</ul>
</li>
<li>
<p><input disabled="" type="checkbox"> <strong>測試是否包含異常流程？</strong></p>
<ul>
<li>確認有 Error Cases 測試</li>
</ul>
</li>
<li>
<p><input disabled="" type="checkbox"> <strong>測試是否獨立（不依賴外部資源）？</strong></p>
<ul>
<li>確認使用 Mock/Stub 隔離依賴</li>
</ul>
</li>
</ul>
<hr>
<h3 id="63-違規模式識別引用層級隔離派工方法論-第-65-節">6.3 違規模式識別（引用<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 第 6.5 節）</h3>
<p><strong>常見違規模式</strong>:</p>
<h4 id="違規模式-1-ui-層包含業務邏輯">違規模式 1: UI 層包含業務邏輯</h4>





<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="kd">class</span> <span class="nc">BookDetailWidget</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="n">Widget</span> <span class="n">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="c1">// 反例：業務規則不應在 UI 層
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">book</span><span class="p">.</span><span class="n">publicationDate</span><span class="p">.</span><span class="n">year</span> <span class="o">&gt;=</span> <span class="m">2024</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">      <span class="k">return</span> <span class="n">Text</span><span class="p">(</span><span class="s1">&#39;新書&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="c1">// 反例：業務計算不應在 UI 層
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"></span>    <span class="kd">final</span> <span class="n">discountedPrice</span> <span class="o">=</span> <span class="n">book</span><span class="p">.</span><span class="n">price</span> <span class="o">*</span> <span class="m">0.9</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="k">return</span> <span class="n">Text</span><span class="p">(</span><span class="s1">&#39;優惠價: </span><span class="si">$</span><span class="n">discountedPrice</span><span class="s1">&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1">// 正例：業務邏輯在 Domain 層
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">Book</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">  <span class="kt">bool</span> <span class="n">isNewRelease</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="k">return</span> <span class="n">publicationDate</span><span class="p">.</span><span class="n">year</span> <span class="o">&gt;=</span> <span class="m">2024</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl">  <span class="kt">double</span> <span class="n">getDiscountedPrice</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="k">return</span> <span class="n">price</span> <span class="o">*</span> <span class="m">0.9</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="c1">// 正例：UI 使用 ViewModel
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookDetailWidget</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">  <span class="n">Widget</span> <span class="n">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">    <span class="k">return</span> <span class="n">Column</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">      <span class="nl">children:</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">viewModel</span><span class="p">.</span><span class="n">isNew</span><span class="p">)</span> <span class="n">Text</span><span class="p">(</span><span class="s1">&#39;新書&#39;</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">        <span class="n">Text</span><span class="p">(</span><span class="s1">&#39;優惠價: </span><span class="si">${</span><span class="n">viewModel</span><span class="p">.</span><span class="n">discountedPrice</span><span class="si">}</span><span class="s1">&#39;</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">      <span class="p">],</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h4 id="違規模式-2-controller-包含業務規則">違規模式 2: Controller 包含業務規則</h4>





<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="kd">class</span> <span class="nc">BookController</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="kt">void</span> <span class="n">validateBook</span><span class="p">(</span><span class="n">Book</span> <span class="n">book</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="c1">// 反例：業務規則應在 Domain 層
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">book</span><span class="p">.</span><span class="n">isbn</span><span class="p">.</span><span class="n">length</span> <span class="o">!=</span> <span class="m">13</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">      <span class="k">throw</span> <span class="n">ValidationException</span><span class="p">(</span><span class="s1">&#39;ISBN 必須是 13 碼&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1">// 正例：業務規則在 Domain 層
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">ISBN</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">  <span class="kd">final</span> <span class="kt">String</span> <span class="n">value</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">  <span class="n">ISBN</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="n">value</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">value</span><span class="p">.</span><span class="n">length</span> <span class="o">!=</span> <span class="m">13</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">      <span class="k">throw</span> <span class="n">ValidationException</span><span class="p">(</span><span class="s1">&#39;ISBN 必須是 13 碼&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h4 id="違規模式-3-usecase-包含-ui-邏輯">違規模式 3: UseCase 包含 UI 邏輯</h4>





<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="kd">class</span> <span class="nc">GetBookDetailUseCase</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">String</span><span class="o">&gt;</span> <span class="n">execute</span><span class="p">(</span><span class="kt">String</span> <span class="n">id</span><span class="p">)</span> <span class="kd">async</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="kd">final</span> <span class="n">book</span> <span class="o">=</span> <span class="kd">await</span> <span class="n">repository</span><span class="p">.</span><span class="n">findById</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="c1">// 反例：UI 格式化不應在 UseCase
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span>    <span class="k">return</span> <span class="s1">&#39;書名: </span><span class="si">${</span><span class="n">book</span><span class="p">.</span><span class="n">title</span><span class="si">}</span><span class="s1">&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1">// 正例：UseCase 回傳 Domain 類型
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">GetBookDetailUseCase</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="n">Book</span><span class="o">&gt;</span> <span class="n">execute</span><span class="p">(</span><span class="kt">String</span> <span class="n">id</span><span class="p">)</span> <span class="kd">async</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="k">return</span> <span class="kd">await</span> <span class="n">repository</span><span class="p">.</span><span class="n">findById</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c1">// 正例：Presenter 負責轉換
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookPresenter</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">  <span class="kd">static</span> <span class="n">BookViewModel</span> <span class="n">toViewModel</span><span class="p">(</span><span class="n">Book</span> <span class="n">book</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="k">return</span> <span class="n">BookViewModel</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">      <span class="nl">displayText:</span> <span class="s1">&#39;書名: </span><span class="si">${</span><span class="n">book</span><span class="p">.</span><span class="n">title</span><span class="p">.</span><span class="n">value</span><span class="si">}</span><span class="s1">&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><hr>
<h3 id="64-code-review-報告模板">6.4 Code Review 報告模板</h3>
<p><strong>Code Smell 檢測報告格式</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gh"># Code Smell 檢測報告
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gh"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gs">**檢測時間**</span>: 2025-10-11 14:30
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gs">**檢測範圍**</span>: PR <span class="ni">#123</span> - [Layer 2] 實作書籍詳情頁面事件處理
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gs">**Reviewer**</span>: <span class="ni">@reviewer</span>-name
</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">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">## 檢測總結
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span><span class="k">-</span> **高優先級問題**: 1 個
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">-</span> **中優先級問題**: 1 個
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">-</span> **低優先級問題**: 0 個
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">-</span> **總體評估**: 需要修正後再合併
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">---
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="gu">## 高優先級問題
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="gu">### Shotgun Surgery 檢測結果
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="gu"></span>**檔案清單**:
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">-</span> lib/presentation/widgets/book_detail_widget.dart (Layer 1)
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">-</span> lib/presentation/controllers/book_detail_controller.dart (Layer 2)
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">-</span> lib/application/use_cases/get_book_detail_use_case.dart (Layer 3)
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="k">-</span> lib/domain/entities/book.dart (Layer 5)
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="gs">**分析**</span>:
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="k">-</span> 檔案數: 4 個
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="k">-</span> 層級跨度: 4 層（Layer 1, 2, 3, 5）
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="k">-</span> 判斷: Shotgun Surgery
</span></span><span class="line"><span class="ln">29</span><span class="cl">
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="gs">**建議**</span>:
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="k">-</span> 拆分為 4 個獨立 Ticket
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="k">-</span> 每個 Ticket 只修改單一層級
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="k">-</span> 引入 Facade 隔離變更
</span></span><span class="line"><span class="ln">34</span><span class="cl">
</span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="gs">**影響評估**</span>:
</span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="k">-</span> 影響範圍: 5 分（跨 4 層）
</span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="k">-</span> 業務風險: 4 分（重要功能）
</span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="k">-</span> 累積速度: 2 分（偶爾新增）
</span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="k">-</span> 優先級分數: 25（高優先級）
</span></span><span class="line"><span class="ln">40</span><span class="cl">
</span></span><span class="line"><span class="ln">41</span><span class="cl">---
</span></span><span class="line"><span class="ln">42</span><span class="cl">
</span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="gu">## 中優先級問題
</span></span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="gu">### 警告 Large Class 檢測結果
</span></span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="gs">**檔案**</span>: <span class="sb">`lib/presentation/controllers/book_controller.dart`</span>
</span></span><span class="line"><span class="ln">47</span><span class="cl">
</span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="gs">**分析**</span>:
</span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="k">-</span> 總行數: 320 行（超過 300 行標準）
</span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="k">-</span> public 方法: 18 個（超過 15 個標準）
</span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="k">-</span> 方法分組: 3 組（列表、詳情、搜尋）
</span></span><span class="line"><span class="ln">52</span><span class="cl">
</span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="gs">**建議**</span>:
</span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="k">-</span> Extract Class 重構
</span></span><span class="line"><span class="ln">55</span><span class="cl"><span class="k">-</span> 拆分為 BookListController、BookDetailController、BookSearchController
</span></span><span class="line"><span class="ln">56</span><span class="cl">
</span></span><span class="line"><span class="ln">57</span><span class="cl"><span class="gs">**影響評估**</span>:
</span></span><span class="line"><span class="ln">58</span><span class="cl"><span class="k">-</span> 影響範圍: 2 分（2-3 個檔案）
</span></span><span class="line"><span class="ln">59</span><span class="cl"><span class="k">-</span> 業務風險: 3 分（常用功能）
</span></span><span class="line"><span class="ln">60</span><span class="cl"><span class="k">-</span> 累積速度: 4 分（頻繁新增）
</span></span><span class="line"><span class="ln">61</span><span class="cl"><span class="k">-</span> 優先級分數: 16（中優先級）
</span></span><span class="line"><span class="ln">62</span><span class="cl">
</span></span><span class="line"><span class="ln">63</span><span class="cl">---
</span></span><span class="line"><span class="ln">64</span><span class="cl">
</span></span><span class="line"><span class="ln">65</span><span class="cl"><span class="gu">## 無檢測到的 Code Smell
</span></span></span><span class="line"><span class="ln">66</span><span class="cl"><span class="gu"></span><span class="k">-</span> Long Method
</span></span><span class="line"><span class="ln">67</span><span class="cl"><span class="k">-</span> Dead Code
</span></span><span class="line"><span class="ln">68</span><span class="cl"><span class="k">-</span> Feature Envy
</span></span><span class="line"><span class="ln">69</span><span class="cl"><span class="k">-</span> Inappropriate Intimacy
</span></span><span class="line"><span class="ln">70</span><span class="cl"><span class="k">-</span> Leaky Abstraction
</span></span><span class="line"><span class="ln">71</span><span class="cl">
</span></span><span class="line"><span class="ln">72</span><span class="cl">---
</span></span><span class="line"><span class="ln">73</span><span class="cl">
</span></span><span class="line"><span class="ln">74</span><span class="cl"><span class="gu">## 測試覆蓋率
</span></span></span><span class="line"><span class="ln">75</span><span class="cl"><span class="gu"></span><span class="k">-</span> **覆蓋率**: 98%
</span></span><span class="line"><span class="ln">76</span><span class="cl"><span class="k">-</span> **未覆蓋檔案**: <span class="sb">`book_controller.dart`</span> line 285-290
</span></span><span class="line"><span class="ln">77</span><span class="cl"><span class="k">-</span> **建議**: 補充測試覆蓋未測試部分
</span></span><span class="line"><span class="ln">78</span><span class="cl">
</span></span><span class="line"><span class="ln">79</span><span class="cl">---
</span></span><span class="line"><span class="ln">80</span><span class="cl">
</span></span><span class="line"><span class="ln">81</span><span class="cl"><span class="gu">## 總體建議
</span></span></span><span class="line"><span class="ln">82</span><span class="cl"><span class="gu"></span><span class="k">1.</span> <span class="gs">**立即處理**</span>: Shotgun Surgery（高優先級）
</span></span><span class="line"><span class="ln">83</span><span class="cl">   <span class="k">-</span> 拆分 PR 為 4 個獨立 Ticket
</span></span><span class="line"><span class="ln">84</span><span class="cl">   <span class="k">-</span> 每個 Ticket 遵循單層修改原則
</span></span><span class="line"><span class="ln">85</span><span class="cl">
</span></span><span class="line"><span class="ln">86</span><span class="cl"><span class="k">2.</span> <span class="gs">**下個版本處理**</span>: Large Class（中優先級）
</span></span><span class="line"><span class="ln">87</span><span class="cl">   <span class="k">-</span> 建立 Refactoring Ticket
</span></span><span class="line"><span class="ln">88</span><span class="cl">   <span class="k">-</span> 執行 Extract Class 重構
</span></span><span class="line"><span class="ln">89</span><span class="cl">
</span></span><span class="line"><span class="ln">90</span><span class="cl"><span class="k">3.</span> <span class="gs">**補充測試**</span>: 測試覆蓋率不足部分
</span></span><span class="line"><span class="ln">91</span><span class="cl">   <span class="k">-</span> 補充 line 285-290 測試
</span></span><span class="line"><span class="ln">92</span><span class="cl">
</span></span><span class="line"><span class="ln">93</span><span class="cl">---
</span></span><span class="line"><span class="ln">94</span><span class="cl">
</span></span><span class="line"><span class="ln">95</span><span class="cl"><span class="gs">**審查結論**</span>: 建議重構後再合併 PR
</span></span><span class="line"><span class="ln">96</span><span class="cl"><span class="gs">**預估修正時間**</span>: 4 小時</span></span></code></pre></div><hr>
<h2 id="第七章自動化檢測整合">第七章：自動化檢測整合</h2>
<h3 id="71-hook-系統整合點">7.1 Hook 系統整合點</h3>
<p><strong>目標</strong>: 將 Code Smell 檢測整合到 Hook 系統，實現自動化品質檢查</p>
<h4 id="711-phase-1-設計階段-hook">7.1.1 Phase 1 設計階段 Hook</h4>
<p><strong>Hook 名稱</strong>: Pre-Design Dependency Check Hook</p>
<p><strong>觸發時機</strong>: Ticket 設計完成時（Phase 1 完成）</p>
<p><strong>檢測項目</strong>:</p>
<ol>
<li>
<p>Ticket 粒度檢查</p>
<ul>
<li>計算預估修改檔案數</li>
<li>判斷層級跨度</li>
<li>評估預估工時</li>
</ul>
</li>
<li>
<p>God Ticket 檢測</p>
<ul>
<li>檔案數 &gt; 10 → 警告並建議拆分</li>
<li>層級跨度 &gt; 2 → 強制拆分</li>
</ul>
</li>
<li>
<p>Ambiguous Responsibility 檢測</p>
<ul>
<li>檢查 Ticket 標題是否包含 [Layer X]</li>
<li>檢查職責描述是否明確</li>
</ul>
</li>
</ol>
<p><strong>Hook 行為</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 檢測通過 → 允許進入 Phase 2</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># 檢測失敗 → 提示修正並阻止進入下一階段</span></span></span></code></pre></div><h4 id="712-phase-3-實作階段-hook">7.1.2 Phase 3 實作階段 Hook</h4>
<p><strong>Hook 名稱</strong>: Code Smell Detection Hook</p>
<p><strong>觸發時機</strong>: 程式碼修改後（PostEdit Hook）</p>
<p><strong>檢測項目</strong>:</p>
<ol>
<li>
<p>dart analyze 執行</p>
<ul>
<li>檢測 unused 警告（Dead Code）</li>
<li>檢測語法錯誤</li>
</ul>
</li>
<li>
<p>檔案行數檢查</p>
<ul>
<li>類別行數 &gt; 300 → 警告 Large Class</li>
<li>方法行數 &gt; 50 → 警告 Long Method</li>
</ul>
</li>
<li>
<p>import 語句分析</p>
<ul>
<li>檢測 UI 是否 import Domain Entity（Feature Envy）</li>
<li>檢測依賴方向是否正確（Inappropriate Intimacy）</li>
</ul>
</li>
</ol>
<p><strong>Hook 行為</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 偵測到 Code Smell → 記錄到問題追蹤清單</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># 啟動 agents 處理問題（不阻止開發）</span></span></span></code></pre></div><h4 id="713-code-review-階段-hook">7.1.3 Code Review 階段 Hook</h4>
<p><strong>Hook 名稱</strong>: PR Validation Hook</p>
<p><strong>觸發時機</strong>: 提交 PR 時</p>
<p><strong>檢測項目</strong>:</p>
<ol>
<li>
<p>層級隔離檢查</p>
<ul>
<li>執行完整的 A 類 Code Smell 檢測</li>
<li>檢查所有修改檔案的層級定位</li>
</ul>
</li>
<li>
<p>測試覆蓋率檢查</p>
<ul>
<li>執行 <code>flutter test --coverage</code></li>
<li>確保覆蓋率 ≥ 95%</li>
</ul>
</li>
<li>
<p>Code Smell 掃描</p>
<ul>
<li>執行所有 11 種 Code Smell 檢測</li>
<li>生成 Code Smell 檢測報告</li>
</ul>
</li>
</ol>
<p><strong>Hook 行為</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><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></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"># 中/低優先級問題 → 警告但允許合併</span></span></span></code></pre></div><hr>
<h3 id="72-檢測工具推薦">7.2 檢測工具推薦</h3>
<h4 id="721-靜態分析工具">7.2.1 靜態分析工具</h4>
<p><strong>analysis_options.yaml 配置</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># .claude/analysis_options.yaml</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="nt">analyzer</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">  </span><span class="nt">errors</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span><span class="c"># Dead Code 檢測</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="nt">unused_element</span><span class="p">:</span><span class="w"> </span><span class="l">error</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="nt">unused_import</span><span class="p">:</span><span class="w"> </span><span class="l">error</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="nt">unused_local_variable</span><span class="p">:</span><span class="w"> </span><span class="l">error</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="c"># 依賴方向檢測</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="nt">implementation_imports</span><span class="p">:</span><span class="w"> </span><span class="l">error</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">  </span><span class="nt">exclude</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">    </span>- <span class="l">build/**</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">    </span>- <span class="l">lib/generated/**</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w"></span><span class="nt">linter</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">  </span><span class="nt">rules</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">    </span><span class="c"># 程式碼品質</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">    </span>- <span class="l">avoid_classes_with_only_static_members</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">    </span>- <span class="l">prefer_single_quotes</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">    </span>- <span class="l">lines_longer_than_80_chars</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">    </span><span class="c"># Code Smell 檢測</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">    </span>- <span class="l">avoid_returning_null_for_void</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">    </span>- <span class="l">prefer_final_fields</span><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">    </span>- <span class="l">unnecessary_getters_setters</span></span></span></code></pre></div><h4 id="722-程式碼複雜度工具">7.2.2 程式碼複雜度工具</h4>
<p><strong>安裝和配置</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 安裝 dart_code_metrics</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">dart pub global activate dart_code_metrics
</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 class="c1"># 執行複雜度分析</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">metrics analyze lib/
</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 class="c1"># 設定複雜度閾值</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">metrics check-unused-files lib/
</span></span><span class="line"><span class="ln">9</span><span class="cl">metrics check-unused-code lib/</span></span></code></pre></div><p><strong>analysis_options.yaml 整合</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nt">dart_code_metrics</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">  </span><span class="nt">anti-patterns</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">    </span>- <span class="l">long-method</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span>- <span class="l">long-parameter-list</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">  </span><span class="nt">metrics</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="nt">cyclomatic-complexity</span><span class="p">:</span><span class="w"> </span><span class="m">20</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span><span class="nt">number-of-parameters</span><span class="p">:</span><span class="w"> </span><span class="m">4</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="nt">maximum-nesting-level</span><span class="p">:</span><span class="w"> </span><span class="m">5</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">  </span><span class="nt">rules</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span>- <span class="l">avoid-unused-parameters</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">    </span>- <span class="l">avoid-nested-conditional-expressions</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">    </span>- <span class="l">prefer-trailing-comma</span></span></span></code></pre></div><h4 id="723-測試覆蓋率工具">7.2.3 測試覆蓋率工具</h4>
<p><strong>執行測試和生成報告</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><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">flutter <span class="nb">test</span> --coverage
</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 class="c1"># 生成 HTML 報告</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">genhtml coverage/lcov.info -o coverage/html
</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 class="c1"># 開啟報告</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">open coverage/html/index.html
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># 檢查覆蓋率百分比</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">lcov --summary coverage/lcov.info</span></span></code></pre></div><hr>
<h3 id="73-報告格式設計">7.3 報告格式設計</h3>
<h4 id="731-code-smell-檢測報告-json-格式">7.3.1 Code Smell 檢測報告 JSON 格式</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  <span class="nt">&#34;检测时间&#34;</span><span class="p">:</span> <span class="s2">&#34;2025-10-11T14:30:00Z&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="nt">&#34;检测范围&#34;</span><span class="p">:</span> <span class="s2">&#34;PR #123 - [Layer 2] 實作書籍詳情頁面&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="nt">&#34;总体评估&#34;</span><span class="p">:</span> <span class="s2">&#34;需要修正後再合併&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="nt">&#34;优先级统计&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="nt">&#34;高优先级&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="nt">&#34;中优先级&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="nt">&#34;低优先级&#34;</span><span class="p">:</span> <span class="mi">0</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="nt">&#34;检测结果&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="nt">&#34;A类_跨层级&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">      <span class="p">{</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">        <span class="nt">&#34;类型&#34;</span><span class="p">:</span> <span class="s2">&#34;Shotgun Surgery&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">        <span class="nt">&#34;严重程度&#34;</span><span class="p">:</span> <span class="s2">&#34;高&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">        <span class="nt">&#34;文件数&#34;</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">        <span class="nt">&#34;层级跨度&#34;</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">        <span class="nt">&#34;影响范围&#34;</span><span class="p">:</span> <span class="mi">5</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">        <span class="nt">&#34;业务风险&#34;</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">        <span class="nt">&#34;累积速度&#34;</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">        <span class="nt">&#34;优先级分数&#34;</span><span class="p">:</span> <span class="mi">25</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">        <span class="nt">&#34;建议&#34;</span><span class="p">:</span> <span class="s2">&#34;拆分为 4 个独立 Ticket&#34;</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="nt">&#34;B类_单层级&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">      <span class="p">{</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">        <span class="nt">&#34;类型&#34;</span><span class="p">:</span> <span class="s2">&#34;Large Class&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">        <span class="nt">&#34;严重程度&#34;</span><span class="p">:</span> <span class="s2">&#34;中&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">        <span class="nt">&#34;文件&#34;</span><span class="p">:</span> <span class="s2">&#34;lib/presentation/controllers/book_controller.dart&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">        <span class="nt">&#34;总行数&#34;</span><span class="p">:</span> <span class="mi">320</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">        <span class="nt">&#34;public方法数&#34;</span><span class="p">:</span> <span class="mi">18</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">        <span class="nt">&#34;优先级分数&#34;</span><span class="p">:</span> <span class="mi">16</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">        <span class="nt">&#34;建议&#34;</span><span class="p">:</span> <span class="s2">&#34;Extract Class 重構&#34;</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">    <span class="nt">&#34;C类_Ticket粒度&#34;</span><span class="p">:</span> <span class="p">[]</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">  <span class="nt">&#34;测试覆盖率&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">    <span class="nt">&#34;总覆盖率&#34;</span><span class="p">:</span> <span class="mi">98</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">    <span class="nt">&#34;未覆盖文件&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl">      <span class="p">{</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">        <span class="nt">&#34;文件&#34;</span><span class="p">:</span> <span class="s2">&#34;book_controller.dart&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">42</span><span class="cl">        <span class="nt">&#34;行范围&#34;</span><span class="p">:</span> <span class="s2">&#34;285-290&#34;</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="ln">44</span><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><hr>
<h3 id="74-cicd-整合指引">7.4 CI/CD 整合指引</h3>
<h4 id="741-github-actions-整合">7.4.1 GitHub Actions 整合</h4>
<p><strong>工作流程配置</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># .github/workflows/code-smell-check.yml</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Code Smell 檢測</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"></span><span class="nt">on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">  </span><span class="nt">pull_request</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="nt">branches</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="l">main, develop ]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w"></span><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">  </span><span class="nt">code-smell-check</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v3</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">設定 Flutter</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">subosito/flutter-action@v2</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">          </span><span class="nt">flutter-version</span><span class="p">:</span><span class="w"> </span><span class="s1">&#39;3.16.0&#39;</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">安裝依賴</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">flutter pub get</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Dart Analyze</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">dart analyze</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">檢測 Code Smell</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="sd">          # A 類檢測：檔案路徑分析
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="sd">          python .claude/scripts/check_shotgun_surgery.py
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="sd">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="sd">          # B 類檢測：程式碼複雜度
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="sd">          metrics analyze lib/
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="sd">
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="sd">          # 測試覆蓋率
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="sd">          flutter test --coverage
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="sd">          lcov --summary coverage/lcov.info</span><span class="w">
</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">生成報告</span><span class="w">
</span></span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="sd">          python .claude/scripts/generate_code_smell_report.py \
</span></span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="sd">            --output code-smell-report.json</span><span class="w">
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">上傳報告</span><span class="w">
</span></span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/upload-artifact@v3</span><span class="w">
</span></span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="w">          </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">code-smell-report</span><span class="w">
</span></span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="w">          </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">code-smell-report.json</span><span class="w">
</span></span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">檢查優先級</span><span class="w">
</span></span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="sd">          # 如果有高優先級問題，阻止合併
</span></span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="sd">          python .claude/scripts/check_priority.py \
</span></span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="sd">            --input code-smell-report.json \
</span></span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="sd">            --fail-on-high</span></span></span></code></pre></div><hr>
<h2 id="第八章實踐案例">第八章：實踐案例</h2>
<h3 id="81-案例-1-修正-shotgun-surgery">8.1 案例 1: 修正 Shotgun Surgery</h3>
<p><strong>問題描述</strong>:</p>
<p>Ticket: 新增「書籍評分」功能</p>
<p><strong>初始設計</strong>:</p>
<ul>
<li>需要修改 4 個層級（Layer 1, 2, 3, 5）</li>
<li>修改 6 個檔案</li>
<li>預估工時: 16 小時</li>
</ul>
<p><strong>檢測過程</strong>:</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">步驟 1: 列出涉及的檔案
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">1. lib/presentation/widgets/book_detail_widget.dart (Layer 1)
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">2. lib/presentation/controllers/book_detail_controller.dart (Layer 2)
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">3. lib/application/use_cases/rate_book_use_case.dart (Layer 3)
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">4. lib/application/use_cases/get_book_rating_use_case.dart (Layer 3)
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">5. lib/domain/entities/book.dart (Layer 5)
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">6. lib/domain/value_objects/rating_value.dart (Layer 5)
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">步驟 2: 統計層級跨度
</span></span><span class="line"><span class="ln">10</span><span class="cl">- 層級: Layer 1, 2, 3, 5（4 層）
</span></span><span class="line"><span class="ln">11</span><span class="cl">- 判斷: Shotgun Surgery
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">步驟 3: 計算優先級分數
</span></span><span class="line"><span class="ln">14</span><span class="cl">- 影響範圍: 4 分（6 個檔案，跨 2+ 層）
</span></span><span class="line"><span class="ln">15</span><span class="cl">- 業務風險: 3 分（常用功能）
</span></span><span class="line"><span class="ln">16</span><span class="cl">- 累積速度: 2 分（偶爾新增）
</span></span><span class="line"><span class="ln">17</span><span class="cl">- 優先級分數 = (4 × 3) + (3 × 2) + (2 × 1) = 20
</span></span><span class="line"><span class="ln">18</span><span class="cl">- 判斷: 高優先級（立即修正）</span></span></code></pre></div><p><strong>重構步驟</strong>:</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">步驟 1: 拆分 Ticket（引用[層級隔離派工方法論](/record/layered-ticket-methodology/) 第 5.4 節）
</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">Ticket 1 [Layer 5]: Rating Value Object 和 Book Entity 擴充
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  - 新增 Rating Value Object
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  - Book Entity 新增 rating 屬性
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  - 預估工時: 2 小時
</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">Ticket 2 [Layer 3]: RateBookUseCase 實作
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  - 實作評分業務邏輯
</span></span><span class="line"><span class="ln">10</span><span class="cl">  - 整合 BookRepository
</span></span><span class="line"><span class="ln">11</span><span class="cl">  - 預估工時: 3 小時
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">Ticket 3 [Layer 3]: GetBookRatingUseCase 實作
</span></span><span class="line"><span class="ln">14</span><span class="cl">  - 實作取得評分邏輯
</span></span><span class="line"><span class="ln">15</span><span class="cl">  - 整合 RatingRepository
</span></span><span class="line"><span class="ln">16</span><span class="cl">  - 預估工時: 2 小時
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl">Ticket 4 [Layer 2]: Controller 整合 UseCase
</span></span><span class="line"><span class="ln">19</span><span class="cl">  - BookDetailController 新增評分事件處理
</span></span><span class="line"><span class="ln">20</span><span class="cl">  - Presenter 轉換評分資料
</span></span><span class="line"><span class="ln">21</span><span class="cl">  - 預估工時: 3 小時
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl">Ticket 5 [Layer 1]: UI 新增評分元件
</span></span><span class="line"><span class="ln">24</span><span class="cl">  - 新增 RatingWidget
</span></span><span class="line"><span class="ln">25</span><span class="cl">  - 整合 BookDetailWidget
</span></span><span class="line"><span class="ln">26</span><span class="cl">  - 預估工時: 4 小時
</span></span><span class="line"><span class="ln">27</span><span class="cl">
</span></span><span class="line"><span class="ln">28</span><span class="cl">步驟 2: 執行漸進式實作
</span></span><span class="line"><span class="ln">29</span><span class="cl">  - 每個 Ticket 獨立開發和測試
</span></span><span class="line"><span class="ln">30</span><span class="cl">  - 每個 Ticket 完成 TDD 四階段
</span></span><span class="line"><span class="ln">31</span><span class="cl">  - 按順序合併（Layer 5 → 3 → 2 → 1）</span></span></code></pre></div><p><strong>效果驗證</strong>:</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><span class="line"><span class="ln"> 2</span><span class="cl">- 檔案數: 6 個
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">- 層級跨度: 4 層
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">- 預估工時: 16 小時（單一 Ticket）
</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></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">- Ticket 數: 5 個
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- 每個 Ticket 檔案數: 1-2 個
</span></span><span class="line"><span class="ln">10</span><span class="cl">- 每個 Ticket 層級跨度: 1 層
</span></span><span class="line"><span class="ln">11</span><span class="cl">- 總預估工時: 14 小時（分散到 5 個 Ticket）
</span></span><span class="line"><span class="ln">12</span><span class="cl">- 風險: 低（單層修改，逐步整合）
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl">改善效果:
</span></span><span class="line"><span class="ln">15</span><span class="cl">正例 符合單層修改原則
</span></span><span class="line"><span class="ln">16</span><span class="cl">正例 風險可控
</span></span><span class="line"><span class="ln">17</span><span class="cl">正例 可並行開發（Layer 5 和 Layer 1 可同時開發）
</span></span><span class="line"><span class="ln">18</span><span class="cl">正例 易於測試和驗證</span></span></code></pre></div><hr>
<h3 id="82-案例-2-修正-feature-envy">8.2 案例 2: 修正 Feature Envy</h3>
<p><strong>問題描述</strong>:</p>
<p>在 Code Review 中發現 UI 層直接存取 Domain Entity 內部欄位。</p>
<p><strong>檢測過程</strong>:</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">// lib/presentation/widgets/book_detail_widget.dart
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="k">import</span> <span class="s1">&#39;package:book_overview_app/domains/library/entities/book.dart&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1">// 反例：UI 不應 import Domain Entity
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="kd">class</span> <span class="nc">BookDetailWidget</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="kd">final</span> <span class="n">Book</span> <span class="n">book</span><span class="p">;</span> <span class="c1">// 反例：直接依賴 Entity
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="n">Widget</span> <span class="n">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="k">return</span> <span class="n">Column</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">      <span class="nl">children:</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">        <span class="n">Text</span><span class="p">(</span><span class="n">book</span><span class="p">.</span><span class="n">title</span><span class="p">.</span><span class="n">value</span><span class="p">),</span>        <span class="c1">// 存取 1
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"></span>        <span class="n">Text</span><span class="p">(</span><span class="n">book</span><span class="p">.</span><span class="n">isbn</span><span class="p">.</span><span class="n">value</span><span class="p">),</span>         <span class="c1">// 存取 2
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"></span>        <span class="n">Text</span><span class="p">(</span><span class="n">book</span><span class="p">.</span><span class="n">author</span><span class="p">.</span><span class="n">name</span><span class="p">),</span>        <span class="c1">// 存取 3
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1"></span>        <span class="n">Text</span><span class="p">(</span><span class="n">book</span><span class="p">.</span><span class="n">publisher</span><span class="p">),</span>          <span class="c1">// 存取 4
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c1"></span>        <span class="n">Text</span><span class="p">(</span><span class="n">book</span><span class="p">.</span><span class="n">publicationDate</span><span class="p">.</span><span class="n">toString</span><span class="p">()),</span> <span class="c1">// 存取 5
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"></span>      <span class="p">],</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="c1">// 檢測結果:
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="c1"></span><span class="o">//</span> <span class="o">-</span> <span class="err">直接依賴</span> <span class="n">Domain</span> <span class="n">Entity</span><span class="o">//</span> <span class="o">-</span> <span class="err">存取內部欄位</span> <span class="m">5</span> <span class="err">次（</span><span class="o">&gt;</span> <span class="m">3</span> <span class="err">次標準）</span><span class="o">//</span> <span class="o">-</span> <span class="err">判斷</span><span class="o">:</span> <span class="n">Feature</span> <span class="n">Envy</span></span></span></code></pre></div><p><strong>重構步驟</strong>:</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">// 步驟 1: 建立 ViewModel（Layer 2）
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kd">class</span> <span class="nc">BookDetailViewModel</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="kd">final</span> <span class="kt">String</span> <span class="n">title</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="kd">final</span> <span class="kt">String</span> <span class="n">isbn</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  <span class="kd">final</span> <span class="kt">String</span> <span class="n">author</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="kd">final</span> <span class="kt">String</span> <span class="n">publisher</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="kd">final</span> <span class="kt">String</span> <span class="n">publicationDate</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="n">BookDetailViewModel</span><span class="p">({</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="kd">required</span> <span class="k">this</span><span class="p">.</span><span class="n">title</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="kd">required</span> <span class="k">this</span><span class="p">.</span><span class="n">isbn</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="kd">required</span> <span class="k">this</span><span class="p">.</span><span class="n">author</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="kd">required</span> <span class="k">this</span><span class="p">.</span><span class="n">publisher</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="kd">required</span> <span class="k">this</span><span class="p">.</span><span class="n">publicationDate</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="c1">// 步驟 2: 建立 Presenter 轉換（Layer 2）
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="kd">class</span> <span class="nc">BookDetailPresenter</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">  <span class="kd">static</span> <span class="n">BookDetailViewModel</span> <span class="n">toViewModel</span><span class="p">(</span><span class="n">Book</span> <span class="n">book</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">    <span class="k">return</span> <span class="n">BookDetailViewModel</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">      <span class="nl">title:</span> <span class="n">book</span><span class="p">.</span><span class="n">title</span><span class="p">.</span><span class="n">value</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">      <span class="nl">isbn:</span> <span class="n">book</span><span class="p">.</span><span class="n">isbn</span><span class="p">.</span><span class="n">value</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">      <span class="nl">author:</span> <span class="n">book</span><span class="p">.</span><span class="n">author</span><span class="p">.</span><span class="n">name</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">      <span class="nl">publisher:</span> <span class="n">book</span><span class="p">.</span><span class="n">publisher</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">      <span class="nl">publicationDate:</span> <span class="n">book</span><span class="p">.</span><span class="n">publicationDate</span><span class="p">.</span><span class="n">toString</span><span class="p">(),</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="c1">// 步驟 3: 重構 UI（Layer 1）
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="kd">class</span> <span class="nc">BookDetailWidget</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">  <span class="kd">final</span> <span class="n">BookDetailViewModel</span> <span class="n">viewModel</span><span class="p">;</span> <span class="c1">// 正例：依賴 ViewModel
</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">  <span class="n">Widget</span> <span class="n">build</span><span class="p">(</span><span class="n">BuildContext</span> <span class="n">context</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">    <span class="k">return</span> <span class="n">Column</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl">      <span class="nl">children:</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">        <span class="n">Text</span><span class="p">(</span><span class="n">viewModel</span><span class="p">.</span><span class="n">title</span><span class="p">),</span>           <span class="c1">// 正例：使用轉換後的資料
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="c1"></span>        <span class="n">Text</span><span class="p">(</span><span class="n">viewModel</span><span class="p">.</span><span class="n">isbn</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl">        <span class="n">Text</span><span class="p">(</span><span class="n">viewModel</span><span class="p">.</span><span class="n">author</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">44</span><span class="cl">        <span class="n">Text</span><span class="p">(</span><span class="n">viewModel</span><span class="p">.</span><span class="n">publisher</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl">        <span class="n">Text</span><span class="p">(</span><span class="n">viewModel</span><span class="p">.</span><span class="n">publicationDate</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">46</span><span class="cl">      <span class="p">],</span>
</span></span><span class="line"><span class="ln">47</span><span class="cl">    <span class="p">);</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">50</span><span class="cl">
</span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="c1">// 步驟 4: 更新 Controller（Layer 2）
</span></span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="kd">class</span> <span class="nc">BookDetailController</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">54</span><span class="cl">  <span class="kd">final</span> <span class="n">GetBookDetailUseCase</span> <span class="n">getBookDetailUseCase</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">55</span><span class="cl">  <span class="n">BookDetailViewModel</span><span class="o">?</span> <span class="n">viewModel</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">56</span><span class="cl">
</span></span><span class="line"><span class="ln">57</span><span class="cl">  <span class="kt">void</span> <span class="n">loadBookDetail</span><span class="p">(</span><span class="kt">String</span> <span class="n">id</span><span class="p">)</span> <span class="kd">async</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">58</span><span class="cl">    <span class="kd">final</span> <span class="n">book</span> <span class="o">=</span> <span class="kd">await</span> <span class="n">getBookDetailUseCase</span><span class="p">.</span><span class="n">execute</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">59</span><span class="cl">    <span class="n">viewModel</span> <span class="o">=</span> <span class="n">BookDetailPresenter</span><span class="p">.</span><span class="n">toViewModel</span><span class="p">(</span><span class="n">book</span><span class="p">);</span> <span class="c1">// 正例：轉換
</span></span></span><span class="line"><span class="ln">60</span><span class="cl"><span class="c1"></span>    <span class="n">notifyListeners</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">61</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">62</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p><strong>效果驗證</strong>:</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><span class="line"><span class="ln"> 2</span><span class="cl">- UI 直接依賴 Domain Entity
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">- 存取內部欄位 5 次
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">- 緊耦合，Domain 修改影響 UI
</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></span><span class="line"><span class="ln"> 7</span><span class="cl">- UI 依賴 ViewModel
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">- Presenter 集中處理轉換
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- Domain 修改不影響 UI
</span></span><span class="line"><span class="ln">10</span><span class="cl">- 測試更容易（Mock ViewModel）
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">測試改善:
</span></span><span class="line"><span class="ln">13</span><span class="cl">// 重構前：需要 Mock 整個 Domain Entity
</span></span><span class="line"><span class="ln">14</span><span class="cl">test(&#39;should display book details&#39;, () {
</span></span><span class="line"><span class="ln">15</span><span class="cl">  // 需要建立完整的 Book Entity（複雜）
</span></span><span class="line"><span class="ln">16</span><span class="cl">  final book = Book(...); // 需要所有 Value Objects
</span></span><span class="line"><span class="ln">17</span><span class="cl">  ...
</span></span><span class="line"><span class="ln">18</span><span class="cl">});
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl">// 重構後：只需 Mock ViewModel
</span></span><span class="line"><span class="ln">21</span><span class="cl">test(&#39;should display book details&#39;, () {
</span></span><span class="line"><span class="ln">22</span><span class="cl">  final viewModel = BookDetailViewModel(
</span></span><span class="line"><span class="ln">23</span><span class="cl">    title: &#39;Test Book&#39;,
</span></span><span class="line"><span class="ln">24</span><span class="cl">    isbn: &#39;1234567890123&#39;,
</span></span><span class="line"><span class="ln">25</span><span class="cl">    ...
</span></span><span class="line"><span class="ln">26</span><span class="cl">  );
</span></span><span class="line"><span class="ln">27</span><span class="cl">  // 測試更簡單
</span></span><span class="line"><span class="ln">28</span><span class="cl">});</span></span></code></pre></div><hr>
<h3 id="83-案例-3-拆分-god-ticket">8.3 案例 3: 拆分 God Ticket</h3>
<p><strong>問題描述</strong>:</p>
<p>Ticket: 實作完整的「我的書架」功能</p>
<p><strong>初始 Ticket 設計</strong>:</p>
<ul>
<li>修改 15 個檔案</li>
<li>跨 4 個層級</li>
<li>預估工時: 32 小時</li>
<li>包含：列表顯示、新增書籍、刪除書籍、搜尋、排序</li>
</ul>
<p><strong>檢測過程</strong>:</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">步驟 1: 檔案清單分析
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">Layer 1 (UI):
</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">1. lib/presentation/widgets/bookshelf_screen.dart
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">2. lib/presentation/widgets/book_list_widget.dart
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">3. lib/presentation/widgets/book_item_widget.dart
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">4. lib/presentation/widgets/add_book_dialog.dart
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">Layer 2 (Behavior):
</span></span><span class="line"><span class="ln">10</span><span class="cl">5. lib/presentation/controllers/bookshelf_controller.dart
</span></span><span class="line"><span class="ln">11</span><span class="cl">6. lib/presentation/presenters/book_presenter.dart
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">Layer 3 (UseCase):
</span></span><span class="line"><span class="ln">14</span><span class="cl">7. lib/application/use_cases/get_bookshelf_books_use_case.dart
</span></span><span class="line"><span class="ln">15</span><span class="cl">8. lib/application/use_cases/add_book_to_shelf_use_case.dart
</span></span><span class="line"><span class="ln">16</span><span class="cl">9. lib/application/use_cases/remove_book_from_shelf_use_case.dart
</span></span><span class="line"><span class="ln">17</span><span class="cl">10. lib/application/use_cases/search_bookshelf_use_case.dart
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl">Layer 5 (Domain + Infrastructure):
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl">11. lib/domain/entities/bookshelf.dart
</span></span><span class="line"><span class="ln">22</span><span class="cl">12. lib/domain/value_objects/shelf_name.dart
</span></span><span class="line"><span class="ln">23</span><span class="cl">13. lib/infrastructure/repositories/bookshelf_repository_impl.dart
</span></span><span class="line"><span class="ln">24</span><span class="cl">14. lib/infrastructure/database/bookshelf_table.dart
</span></span><span class="line"><span class="ln">25</span><span class="cl">15. lib/infrastructure/database/bookshelf_book_table.dart
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl">步驟 2: God Ticket 判斷
</span></span><span class="line"><span class="ln">28</span><span class="cl">- 檔案數: 15 個（&gt; 10 個標準）
</span></span><span class="line"><span class="ln">29</span><span class="cl">- 層級跨度: 4 層
</span></span><span class="line"><span class="ln">30</span><span class="cl">- 預估工時: 32 小時（&gt; 16 小時標準）
</span></span><span class="line"><span class="ln">31</span><span class="cl">- 判斷: God Ticket
</span></span><span class="line"><span class="ln">32</span><span class="cl">
</span></span><span class="line"><span class="ln">33</span><span class="cl">步驟 3: 計算優先級分數
</span></span><span class="line"><span class="ln">34</span><span class="cl">- 影響範圍: 5 分（&gt; 10 個檔案，跨 3+ 層）
</span></span><span class="line"><span class="ln">35</span><span class="cl">- 業務風險: 4 分（重要功能）
</span></span><span class="line"><span class="ln">36</span><span class="cl">- 累積速度: 3 分（定期新增）
</span></span><span class="line"><span class="ln">37</span><span class="cl">- 優先級分數 = (5 × 3) + (4 × 2) + (3 × 1) = 26
</span></span><span class="line"><span class="ln">38</span><span class="cl">- 判斷: 高優先級（強制拆分）</span></span></code></pre></div><p><strong>拆分策略</strong>（引用<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 第 5.4 節）:</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">策略 1: 按層級拆分（從內而外）
</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">Ticket 1 [Layer 5]: Bookshelf Domain 設計
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  - Bookshelf Entity
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  - ShelfName Value Object
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  - 檔案數: 2 個，預估: 4 小時
</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">Ticket 2 [Layer 5]: Bookshelf Repository 實作
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  - BookshelfRepositoryImpl
</span></span><span class="line"><span class="ln">10</span><span class="cl">  - 資料庫表格設計
</span></span><span class="line"><span class="ln">11</span><span class="cl">  - 檔案數: 3 個，預估: 6 小時
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">Ticket 3 [Layer 3]: 書架查詢 UseCase
</span></span><span class="line"><span class="ln">14</span><span class="cl">  - GetBookshelfBooksUseCase
</span></span><span class="line"><span class="ln">15</span><span class="cl">  - SearchBookshelfUseCase
</span></span><span class="line"><span class="ln">16</span><span class="cl">  - 檔案數: 2 個，預估: 4 小時
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl">Ticket 4 [Layer 3]: 書架操作 UseCase
</span></span><span class="line"><span class="ln">19</span><span class="cl">  - AddBookToShelfUseCase
</span></span><span class="line"><span class="ln">20</span><span class="cl">  - RemoveBookFromShelfUseCase
</span></span><span class="line"><span class="ln">21</span><span class="cl">  - 檔案數: 2 個，預估: 4 小時
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl">Ticket 5 [Layer 2]: Controller 和 Presenter
</span></span><span class="line"><span class="ln">24</span><span class="cl">  - BookshelfController
</span></span><span class="line"><span class="ln">25</span><span class="cl">  - BookPresenter
</span></span><span class="line"><span class="ln">26</span><span class="cl">  - 檔案數: 2 個，預估: 5 小時
</span></span><span class="line"><span class="ln">27</span><span class="cl">
</span></span><span class="line"><span class="ln">28</span><span class="cl">Ticket 6 [Layer 1]: 書架列表 UI
</span></span><span class="line"><span class="ln">29</span><span class="cl">  - BookshelfScreen
</span></span><span class="line"><span class="ln">30</span><span class="cl">  - BookListWidget
</span></span><span class="line"><span class="ln">31</span><span class="cl">  - BookItemWidget
</span></span><span class="line"><span class="ln">32</span><span class="cl">  - 檔案數: 3 個，預估: 6 小時
</span></span><span class="line"><span class="ln">33</span><span class="cl">
</span></span><span class="line"><span class="ln">34</span><span class="cl">Ticket 7 [Layer 1]: 新增書籍 UI
</span></span><span class="line"><span class="ln">35</span><span class="cl">  - AddBookDialog
</span></span><span class="line"><span class="ln">36</span><span class="cl">  - 整合 Controller
</span></span><span class="line"><span class="ln">37</span><span class="cl">  - 檔案數: 1 個，預估: 3 小時
</span></span><span class="line"><span class="ln">38</span><span class="cl">
</span></span><span class="line"><span class="ln">39</span><span class="cl">策略 2: 按功能拆分（MVP 優先）
</span></span><span class="line"><span class="ln">40</span><span class="cl">
</span></span><span class="line"><span class="ln">41</span><span class="cl">Ticket 1: 書架基礎功能（MVP）
</span></span><span class="line"><span class="ln">42</span><span class="cl">  - 只實作「顯示書架列表」功能
</span></span><span class="line"><span class="ln">43</span><span class="cl">  - Layer 5 + 3 + 2 + 1（最小實作）
</span></span><span class="line"><span class="ln">44</span><span class="cl">  - 檔案數: 7 個，預估: 12 小時
</span></span><span class="line"><span class="ln">45</span><span class="cl">
</span></span><span class="line"><span class="ln">46</span><span class="cl">Ticket 2: 新增書籍功能
</span></span><span class="line"><span class="ln">47</span><span class="cl">  - Layer 3 + 2 + 1
</span></span><span class="line"><span class="ln">48</span><span class="cl">  - 檔案數: 4 個，預估: 8 小時
</span></span><span class="line"><span class="ln">49</span><span class="cl">
</span></span><span class="line"><span class="ln">50</span><span class="cl">Ticket 3: 刪除書籍功能
</span></span><span class="line"><span class="ln">51</span><span class="cl">  - Layer 3 + 2 + 1
</span></span><span class="line"><span class="ln">52</span><span class="cl">  - 檔案數: 3 個，預估: 6 小時
</span></span><span class="line"><span class="ln">53</span><span class="cl">
</span></span><span class="line"><span class="ln">54</span><span class="cl">Ticket 4: 搜尋和排序功能
</span></span><span class="line"><span class="ln">55</span><span class="cl">  - Layer 3 + 2 + 1
</span></span><span class="line"><span class="ln">56</span><span class="cl">  - 檔案數: 4 個，預估: 6 小時
</span></span><span class="line"><span class="ln">57</span><span class="cl">
</span></span><span class="line"><span class="ln">58</span><span class="cl">選擇策略 1（按層級拆分）的理由:
</span></span><span class="line"><span class="ln">59</span><span class="cl">正例 符合從內而外實作順序（[層級隔離派工方法論](/record/layered-ticket-methodology/) 第 4.1 節）
</span></span><span class="line"><span class="ln">60</span><span class="cl">正例 每個 Ticket 單層修改
</span></span><span class="line"><span class="ln">61</span><span class="cl">正例 可並行開發（Layer 5 和 Layer 1 可同時開發）
</span></span><span class="line"><span class="ln">62</span><span class="cl">正例 依賴關係清晰</span></span></code></pre></div><p><strong>效果驗證</strong>:</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">重構前（God Ticket）:
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">- 檔案數: 15 個
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">- 層級跨度: 4 層
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">- 預估工時: 32 小時（單一巨大 Ticket）
</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></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">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">重構後（7 個 Ticket）:
</span></span><span class="line"><span class="ln">10</span><span class="cl">- 每個 Ticket 檔案數: 1-3 個
</span></span><span class="line"><span class="ln">11</span><span class="cl">- 每個 Ticket 層級跨度: 1 層
</span></span><span class="line"><span class="ln">12</span><span class="cl">- 總預估工時: 32 小時（分散到 7 個 Ticket）
</span></span><span class="line"><span class="ln">13</span><span class="cl">- 風險: 低（單層修改）
</span></span><span class="line"><span class="ln">14</span><span class="cl">- 測試困難度: 低（每個 Ticket 獨立測試）
</span></span><span class="line"><span class="ln">15</span><span class="cl">- 可並行開發（Ticket 1-2, Ticket 6-7 可並行）
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl">實際改善效果:
</span></span><span class="line"><span class="ln">18</span><span class="cl">正例 開發時間縮短 20%（並行開發）
</span></span><span class="line"><span class="ln">19</span><span class="cl">正例 Bug 數量減少 60%（單層修改，易於測試）
</span></span><span class="line"><span class="ln">20</span><span class="cl">正例 Code Review 時間縮短 40%（每個 PR 更小）
</span></span><span class="line"><span class="ln">21</span><span class="cl">正例 團隊協作效率提升（可分配給不同開發人員）</span></span></code></pre></div><hr>
<h3 id="84-案例-4-重構-large-class">8.4 案例 4: 重構 Large Class</h3>
<p><strong>問題描述</strong>:</p>
<p>在 Phase 4 重構階段發現 <code>BookController</code> 類別過大。</p>
<p><strong>檢測過程</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><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">wc -l lib/presentation/controllers/book_controller.dart
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"># 輸出: 450 lib/presentation/controllers/book_controller.dart</span>
</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"><span class="c1"># 統計 public 方法數量</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">grep -c <span class="s2">&#34;void\|Future&#34;</span> lib/presentation/controllers/book_controller.dart
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"># 輸出: 25</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"># 分析結果:</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># - 總行數: 450 行（&gt; 300 行標準）</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"># - public 方法: 25 個（&gt; 15 個標準）</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"># - 判斷: Large Class</span></span></span></code></pre></div><p><strong>方法分組分析</strong>:</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">// 分析 BookController 的方法
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookController</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="c1">// 群組 A：書架列表相關（8 個方法）
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"></span>  <span class="n">List</span><span class="o">&lt;</span><span class="n">BookViewModel</span><span class="o">&gt;</span> <span class="n">bookList</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="kt">void</span> <span class="n">loadBookList</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  <span class="kt">void</span> <span class="n">refreshBookList</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="kt">void</span> <span class="n">sortBookList</span><span class="p">(</span><span class="kt">String</span> <span class="n">sortBy</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="kt">void</span> <span class="n">filterBookList</span><span class="p">(</span><span class="kt">String</span> <span class="n">filter</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="kt">void</span> <span class="n">loadMoreBooks</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="kt">void</span> <span class="n">clearBookList</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">  <span class="kt">void</span> <span class="n">updateBookListView</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="kt">void</span> <span class="n">onBookListError</span><span class="p">(</span><span class="n">Exception</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl">  <span class="c1">// 群組 B：書籍詳情相關（7 個方法）
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"></span>  <span class="n">BookViewModel</span><span class="o">?</span> <span class="n">bookDetail</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="kt">void</span> <span class="n">loadBookDetail</span><span class="p">(</span><span class="kt">String</span> <span class="n">id</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">  <span class="kt">void</span> <span class="n">updateBookDetail</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">  <span class="kt">void</span> <span class="n">deleteBook</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">  <span class="kt">void</span> <span class="n">shareBook</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">  <span class="kt">void</span> <span class="n">favoriteBook</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">  <span class="kt">void</span> <span class="n">unfavoriteBook</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">  <span class="kt">void</span> <span class="n">onBookDetailError</span><span class="p">(</span><span class="n">Exception</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl">  <span class="c1">// 群組 C：搜尋相關（6 個方法）
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="c1"></span>  <span class="n">List</span><span class="o">&lt;</span><span class="n">BookViewModel</span><span class="o">&gt;</span> <span class="n">searchResults</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">  <span class="kt">void</span> <span class="n">searchBooks</span><span class="p">(</span><span class="kt">String</span> <span class="n">query</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">  <span class="kt">void</span> <span class="n">clearSearchResults</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">  <span class="kt">void</span> <span class="n">updateSearchQuery</span><span class="p">(</span><span class="kt">String</span> <span class="n">query</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">  <span class="kt">void</span> <span class="n">loadSearchHistory</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">  <span class="kt">void</span> <span class="n">saveSearchHistory</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">  <span class="kt">void</span> <span class="n">deleteSearchHistory</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">
</span></span><span class="line"><span class="ln">33</span><span class="cl">  <span class="c1">// 群組 D：評分相關（4 個方法）
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="c1"></span>  <span class="kt">void</span> <span class="n">rateBook</span><span class="p">(</span><span class="kt">String</span> <span class="n">id</span><span class="p">,</span> <span class="kt">int</span> <span class="n">rating</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">  <span class="kt">void</span> <span class="n">loadBookRating</span><span class="p">(</span><span class="kt">String</span> <span class="n">id</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">  <span class="kt">void</span> <span class="n">updateRating</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">  <span class="kt">void</span> <span class="n">deleteRating</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">
</span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="c1">// 分析結果:
</span></span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="c1">// - 4 個方法群組
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="c1">// - 4 種變更原因（列表、詳情、搜尋、評分）
</span></span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="c1"></span><span class="o">//</span> <span class="o">-</span> <span class="err">判斷</span><span class="o">:</span> <span class="n">Divergent</span> <span class="n">Change</span> <span class="o">+</span> <span class="n">Large</span> <span class="n">Class</span></span></span></code></pre></div><p><strong>重構步驟</strong>:</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">// 步驟 1: Extract Class 重構
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1">// Controller 1：只負責書架列表
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookListController</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="n">List</span><span class="o">&lt;</span><span class="n">BookViewModel</span><span class="o">&gt;</span> <span class="n">bookList</span> <span class="o">=</span> <span class="p">[];</span>
</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 class="kt">void</span> <span class="n">loadBookList</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="kt">void</span> <span class="n">refreshBookList</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="kt">void</span> <span class="n">sortBookList</span><span class="p">(</span><span class="kt">String</span> <span class="n">sortBy</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="kt">void</span> <span class="n">filterBookList</span><span class="p">(</span><span class="kt">String</span> <span class="n">filter</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">  <span class="kt">void</span> <span class="n">loadMoreBooks</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1">// Controller 2：只負責書籍詳情
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookDetailController</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="n">BookViewModel</span><span class="o">?</span> <span class="n">bookDetail</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl">  <span class="kt">void</span> <span class="n">loadBookDetail</span><span class="p">(</span><span class="kt">String</span> <span class="n">id</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">  <span class="kt">void</span> <span class="n">updateBookDetail</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">  <span class="kt">void</span> <span class="n">deleteBook</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">  <span class="kt">void</span> <span class="n">shareBook</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">  <span class="kt">void</span> <span class="n">toggleFavorite</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="c1">// Controller 3：只負責搜尋
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookSearchController</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">  <span class="n">List</span><span class="o">&lt;</span><span class="n">BookViewModel</span><span class="o">&gt;</span> <span class="n">searchResults</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">
</span></span><span class="line"><span class="ln">29</span><span class="cl">  <span class="kt">void</span> <span class="n">searchBooks</span><span class="p">(</span><span class="kt">String</span> <span class="n">query</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">  <span class="kt">void</span> <span class="n">clearSearchResults</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">  <span class="kt">void</span> <span class="n">updateSearchQuery</span><span class="p">(</span><span class="kt">String</span> <span class="n">query</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">  <span class="kt">void</span> <span class="n">manageSearchHistory</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">
</span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="c1">// Controller 4：只負責評分
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookRatingController</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">  <span class="kt">void</span> <span class="n">rateBook</span><span class="p">(</span><span class="kt">String</span> <span class="n">id</span><span class="p">,</span> <span class="kt">int</span> <span class="n">rating</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">  <span class="kt">void</span> <span class="n">loadBookRating</span><span class="p">(</span><span class="kt">String</span> <span class="n">id</span><span class="p">)</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">  <span class="kt">void</span> <span class="n">updateRating</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl">  <span class="kt">void</span> <span class="n">deleteRating</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">42</span><span class="cl">
</span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="c1">// 步驟 2: 更新 Widget 依賴
</span></span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="c1">// Before: 單一巨大 Controller
</span></span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookshelfScreen</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">47</span><span class="cl">  <span class="kd">final</span> <span class="n">BookController</span> <span class="n">controller</span><span class="p">;</span> <span class="c1">// 依賴巨大 Controller
</span></span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl">
</span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="c1">// After: 使用對應的小 Controller
</span></span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookListScreen</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">52</span><span class="cl">  <span class="kd">final</span> <span class="n">BookListController</span> <span class="n">controller</span><span class="p">;</span> <span class="c1">// 只依賴需要的 Controller
</span></span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="ln">54</span><span class="cl">
</span></span><span class="line"><span class="ln">55</span><span class="cl"><span class="kd">class</span> <span class="nc">BookDetailScreen</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">56</span><span class="cl">  <span class="kd">final</span> <span class="n">BookDetailController</span> <span class="n">controller</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">57</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">58</span><span class="cl">
</span></span><span class="line"><span class="ln">59</span><span class="cl"><span class="kd">class</span> <span class="nc">BookSearchScreen</span> <span class="kd">extends</span> <span class="n">StatelessWidget</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">60</span><span class="cl">  <span class="kd">final</span> <span class="n">BookSearchController</span> <span class="n">controller</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">61</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p><strong>效果驗證</strong>:</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">重構前（Large Class）:
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">- BookController: 450 行，25 個方法
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">- 職責: 列表 + 詳情 + 搜尋 + 評分（4 種）
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">- 變更原因: 4 個
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">- 測試困難度: 高（需要 Mock 所有依賴）
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">- 單一測試檔案: 800+ 行
</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">重構後（4 個小 Controller）:
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- BookListController: 120 行，5 個方法
</span></span><span class="line"><span class="ln">10</span><span class="cl">- BookDetailController: 110 行，5 個方法
</span></span><span class="line"><span class="ln">11</span><span class="cl">- BookSearchController: 100 行，4 個方法
</span></span><span class="line"><span class="ln">12</span><span class="cl">- BookRatingController: 80 行，4 個方法
</span></span><span class="line"><span class="ln">13</span><span class="cl">- 每個 Controller 單一職責
</span></span><span class="line"><span class="ln">14</span><span class="cl">- 每個 Controller 單一變更原因
</span></span><span class="line"><span class="ln">15</span><span class="cl">- 測試困難度: 低（每個 Controller 獨立測試）
</span></span><span class="line"><span class="ln">16</span><span class="cl">- 測試檔案: 每個 150-200 行
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl">測試改善:
</span></span><span class="line"><span class="ln">19</span><span class="cl">- 測試執行時間: 從 8 秒 → 2 秒（每個 Controller 獨立測試）
</span></span><span class="line"><span class="ln">20</span><span class="cl">- Mock 複雜度: 降低 70%
</span></span><span class="line"><span class="ln">21</span><span class="cl">- 測試可讀性: 提升（每個測試檔案更專注）
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl">維護改善:
</span></span><span class="line"><span class="ln">24</span><span class="cl">- 修改列表功能: 只需要修改 BookListController
</span></span><span class="line"><span class="ln">25</span><span class="cl">- Bug 定位時間: 縮短 50%（範圍更明確）
</span></span><span class="line"><span class="ln">26</span><span class="cl">- Code Review 時間: 縮短 40%（每個類別更小）</span></span></code></pre></div><hr>
<h3 id="85-案例-5-消除-inappropriate-intimacy">8.5 案例 5: 消除 Inappropriate Intimacy</h3>
<p><strong>問題描述</strong>:</p>
<p>在 Code Review 中發現 Domain 層依賴 UseCase 層。</p>
<p><strong>檢測過程</strong>:</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">// lib/domain/entities/book.dart
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="k">import</span> <span class="s1">&#39;package:book_overview_app/application/use_cases/add_book_to_favorite_use_case.dart&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1">// 反例：Domain 不應 import UseCase
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="kd">class</span> <span class="nc">Book</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="kd">final</span> <span class="kt">String</span> <span class="n">id</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="kd">final</span> <span class="n">Title</span> <span class="n">title</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="kd">final</span> <span class="n">AddBookToFavoriteUseCase</span> <span class="n">favoriteUseCase</span><span class="p">;</span> <span class="c1">// 反例：Domain 依賴 UseCase
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="kt">void</span> <span class="n">addToFavorite</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="n">favoriteUseCase</span><span class="p">.</span><span class="n">execute</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="n">id</span><span class="p">);</span> <span class="c1">// 反例：呼叫 UseCase
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="kt">void</span> <span class="n">removeFromFavorite</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="c1">// 類似的錯誤模式
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="c1">// 檢測結果:
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="c1"></span><span class="o">//</span> <span class="o">-</span> <span class="n">Domain</span> <span class="err">依賴外層（</span><span class="n">UseCase</span><span class="err">）</span><span class="o">//</span> <span class="o">-</span> <span class="err">違反依賴方向規則</span><span class="o">//</span> <span class="o">-</span> <span class="n">Domain</span> <span class="err">失去獨立性</span><span class="o">//</span> <span class="o">-</span> <span class="err">判斷</span><span class="o">:</span> <span class="n">Inappropriate</span> <span class="n">Intimacy</span></span></span></code></pre></div><p><strong>重構步驟</strong>:</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">// 步驟 1: 重新設計 Domain（移除外層依賴）
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1">// 正例： Domain 設計
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">Book</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="kd">final</span> <span class="kt">String</span> <span class="n">id</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  <span class="kd">final</span> <span class="n">Title</span> <span class="n">title</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="kt">bool</span> <span class="n">isFavorited</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span> <span class="c1">// 正例：只記錄狀態
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="c1">// 正例：Domain 只處理業務邏輯
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"></span>  <span class="kt">void</span> <span class="n">markAsFavorite</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="n">isFavorited</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">      <span class="k">throw</span> <span class="n">AlreadyFavoritedException</span><span class="p">(</span><span class="s1">&#39;書籍已在我的最愛&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="n">isFavorited</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl">  <span class="kt">void</span> <span class="n">unmarkFromFavorite</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">isFavorited</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">      <span class="k">throw</span> <span class="n">NotFavoritedException</span><span class="p">(</span><span class="s1">&#39;書籍不在我的最愛&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="n">isFavorited</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl">  <span class="c1">// 正例：Domain 方法完全獨立，無外層依賴
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="c1">// 步驟 2: UseCase 協調業務流程
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="c1">// 正例：UseCase 負責協調
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">AddBookToFavoriteUseCase</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">  <span class="kd">final</span> <span class="n">IBookRepository</span> <span class="n">bookRepository</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">  <span class="kd">final</span> <span class="n">IFavoriteRepository</span> <span class="n">favoriteRepository</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">
</span></span><span class="line"><span class="ln">34</span><span class="cl">  <span class="n">Future</span><span class="o">&lt;</span><span class="kt">void</span><span class="o">&gt;</span> <span class="n">execute</span><span class="p">(</span><span class="kt">String</span> <span class="n">bookId</span><span class="p">)</span> <span class="kd">async</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">    <span class="c1">// 1. 取得書籍
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="c1"></span>    <span class="kd">final</span> <span class="n">book</span> <span class="o">=</span> <span class="kd">await</span> <span class="n">bookRepository</span><span class="p">.</span><span class="n">findById</span><span class="p">(</span><span class="n">bookId</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">
</span></span><span class="line"><span class="ln">38</span><span class="cl">    <span class="c1">// 2. 執行 Domain 方法
</span></span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="c1"></span>    <span class="n">book</span><span class="p">.</span><span class="n">markAsFavorite</span><span class="p">();</span> <span class="c1">// 正例：UseCase 呼叫 Domain
</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">    <span class="c1">// 3. 儲存狀態
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="c1"></span>    <span class="kd">await</span> <span class="n">bookRepository</span><span class="p">.</span><span class="n">save</span><span class="p">(</span><span class="n">book</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl">    <span class="kd">await</span> <span class="n">favoriteRepository</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="n">bookId</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">44</span><span class="cl">
</span></span><span class="line"><span class="ln">45</span><span class="cl">    <span class="c1">// 4. 發送事件
</span></span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="c1"></span>    <span class="n">eventBus</span><span class="p">.</span><span class="n">fire</span><span class="p">(</span><span class="n">BookAddedToFavoriteEvent</span><span class="p">(</span><span class="n">bookId</span><span class="p">));</span>
</span></span><span class="line"><span class="ln">47</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl">
</span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="c1">// 步驟 3: Controller 觸發 UseCase
</span></span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="kd">class</span> <span class="nc">BookDetailController</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">53</span><span class="cl">  <span class="kd">final</span> <span class="n">AddBookToFavoriteUseCase</span> <span class="n">addToFavoriteUseCase</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">54</span><span class="cl">
</span></span><span class="line"><span class="ln">55</span><span class="cl">  <span class="kt">void</span> <span class="n">onFavoriteButtonPressed</span><span class="p">(</span><span class="kt">String</span> <span class="n">bookId</span><span class="p">)</span> <span class="kd">async</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">56</span><span class="cl">    <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">57</span><span class="cl">      <span class="kd">await</span> <span class="n">addToFavoriteUseCase</span><span class="p">.</span><span class="n">execute</span><span class="p">(</span><span class="n">bookId</span><span class="p">);</span> <span class="c1">// 正例：呼叫方向
</span></span></span><span class="line"><span class="ln">58</span><span class="cl"><span class="c1"></span>      <span class="c1">// 更新 UI 狀態
</span></span></span><span class="line"><span class="ln">59</span><span class="cl"><span class="c1"></span>    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="n">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">60</span><span class="cl">      <span class="c1">// 錯誤處理
</span></span></span><span class="line"><span class="ln">61</span><span class="cl"><span class="c1"></span>    <span class="p">}</span>
</span></span><span class="line"><span class="ln">62</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">63</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p><strong>依賴方向驗證</strong>:</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><span class="line"><span class="ln"> 2</span><span class="cl">Layer 5 (Domain) → Layer 3 (UseCase)
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">- Book Entity 依賴 AddBookToFavoriteUseCase
</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">- Domain 失去獨立性和可重用性
</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">Layer 2 → Layer 3 → Layer 5
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- Controller → UseCase → Domain
</span></span><span class="line"><span class="ln">10</span><span class="cl">- 符合依賴倒置原則
</span></span><span class="line"><span class="ln">11</span><span class="cl">- Domain 獨立且純淨
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">依賴關係圖:
</span></span><span class="line"><span class="ln">14</span><span class="cl">重構前:
</span></span><span class="line"><span class="ln">15</span><span class="cl">Book (Layer 5) ──┐
</span></span><span class="line"><span class="ln">16</span><span class="cl">                 ↓
</span></span><span class="line"><span class="ln">17</span><span class="cl">AddBookToFavoriteUseCase (Layer 3) 內層依賴外層
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl">重構後:
</span></span><span class="line"><span class="ln">20</span><span class="cl">BookDetailController (Layer 2)
</span></span><span class="line"><span class="ln">21</span><span class="cl">        ↓
</span></span><span class="line"><span class="ln">22</span><span class="cl">AddBookToFavoriteUseCase (Layer 3)
</span></span><span class="line"><span class="ln">23</span><span class="cl">        ↓
</span></span><span class="line"><span class="ln">24</span><span class="cl">Book (Layer 5) 正確的依賴方向</span></span></code></pre></div><p><strong>效果驗證</strong>:</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><span class="line"><span class="ln"> 2</span><span class="cl">- Domain 依賴 UseCase
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">- Domain 無法獨立測試
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">- Domain 無法重用
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">- 違反 Clean Architecture
</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">- Domain 完全獨立
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- Domain 可獨立測試
</span></span><span class="line"><span class="ln">10</span><span class="cl">- Domain 可重用（可在不同 UseCase 中使用）
</span></span><span class="line"><span class="ln">11</span><span class="cl">- 符合 Clean Architecture
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">測試改善:
</span></span><span class="line"><span class="ln">14</span><span class="cl">// 重構前：Domain 測試需要 Mock UseCase
</span></span><span class="line"><span class="ln">15</span><span class="cl">test(&#39;should add book to favorite&#39;, () {
</span></span><span class="line"><span class="ln">16</span><span class="cl">  final mockUseCase = MockAddBookToFavoriteUseCase();
</span></span><span class="line"><span class="ln">17</span><span class="cl">  final book = Book(favoriteUseCase: mockUseCase); // 需要注入
</span></span><span class="line"><span class="ln">18</span><span class="cl">  book.addToFavorite();
</span></span><span class="line"><span class="ln">19</span><span class="cl">  verify(mockUseCase.execute(book.id)).called(1);
</span></span><span class="line"><span class="ln">20</span><span class="cl">});
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl">// 重構後：Domain 測試完全獨立
</span></span><span class="line"><span class="ln">23</span><span class="cl">test(&#39;should mark book as favorite&#39;, () {
</span></span><span class="line"><span class="ln">24</span><span class="cl">  final book = Book(...); // 無需任何 Mock
</span></span><span class="line"><span class="ln">25</span><span class="cl">  book.markAsFavorite();
</span></span><span class="line"><span class="ln">26</span><span class="cl">  expect(book.isFavorited, true); // 純粹的單元測試
</span></span><span class="line"><span class="ln">27</span><span class="cl">});
</span></span><span class="line"><span class="ln">28</span><span class="cl">
</span></span><span class="line"><span class="ln">29</span><span class="cl">架構改善:
</span></span><span class="line"><span class="ln">30</span><span class="cl">正例 Domain 層純淨（無外層依賴）
</span></span><span class="line"><span class="ln">31</span><span class="cl">正例 依賴方向正確（外層→內層）
</span></span><span class="line"><span class="ln">32</span><span class="cl">正例 可在不同 UseCase 中重用 Domain 邏輯
</span></span><span class="line"><span class="ln">33</span><span class="cl">正例 易於測試和維護</span></span></code></pre></div><hr>
<h2 id="第九章常見問題-faq">第九章：常見問題 FAQ</h2>
<h3 id="91-理論問題">9.1 理論問題</h3>
<h4 id="q1-code-smell-和-bug-有什麼區別">Q1: Code Smell 和 Bug 有什麼區別？</h4>
<p><strong>答</strong>:</p>
<table>
  <thead>
      <tr>
          <th>特性</th>
          <th>Bug（程式錯誤）</th>
          <th>Code Smell（程式異味）</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>影響</strong></td>
          <td>導致功能失敗或程式崩潰</td>
          <td>程式功能正常運作</td>
      </tr>
      <tr>
          <td><strong>檢測方式</strong></td>
          <td>透過測試失敗發現</td>
          <td>透過程式碼檢視或靜態分析發現</td>
      </tr>
      <tr>
          <td><strong>修正優先級</strong></td>
          <td>必須立即修正</td>
          <td>可規劃重構時機</td>
      </tr>
      <tr>
          <td><strong>修正方法</strong></td>
          <td>修正邏輯錯誤</td>
          <td>透過重構改善設計</td>
      </tr>
      <tr>
          <td><strong>長期影響</strong></td>
          <td>直接影響用戶體驗</td>
          <td>影響程式碼可維護性和擴展性</td>
      </tr>
  </tbody>
</table>
<p><strong>範例說明</strong>:</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">// Bug（程式錯誤）
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="kt">void</span> <span class="n">calculateTotal</span><span class="p">(</span><span class="n">List</span><span class="o">&lt;</span><span class="n">Item</span><span class="o">&gt;</span> <span class="n">items</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="kt">double</span> <span class="n">total</span> <span class="o">=</span> <span class="m">0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="n">item</span> <span class="k">in</span> <span class="n">items</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="n">total</span> <span class="o">+=</span> <span class="n">item</span><span class="p">.</span><span class="n">price</span><span class="p">;</span> <span class="c1">// 反例：Bug: 沒有考慮數量
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="k">return</span> <span class="n">total</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1">// Code Smell（Long Method）
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"></span><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">12</span><span class="cl">  <span class="c1">// 80 行的方法
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"></span>  <span class="c1">// 功能正常，但難以理解和維護
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"></span>  <span class="c1">// 這是 Code Smell，不是 Bug
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><hr>
<h4 id="q2-為什麼要從-ticket-粒度檢測-code-smell">Q2: 為什麼要從 Ticket 粒度檢測 Code Smell？</h4>
<p><strong>答</strong>:</p>
<p><strong>Ticket 粒度檢測的優勢</strong>:</p>
<ol>
<li>
<p><strong>及早發現問題</strong>（設計階段 vs 實作階段）</p>
<ul>
<li>設計階段發現 → 修正成本低（只需調整設計）</li>
<li>實作階段發現 → 修正成本中（需要重寫程式碼）</li>
<li>維護階段發現 → 修正成本高（需要大規模重構）</li>
</ul>
</li>
<li>
<p>預防勝於治療</p>
<ul>
<li>Ticket 設計時檢測到 God Ticket → 拆分為多個 Ticket</li>
<li>避免實作後才發現範圍過大</li>
</ul>
</li>
<li>
<p>與 TDD 四階段整合</p>
<ul>
<li>Phase 1 設計：檢測 Ticket 粒度（C1, C3, A1）</li>
<li>Phase 2 測試：檢測測試範圍（C2）</li>
<li>Phase 3 實作：檢測程式碼品質（A2, A3, A4, B2, B3）</li>
<li>Phase 4 重構：識別重構需求（B1, B2, B3, B4）</li>
</ul>
</li>
</ol>
<p><strong>成本對比</strong>:</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">Ticket 粒度檢測（Phase 1）:
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">- 修正成本: 1 小時（調整設計）
</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">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">實作完成後檢測（Phase 3-4）:
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">- 修正成本: 8 小時（重寫程式碼）
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">- 影響範圍: 中（需要修改多個檔案）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- 風險: 中（需要回歸測試）
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">上線後檢測（維護階段）:
</span></span><span class="line"><span class="ln">12</span><span class="cl">- 修正成本: 24 小時（大規模重構）
</span></span><span class="line"><span class="ln">13</span><span class="cl">- 影響範圍: 大（可能影響多個模組）
</span></span><span class="line"><span class="ln">14</span><span class="cl">- 風險: 高（可能引入新 Bug）</span></span></code></pre></div><hr>
<h4 id="q3-所有-code-smell-都必須立即修正嗎">Q3: 所有 Code Smell 都必須立即修正嗎？</h4>
<p><strong>答</strong>: 不是。應該根據<strong>優先級評估公式</strong>決定修正時機。</p>
<p><strong>優先級分類</strong>:</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">優先級分數 = (影響範圍 × 3) + (業務風險 × 2) + (累積速度 × 1)
</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">高優先級（分數 &gt; 20）→ 立即修正
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">- Inappropriate Intimacy（依賴方向錯誤）
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">- Shotgun Surgery（影響範圍大）
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">- God Ticket（風險高）
</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">中優先級（分數 10-20）→ 排入下個版本
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- Feature Envy（耦合度高但不影響功能）
</span></span><span class="line"><span class="ln">10</span><span class="cl">- Divergent Change（技術債務累積）
</span></span><span class="line"><span class="ln">11</span><span class="cl">- Large Class（複雜度高）
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">低優先級（分數 &lt; 10）→ 重構階段處理
</span></span><span class="line"><span class="ln">14</span><span class="cl">- Long Method（可讀性問題）
</span></span><span class="line"><span class="ln">15</span><span class="cl">- Dead Code（無功能影響）
</span></span><span class="line"><span class="ln">16</span><span class="cl">- Incomplete Ticket（補測試即可）</span></span></code></pre></div><p><strong>決策建議</strong>:</p>
<ul>
<li>高優先級：<strong>立即修正</strong>（影響架構或核心功能）</li>
<li>中優先級：<strong>規劃重構</strong>（技術債務累積但不緊急）</li>
<li>低優先級：<strong>opportunistic 重構</strong>（修改相關程式碼時順便重構）</li>
</ul>
<hr>
<h4 id="q4-code-smell-檢測會不會過度限制創意">Q4: Code Smell 檢測會不會過度限制創意？</h4>
<p><strong>答</strong>: 不會。Code Smell 檢查清單是<strong>品質標準</strong>，不是<strong>創意限制</strong>。</p>
<p><strong>澄清誤解</strong>:</p>
<table>
  <thead>
      <tr>
          <th>誤解</th>
          <th>實際情況</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>「檢查清單限制了我的設計」</td>
          <td>檢查清單是<strong>最低標準</strong>，不限制創新設計</td>
      </tr>
      <tr>
          <td>「量化指標太死板」</td>
          <td>量化指標是<strong>參考標準</strong>，特殊情況可調整</td>
      </tr>
      <tr>
          <td>「所有 Code Smell 都要消除」</td>
          <td>根據<strong>優先級評估</strong>決定修正時機</td>
      </tr>
      <tr>
          <td>「重構會降低開發速度」</td>
          <td>及早重構<strong>降低長期維護成本</strong></td>
      </tr>
  </tbody>
</table>
<p><strong>正確理解</strong>:</p>
<ol>
<li>
<p>量化指標是參考，不是絕對</p>
<ul>
<li>方法行數 &gt; 50 行 → 「建議」拆分，不是「強制」</li>
<li>特殊情況（如配置檔載入）可以例外</li>
</ul>
</li>
<li>
<p>檢查清單是輔助，不是束縛</p>
<ul>
<li>幫助發現潛在問題</li>
<li>提供重構方向</li>
<li>不限制創新設計</li>
</ul>
</li>
<li>
<p>重構是投資，不是成本</p>
<ul>
<li>短期投入時間重構</li>
<li>長期降低維護成本</li>
<li>提升團隊生產力</li>
</ul>
</li>
</ol>
<hr>
<h4 id="q5-本檢查清單和層級隔離派工方法論-的關係是什麼">Q5: 本檢查清單和<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 的關係是什麼？</h4>
<p><strong>答</strong>: <strong>互補關係</strong> - <a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 定義「應該怎麼做」，本檢查清單定義「不應該怎麼做」。</p>
<p><strong>關係說明</strong>:</p>
<table>
  <thead>
      <tr>
          <th>方法論</th>
          <th><a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a></th>
          <th>本 Code Smell 檢查清單</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>角色</strong></td>
          <td>正面原則（應該怎麼做）</td>
          <td>負面模式（不應該怎麼做）</td>
      </tr>
      <tr>
          <td><strong>內容</strong></td>
          <td>五層架構定義、單層修改原則、Ticket 粒度標準</td>
          <td>Code Smell 檢測、違規模式識別、重構策略</td>
      </tr>
      <tr>
          <td><strong>使用時機</strong></td>
          <td>設計和規劃階段</td>
          <td>檢測和驗證階段</td>
      </tr>
      <tr>
          <td><strong>產出</strong></td>
          <td>架構設計、Ticket 規劃</td>
          <td>品質檢測報告、重構建議</td>
      </tr>
  </tbody>
</table>
<p><strong>引用關係</strong>:</p>
<ul>
<li>本檢查清單<strong>引用</strong><a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 的定義，不重複定義層級架構</li>
<li>例如：Shotgun Surgery 的判斷標準引用<a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 第 5.2 節 Ticket 粒度標準</li>
</ul>
<p><strong>協作流程</strong>:</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">Phase 1 設計:
</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">1. 使用[層級隔離派工方法論](/record/layered-ticket-methodology/) 設計 Ticket（定義層級、規劃粒度）
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">2. 使用本檢查清單檢測 Ticket（檢查是否有 God Ticket、Ambiguous Responsibility）
</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">Phase 3 實作:
</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">1. 使用[層級隔離派工方法論](/record/layered-ticket-methodology/) 指導實作（遵循單層修改原則）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">2. 使用本檢查清單檢測實作（檢查是否產生 Code Smell）
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">Phase 4 重構:
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">1. 使用本檢查清單識別 Code Smell
</span></span><span class="line"><span class="ln">14</span><span class="cl">2. 使用本檢查清單的重構策略修正
</span></span><span class="line"><span class="ln">15</span><span class="cl">3. 使用[層級隔離派工方法論](/record/layered-ticket-methodology/) 驗證重構後是否符合層級隔離原則</span></span></code></pre></div><hr>
<h3 id="92-實務問題">9.2 實務問題</h3>
<h4 id="q6-如何處理必要的shotgun-surgery">Q6: 如何處理「必要的」Shotgun Surgery？</h4>
<p><strong>答</strong>: 區分<strong>真正的 Shotgun Surgery</strong> 和<strong>合理的跨層修改</strong>。</p>
<p><strong>特殊場景（可能需要跨層修改）</strong>:</p>
<ol>
<li>
<p><strong>架構遷移</strong>（一次性重構）</p>
<ul>
<li>情境：從舊架構遷移到 Clean Architecture</li>
<li>允許：臨時性的大規模修改</li>
<li>要求：完整的測試覆蓋率、詳細的遷移計畫</li>
</ul>
</li>
<li>
<p>Hotfix（緊急修復）</p>
<ul>
<li>情境：生產環境緊急 Bug 修復</li>
<li>允許：臨時性跨層修改</li>
<li>要求：事後必須重構、補充測試</li>
</ul>
</li>
<li>
<p><strong>新增核心欄位</strong>（影響多層的基礎資料）</p>
<ul>
<li>情境：新增影響整個系統的核心欄位</li>
<li>建議：使用 Facade 模式隔離變更</li>
<li>要求：遵循「從內而外」實作順序</li>
</ul>
</li>
</ol>
<p><strong>處理策略</strong>:</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">步驟 1: 評估是否為真正的「必要」跨層修改
</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">  ├─ 是否為 Hotfix？ → 允許（事後重構）
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  └─ 是否可引入 Facade 隔離？ → 建議重新設計
</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">步驟 2: 如果確認「必要」，執行風險控制
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  ├─ 確保測試覆蓋率 100%
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  ├─ 建立詳細的修改計畫
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  ├─ 逐層修改並測試
</span></span><span class="line"><span class="ln">10</span><span class="cl">  └─ 記錄技術債務（Hotfix 情況）
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">步驟 3: 事後處理
</span></span><span class="line"><span class="ln">13</span><span class="cl">  └─ Hotfix → 規劃重構 Ticket 消除技術債務
</span></span><span class="line"><span class="ln">14</span><span class="cl">  └─ 架構遷移 → 完成後驗證架構一致性</span></span></code></pre></div><p><strong>範例說明</strong>:</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></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1">// 反例：直接跨層修改
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1">// - Layer 5: Book Entity 新增 language
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1">// - Layer 3: GetBookDetailUseCase 處理 language
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1">// - Layer 2: Controller 新增 language 屬性
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1">// - Layer 1: UI 顯示 language
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1">// 正例：使用 Facade 隔離變更
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1">// 步驟 1 [Layer 5]: Book Entity 新增 language
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1">// 步驟 2 [Layer 3]: BookDetailFacade 更新（統一處理）
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1">//   - Facade 內部整合新欄位
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1">//   - 對外介面不變或最小變更
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1">// 步驟 3 [Layer 2]: Presenter 更新 ViewModel（只在這裡處理轉換）
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1">// 步驟 4 [Layer 1]: UI 使用 ViewModel（透明變更）
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c1">// 效果：
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="c1">// - 未來新增欄位只需修改 Facade 和 Presenter
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="c1"></span><span class="o">//</span> <span class="o">-</span> <span class="n">Layer</span> <span class="m">1</span> <span class="err">和</span> <span class="n">Layer</span> <span class="m">5</span> <span class="err">的修改影響已隔離</span></span></span></code></pre></div><hr>
<h4 id="q7-large-class-的-300-行標準是否太嚴格">Q7: Large Class 的 300 行標準是否太嚴格？</h4>
<p><strong>答</strong>: 300 行是<strong>建議標準</strong>，不是<strong>絕對限制</strong>。應根據<strong>類別職責</strong>判斷。</p>
<p><strong>彈性標準</strong>:</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><span class="line"><span class="ln"> 2</span><span class="cl">- &lt; 200 行 → 優秀
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">- 200-300 行 → 良好（可接受）
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">- 300-400 行 → 需要注意（考慮拆分）
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">- &gt; 400 行 → 需要拆分
</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">例外情況（可以超過 300 行）:
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">1. 配置類別（如 analysis_options.yaml 定義類別）
</span></span><span class="line"><span class="ln">10</span><span class="cl">2. 自動生成的程式碼（如 *.g.dart）
</span></span><span class="line"><span class="ln">11</span><span class="cl">3. 大型 enum 定義（如包含 50+ 個值）
</span></span><span class="line"><span class="ln">12</span><span class="cl">4. 完整的狀態機實作（如包含所有狀態轉換）
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl">判斷原則：
</span></span><span class="line"><span class="ln">15</span><span class="cl">「類別職責是否可以用一句話清楚描述？」
</span></span><span class="line"><span class="ln">16</span><span class="cl">  ├─ 可以 → 即使超過 300 行也可接受
</span></span><span class="line"><span class="ln">17</span><span class="cl">  └─ 不行 → 即使未超過 300 行也應拆分</span></span></code></pre></div><p><strong>實務建議</strong>:</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">// 範例 1: 可接受的 Large Class
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1">// AppConfig.dart (350 行)
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">AppConfig</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="c1">// 統一管理所有應用程式配置
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span>  <span class="c1">// 職責單一且明確：「應用程式配置管理」
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span>  <span class="c1">// 雖然超過 300 行，但職責清晰 → 可接受
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"></span>  <span class="kd">final</span> <span class="kt">String</span> <span class="n">appName</span> <span class="o">=</span> <span class="s1">&#39;書籍管理&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="kd">final</span> <span class="kt">String</span> <span class="n">apiBaseUrl</span> <span class="o">=</span> <span class="s1">&#39;https://api.example.com&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="c1">// ... 100+ 個配置項
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1">// 範例 2: 需要拆分的類別
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1">// BookService.dart (280 行)
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookService</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">  <span class="c1">// 職責：書籍管理 + 查詢 + 統計 + 報表
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1"></span>  <span class="c1">// 雖然未超過 300 行，但職責不單一 → 應該拆分
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c1"></span>  <span class="kt">void</span> <span class="n">addBook</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">  <span class="kt">void</span> <span class="n">searchBooks</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">  <span class="kt">void</span> <span class="n">getStatistics</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">  <span class="kt">void</span> <span class="n">exportReport</span><span class="p">()</span> <span class="p">{</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="o">//</span> <span class="err">重點：判斷依據是「職責是否單一」，不只是「行數」</span></span></span></code></pre></div><hr>
<h4 id="q8-如何在敏捷開發中平衡速度和程式碼品質">Q8: 如何在敏捷開發中平衡速度和程式碼品質？</h4>
<p><strong>答</strong>: 使用<strong>分階段品質策略</strong> - Phase 1-3 優先速度，Phase 4 確保品質。</p>
<p><strong>分階段策略</strong>:</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">Phase 1 設計（重點：Ticket 粒度）:
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">- 檢測: God Ticket、Ambiguous Responsibility
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">- 目標: 確保 Ticket 範圍合理
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">- 時間投入: 10 分鐘/Ticket
</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">Phase 2 測試設計（重點：測試完整性）:
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">- 檢測: Incomplete Ticket
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">- 目標: 確保有測試設計
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- 時間投入: 30 分鐘/Ticket
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">Phase 3 實作（重點：快速交付）:
</span></span><span class="line"><span class="ln">12</span><span class="cl">- 檢測: 嚴重的 Code Smell（Inappropriate Intimacy、Leaky Abstraction）
</span></span><span class="line"><span class="ln">13</span><span class="cl">- 目標: 快速實作功能，避免嚴重架構問題
</span></span><span class="line"><span class="ln">14</span><span class="cl">- 時間投入: 根據 Ticket 預估工時
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">Phase 4 重構（重點：持續改進）:
</span></span><span class="line"><span class="ln">17</span><span class="cl">- 檢測: 所有 Code Smell
</span></span><span class="line"><span class="ln">18</span><span class="cl">- 目標: 識別技術債務，規劃重構
</span></span><span class="line"><span class="ln">19</span><span class="cl">- 時間投入: 20% 時間用於重構
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl">平衡原則：
</span></span><span class="line"><span class="ln">22</span><span class="cl">「先快速交付功能（Phase 3），再持續改進品質（Phase 4）」</span></span></code></pre></div><p><strong>實務做法</strong>:</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">Sprint 規劃:
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">- 80% 時間: 功能開發（Phase 1-3）
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">- 20% 時間: 技術債務償還（Phase 4 重構）
</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">每個 Sprint:
</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">1. 功能開發（快速交付）
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">   - 確保基本品質（無嚴重 Code Smell）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">   - 允許存在低優先級 Code Smell
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">2. 技術債務償還（持續改進）
</span></span><span class="line"><span class="ln">12</span><span class="cl">   - 根據優先級評估公式選擇重構項目
</span></span><span class="line"><span class="ln">13</span><span class="cl">   - 優先處理高優先級 Code Smell
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">3. 平衡指標
</span></span><span class="line"><span class="ln">16</span><span class="cl">   - 新功能交付速度
</span></span><span class="line"><span class="ln">17</span><span class="cl">   - 技術債務控制在可接受範圍
</span></span><span class="line"><span class="ln">18</span><span class="cl">   - 測試覆蓋率維持 95%+</span></span></code></pre></div><hr>
<h4 id="q9-code-smell-檢測是否會增加-code-review-時間">Q9: Code Smell 檢測是否會增加 Code Review 時間？</h4>
<p><strong>答</strong>: 短期增加 5-10 分鐘，長期<strong>縮短</strong> Code Review 時間 30-40%。</p>
<p><strong>時間分析</strong>:</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">傳統 Code Review（無系統化檢測）:
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">- 審查時間: 30-45 分鐘/PR
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">- 問題發現率: 60%（依賴 Reviewer 經驗）
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">- 往返次數: 平均 2-3 次
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">- 總時間成本: 60-120 分鐘
</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">使用 Code Smell 檢查清單:
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">- 快速檢查: 5 分鐘（使用 6.1 快速檢查清單）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- 深度檢查: 15 分鐘（使用 6.2 深度檢查清單）
</span></span><span class="line"><span class="ln">10</span><span class="cl">- 問題發現率: 90%（系統化檢測）
</span></span><span class="line"><span class="ln">11</span><span class="cl">- 往返次數: 平均 1 次（問題更早發現）
</span></span><span class="line"><span class="ln">12</span><span class="cl">- 總時間成本: 20-30 分鐘
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl">時間節省: 40-90 分鐘/PR（66-75% 改善）</span></span></code></pre></div><p><strong>改善原因</strong>:</p>
<ol>
<li><strong>系統化檢測更快</strong>（不依賴回憶）</li>
<li><strong>問題更早發現</strong>（減少往返次數）</li>
<li><strong>檢測標準統一</strong>（減少討論時間）</li>
</ol>
<p><strong>實測數據</strong>:</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">專案A (10 人團隊，100 個 PR/月):
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">- 導入前: 平均 Code Review 時間 45 分鐘/PR
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">- 導入後: 平均 Code Review 時間 18 分鐘/PR
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">- 改善: 60% 時間節省
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">- 每月節省: 27 * 100 = 2700 分鐘（45 小時）
</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">- Bug 數量: 減少 40%
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- 重構需求: 減少 50%（問題更早發現）
</span></span><span class="line"><span class="ln">10</span><span class="cl">- 團隊滿意度: 提升（減少返工）</span></span></code></pre></div><hr>
<h4 id="q10-團隊成員對-code-smell-標準有不同理解怎麼辦">Q10: 團隊成員對 Code Smell 標準有不同理解怎麼辦？</h4>
<p><strong>答</strong>: 建立<strong>共識機制</strong> - 團隊 Code Smell 討論會 + 案例庫。</p>
<p><strong>共識建立流程</strong>:</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">步驟 1: 初始化階段（第 1-2 週）
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  - 全體成員閱讀本 Code Smell 檢查清單
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  - 舉辦 Code Smell 培訓工作坊（2 小時）
</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">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">步驟 2: 調整階段（第 3-4 週）
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  - 每週 Code Smell 討論會（30 分鐘）
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  - 討論爭議案例
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  - 調整團隊特定標準（如果需要）
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">步驟 3: 穩定階段（第 5 週後）
</span></span><span class="line"><span class="ln">12</span><span class="cl">  - 建立團隊 Code Smell 案例庫
</span></span><span class="line"><span class="ln">13</span><span class="cl">  - 持續更新檢查清單
</span></span><span class="line"><span class="ln">14</span><span class="cl">  - 每月回顧和優化標準</span></span></code></pre></div><p><strong>爭議處理機制</strong>:</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">情境：團隊成員對「Large Class」標準有不同意見
</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">成員 A: 「300 行太嚴格，我們的配置類別都超過 300 行」
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">成員 B: 「300 行是合理標準，配置類別應該拆分」
</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></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">1. 討論會議（30 分鐘）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">   - 展示具體案例
</span></span><span class="line"><span class="ln">10</span><span class="cl">   - 分析職責是否單一
</span></span><span class="line"><span class="ln">11</span><span class="cl">   - 評估拆分成本和收益
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">2. 團隊共識
</span></span><span class="line"><span class="ln">14</span><span class="cl">   - 投票決定團隊標準
</span></span><span class="line"><span class="ln">15</span><span class="cl">   - 記錄決策理由
</span></span><span class="line"><span class="ln">16</span><span class="cl">   - 更新團隊檢查清單
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl">3. 案例記錄
</span></span><span class="line"><span class="ln">19</span><span class="cl">   - 將決策加入團隊案例庫
</span></span><span class="line"><span class="ln">20</span><span class="cl">   - 未來遇到類似情況參考此案例</span></span></code></pre></div><p><strong>團隊案例庫範例</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gh"># 團隊 Code Smell 案例庫
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gh"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">## 案例 #1: AppConfig 類別（350 行）
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gs">**爭議**</span>: 是否屬於 Large Class？
</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 class="gs">**團隊決議**</span>: 可接受
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="gs">**理由**</span>: 職責單一（應用程式配置管理），雖超過 300 行但不拆分
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gs">**標準**</span>: 配置類別可以超過 300 行，但職責必須單一
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">---
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu">## 案例 #2: BookController（280 行，4 種職責）
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="gs">**爭議**</span>: 未超過 300 行，是否需要拆分？
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="gs">**團隊決議**</span>: 需要拆分
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="gs">**理由**</span>: 雖未超過 300 行，但有 4 種職責（Divergent Change）
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="gs">**標準**</span>: 判斷依據是「職責是否單一」，不只是「行數」</span></span></code></pre></div><hr>
<h3 id="93-工具問題">9.3 工具問題</h3>
<h4 id="q11-如何自動化檢測-code-smell">Q11: 如何自動化檢測 Code Smell？</h4>
<p><strong>答</strong>: 整合靜態分析工具 + Hook 系統 + CI/CD pipeline。</p>
<p><strong>自動化檢測架構</strong>:</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">Level 1: 本地開發（即時反饋）
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  └─ PostEdit Hook → 程式碼修改後立即檢測
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">     ├─ dart analyze（Dead Code、unused 警告）
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">     ├─ 檔案行數檢查（Large Class、Long Method）
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">     └─ import 語句分析（Feature Envy、Inappropriate Intimacy）
</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">Level 2: 提交前（全面檢測）
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  └─ Pre-Commit Hook → git commit 前檢測
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">     ├─ 執行所有 Level 1 檢測
</span></span><span class="line"><span class="ln">10</span><span class="cl">     ├─ 測試覆蓋率檢查（Incomplete Ticket）
</span></span><span class="line"><span class="ln">11</span><span class="cl">     └─ Code Smell 優先級評估
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">Level 3: PR 階段（完整報告）
</span></span><span class="line"><span class="ln">14</span><span class="cl">  └─ GitHub Actions → PR 提交時檢測
</span></span><span class="line"><span class="ln">15</span><span class="cl">     ├─ 執行所有 Level 1-2 檢測
</span></span><span class="line"><span class="ln">16</span><span class="cl">     ├─ 生成 Code Smell 檢測報告
</span></span><span class="line"><span class="ln">17</span><span class="cl">     └─ 高優先級問題阻止合併</span></span></code></pre></div><p><strong>工具整合範例</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># .claude/hooks/post-edit.sh</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1">#!/bin/bash</span>
</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 class="c1"># Level 1: 即時檢測</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;執行 Code Smell 即時檢測...&#34;</span>
</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 class="c1"># 1. dart analyze</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">dart analyze --fatal-infos 2&gt;<span class="p">&amp;</span><span class="m">1</span> <span class="p">|</span> grep <span class="s2">&#34;unused&#34;</span> <span class="o">&amp;&amp;</span> <span class="o">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;⚠️ 檢測到 Dead Code (unused 警告)&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"># 2. 檔案行數檢查</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">for</span> file in <span class="k">$(</span>git diff --name-only --staged<span class="k">)</span><span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">  <span class="k">if</span> <span class="o">[[</span> <span class="nv">$file</span> <span class="o">==</span> *.dart <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="nv">lines</span><span class="o">=</span><span class="k">$(</span>wc -l &lt; <span class="s2">&#34;</span><span class="nv">$file</span><span class="s2">&#34;</span><span class="k">)</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="k">if</span> <span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$lines</span><span class="s2">&#34;</span> -gt <span class="m">300</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">      <span class="nb">echo</span> <span class="s2">&#34;⚠️ Large Class: </span><span class="nv">$file</span><span class="s2"> (</span><span class="nv">$lines</span><span class="s2"> 行)&#34;</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">  <span class="k">fi</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">done</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="c1"># 3. import 語句分析</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="k">for</span> file in <span class="k">$(</span>git diff --name-only --staged<span class="k">)</span><span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">  <span class="k">if</span> <span class="o">[[</span> <span class="nv">$file</span> <span class="o">==</span> lib/presentation/* <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">    <span class="k">if</span> grep -q <span class="s2">&#34;import.*domains/.*/entities&#34;</span> <span class="s2">&#34;</span><span class="nv">$file</span><span class="s2">&#34;</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">      <span class="nb">echo</span> <span class="s2">&#34;⚠️ Feature Envy: UI 直接 import Domain Entity&#34;</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">  <span class="k">fi</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="k">done</span></span></span></code></pre></div><p><strong>CI/CD 整合範例</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># .github/workflows/code-smell.yml</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Code Smell 檢測</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"></span><span class="nt">on</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">pull_request]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w"></span><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">  </span><span class="nt">code-smell</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v3</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Code Smell 檢測</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="sd">          # 執行完整檢測
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="sd">          bash .claude/scripts/code-smell-check.sh
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="sd">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="sd">          # 生成報告
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="sd">          python .claude/scripts/generate-report.py</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">檢查優先級</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="sd">          # 高優先級問題 → 阻止合併
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="sd">          python .claude/scripts/check-priority.py --fail-on-high</span></span></span></code></pre></div><hr>
<h4 id="q12-dart_code_metrics-和本檢查清單的關係">Q12: dart_code_metrics 和本檢查清單的關係？</h4>
<p><strong>答</strong>: <strong>互補關係</strong> - dart_code_metrics 提供量化指標，本檢查清單提供架構檢測。</p>
<p><strong>工具定位</strong>:</p>
<table>
  <thead>
      <tr>
          <th>工具</th>
          <th>dart_code_metrics</th>
          <th>Code Smell 檢查清單（本文件）</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>檢測範圍</strong></td>
          <td>程式碼複雜度、重複度</td>
          <td>架構設計、層級隔離</td>
      </tr>
      <tr>
          <td><strong>檢測對象</strong></td>
          <td>單一檔案、方法</td>
          <td>跨檔案、跨層級</td>
      </tr>
      <tr>
          <td><strong>量化指標</strong></td>
          <td>循環複雜度、認知複雜度</td>
          <td>檔案數、層級跨度</td>
      </tr>
      <tr>
          <td><strong>適用場景</strong></td>
          <td>Phase 3 實作、Phase 4 重構</td>
          <td>Phase 1 設計、Code Review</td>
      </tr>
  </tbody>
</table>
<p><strong>整合使用</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># analysis_options.yaml</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="nt">dart_code_metrics</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">  </span><span class="nt">metrics</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span><span class="c"># B3. Long Method 檢測</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="nt">cyclomatic-complexity</span><span class="p">:</span><span class="w"> </span><span class="m">20</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="nt">lines-of-code</span><span class="p">:</span><span class="w"> </span><span class="m">50</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="nt">maximum-nesting-level</span><span class="p">:</span><span class="w"> </span><span class="m">3</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="c"># B2. Large Class 檢測</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="nt">number-of-methods</span><span class="p">:</span><span class="w"> </span><span class="m">15</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span><span class="nt">weight-of-class</span><span class="p">:</span><span class="w"> </span><span class="m">0.33</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">  </span><span class="nt">rules</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">    </span><span class="c"># B4. Dead Code 檢測</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">    </span>- <span class="l">avoid-unused-parameters</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">    </span><span class="c"># B1. Divergent Change 檢測</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">    </span>- <span class="l">prefer-single-widget-per-file</span></span></span></code></pre></div><p><strong>協作流程</strong>:</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">步驟 1: dart_code_metrics 檢測程式碼複雜度
</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">步驟 2: 本檢查清單檢測架構問題
</span></span><span class="line"><span class="ln">5</span><span class="cl">  └─ 輸出: 層級跨度、依賴方向、Ticket 粒度
</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">步驟 3: 整合報告
</span></span><span class="line"><span class="ln">8</span><span class="cl">  └─ 結合兩者結果，提供完整的 Code Smell 檢測報告</span></span></code></pre></div><hr>
<h4 id="q13-如何處理自動生成的程式碼如-gdart">Q13: 如何處理自動生成的程式碼（如 *.g.dart）？</h4>
<p><strong>答</strong>: 在檢測配置中<strong>排除</strong>自動生成的程式碼。</p>
<p><strong>排除配置</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># analysis_options.yaml</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="nt">analyzer</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">  </span><span class="nt">exclude</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span><span class="c"># 排除自動生成的程式碼</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span>- <span class="s2">&#34;**/*.g.dart&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span>- <span class="s2">&#34;**/*.freezed.dart&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span>- <span class="s2">&#34;**/generated/**&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span>- <span class="s2">&#34;build/**&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="c"># 排除第三方程式碼</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span>- <span class="s2">&#34;lib/generated_plugin_registrant.dart&#34;</span></span></span></code></pre></div><p><strong>Hook 系統排除</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># .claude/hooks/code-smell-check.sh</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1">#!/bin/bash</span>
</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 class="c1"># 排除自動生成的檔案</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">for</span> file in <span class="k">$(</span>git diff --name-only --staged<span class="k">)</span><span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  <span class="c1"># 跳過 *.g.dart</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="k">if</span> <span class="o">[[</span> <span class="nv">$file</span> <span class="o">==</span> *.g.dart <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="k">continue</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="k">fi</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">  <span class="c1"># 跳過 *.freezed.dart</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="k">if</span> <span class="o">[[</span> <span class="nv">$file</span> <span class="o">==</span> *.freezed.dart <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="k">continue</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">  <span class="k">fi</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="c1"># 執行檢測</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">  check_code_smell <span class="s2">&#34;</span><span class="nv">$file</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">done</span></span></span></code></pre></div><p><strong>原則</strong>:</p>
<ul>
<li>檢測：手寫程式碼</li>
<li>不檢測：自動生成的程式碼（*.g.dart, *.freezed.dart）</li>
<li>不檢測：第三方程式碼（dependencies）</li>
<li>不檢測：測試 Mock 程式碼（*.mocks.dart）</li>
</ul>
<hr>
<h4 id="q14-如何在-vs-code-中整合-code-smell-檢測">Q14: 如何在 VS Code 中整合 Code Smell 檢測？</h4>
<p><strong>答</strong>: 使用 <strong>VS Code 擴充功能</strong> + <strong>Tasks</strong> + <strong>Problem Matchers</strong>。</p>
<p><strong>設定檔配置</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// .vscode/tasks.json
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="s2">&#34;2.0.0&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="nt">&#34;tasks&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">      <span class="nt">&#34;label&#34;</span><span class="p">:</span> <span class="s2">&#34;Code Smell 檢測&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">      <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;shell&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">      <span class="nt">&#34;command&#34;</span><span class="p">:</span> <span class="s2">&#34;bash .claude/scripts/code-smell-check.sh&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">      <span class="nt">&#34;problemMatcher&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">        <span class="nt">&#34;owner&#34;</span><span class="p">:</span> <span class="s2">&#34;code-smell&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">        <span class="nt">&#34;fileLocation&#34;</span><span class="p">:</span> <span class="s2">&#34;relative&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">        <span class="nt">&#34;pattern&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">          <span class="nt">&#34;regexp&#34;</span><span class="p">:</span> <span class="s2">&#34;^(⚠️|❌)\\s+(\\w+):\\s+(.+)\\s+\\((.+):(\\d+)\\)$&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">          <span class="nt">&#34;severity&#34;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">          <span class="nt">&#34;code&#34;</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">          <span class="nt">&#34;message&#34;</span><span class="p">:</span> <span class="mi">3</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">          <span class="nt">&#34;file&#34;</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">          <span class="nt">&#34;line&#34;</span><span class="p">:</span> <span class="mi">5</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">      <span class="nt">&#34;group&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">        <span class="nt">&#34;kind&#34;</span><span class="p">:</span> <span class="s2">&#34;test&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">        <span class="nt">&#34;isDefault&#34;</span><span class="p">:</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">  <span class="p">]</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p><strong>快捷鍵設定</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">// .vscode/keybindings.json
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span><span class="p">[</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">    <span class="nt">&#34;key&#34;</span><span class="p">:</span> <span class="s2">&#34;ctrl+shift+s&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">    <span class="nt">&#34;command&#34;</span><span class="p">:</span> <span class="s2">&#34;workbench.action.tasks.runTask&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">    <span class="nt">&#34;args&#34;</span><span class="p">:</span> <span class="s2">&#34;Code Smell 檢測&#34;</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="p">]</span></span></span></code></pre></div><p><strong>使用方式</strong>:</p>
<ol>
<li>按 <code>Ctrl+Shift+S</code> 執行 Code Smell 檢測</li>
<li>問題面板顯示檢測結果</li>
<li>點擊問題項目跳轉到對應程式碼</li>
</ol>
<hr>
<h4 id="q15-測試覆蓋率工具與-code-smell-檢測的關係">Q15: 測試覆蓋率工具與 Code Smell 檢測的關係？</h4>
<p><strong>答</strong>: 測試覆蓋率工具檢測<strong>測試完整性</strong>，輔助識別 <strong>Dead Code</strong> 和 <strong>Incomplete Ticket</strong>。</p>
<p><strong>工具整合</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 1. 執行測試並生成覆蓋率報告</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">flutter <span class="nb">test</span> --coverage
</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 class="c1"># 2. 分析覆蓋率報告</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"># a. 0% 覆蓋率 → 可能是 Dead Code</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">lcov --summary coverage/lcov.info <span class="p">|</span> grep <span class="s2">&#34;0.0%&#34;</span>
</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"><span class="c1"># b. 新增程式碼無測試 → Incomplete Ticket</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">git diff main --name-only <span class="p">|</span> <span class="k">while</span> <span class="nb">read</span> file<span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="k">if</span> <span class="o">[[</span> <span class="nv">$file</span> <span class="o">==</span> lib/*.dart <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="nv">test_file</span><span class="o">=</span><span class="s2">&#34;test/</span><span class="si">${</span><span class="nv">file</span><span class="p">#lib/</span><span class="si">}</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="nv">test_file</span><span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">test_file</span><span class="p">%.dart</span><span class="si">}</span><span class="s2">_test.dart&#34;</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="k">if</span> <span class="o">[</span> ! -f <span class="s2">&#34;</span><span class="nv">$test_file</span><span class="s2">&#34;</span> <span class="o">]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">      <span class="nb">echo</span> <span class="s2">&#34;⚠️ Incomplete Ticket: </span><span class="nv">$file</span><span class="s2"> 缺少測試檔案&#34;</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="k">fi</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">done</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="c1"># 3. 生成 HTML 報告</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">genhtml coverage/lcov.info -o coverage/html</span></span></code></pre></div><p><strong>Dead Code 檢測流程</strong>:</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">步驟 1: 執行測試覆蓋率分析
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  └─ flutter test --coverage
</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">步驟 2: 識別 0% 覆蓋率程式碼
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  └─ 可能是 Dead Code 或缺少測試
</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">步驟 3: 交叉驗證
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  ├─ dart analyze 有 unused 警告？ → Dead Code
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  └─ dart analyze 無警告？ → 缺少測試
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">步驟 4: 採取行動
</span></span><span class="line"><span class="ln">12</span><span class="cl">  ├─ Dead Code → 刪除
</span></span><span class="line"><span class="ln">13</span><span class="cl">  └─ 缺少測試 → 補充測試</span></span></code></pre></div><p><strong>Incomplete Ticket 檢測流程</strong>:</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">步驟 1: 檢查程式碼檔案是否有對應測試
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  └─ lib/foo.dart → test/foo_test.dart 是否存在？
</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">步驟 2: 檢查測試覆蓋率
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  └─ 新增程式碼覆蓋率是否達到 100%？
</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">步驟 3: 判斷
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  ├─ 無測試檔案 → Incomplete Ticket
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  ├─ 覆蓋率 &lt; 100% → Incomplete Ticket
</span></span><span class="line"><span class="ln">10</span><span class="cl">  └─ 覆蓋率 = 100% → 完整 Ticket</span></span></code></pre></div><hr>
<h2 id="第十章參考資料">第十章：參考資料</h2>
<h3 id="101-引用的方法論">10.1 引用的方法論</h3>
<p>本 Code Smell 檢查清單基於以下方法論建立：</p>
<h4 id="層級隔離派工方法論"><a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a></h4>
<p><strong>檔案位置</strong>: <code>.claude/methodologies/layered-ticket-methodology.md</code></p>
<p><strong>引用章節</strong>:</p>
<ul>
<li>
<p><strong>2.2 節</strong>: Clean Architecture 五層完整定義</p>
<ul>
<li>Layer 1 (UI): 視覺呈現職責</li>
<li>Layer 2 (Behavior): 事件處理和資料轉換職責</li>
<li>Layer 3 (UseCase): 業務流程協調職責</li>
<li>Layer 4 (Domain Interface): 介面契約職責</li>
<li>Layer 5 (Domain): 業務規則和不可變邏輯職責</li>
</ul>
</li>
<li>
<p><strong>2.3 節</strong>: 依賴方向規則</p>
<ul>
<li>正確依賴方向：Layer 1 → Layer 2 → Layer 3 → Layer 4 ← Layer 5</li>
</ul>
</li>
<li>
<p><strong>2.4 節</strong>: 層級定位決策樹</p>
<ul>
<li>檔案路徑分析法判斷層級歸屬</li>
</ul>
</li>
<li>
<p><strong>3.1 節</strong>: 單層修改原則定義</p>
<ul>
<li>單一 Ticket 應該只修改單一架構層級</li>
</ul>
</li>
<li>
<p><strong>3.2 節</strong>: SRP 理論依據</p>
<ul>
<li>Single Responsibility Principle 應用</li>
</ul>
</li>
<li>
<p><strong>5.2 節</strong>: Ticket 粒度量化指標</p>
<ul>
<li>良好 Ticket：1-5 個檔案，1 層，2-8 小時</li>
<li>God Ticket：&gt; 10 個檔案，&gt; 2 層，&gt; 16 小時</li>
</ul>
</li>
<li>
<p><strong>5.4 節</strong>: Ticket 拆分指引</p>
<ul>
<li>按層級拆分、按 Domain 拆分、按功能拆分</li>
</ul>
</li>
<li>
<p><strong>6.2 節</strong>: 檔案路徑分析法</p>
<ul>
<li>從檔案路徑判斷層級歸屬</li>
</ul>
</li>
<li>
<p><strong>6.4 節</strong>: 測試層級對應原則</p>
<ul>
<li>測試檔案路徑對應層級結構</li>
</ul>
</li>
<li>
<p><strong>6.5 節</strong>: 違規模式識別</p>
<ul>
<li>常見的層級違規模式</li>
</ul>
</li>
</ul>
<p><strong>關係說明</strong>:</p>
<ul>
<li><a href="/blog/record/%E5%B1%A4%E7%B4%9A%E9%9A%94%E9%9B%A2%E8%AE%93%E6%AF%8F%E5%BC%B5-ticket-%E5%8F%AA%E5%81%9A%E4%B8%80%E4%BB%B6%E5%B1%A4%E7%B4%9A%E7%9A%84%E4%BA%8B/" data-link-title="層級隔離：讓每張 Ticket 只做一件層級的事" data-link-desc="我們在實際開發中整理出一套方法論，讓 Clean Architecture 五層架構與 Ticket 拆分真正結合——每張 Ticket 只修改一個架構層，不多也不少。">層級隔離派工方法論</a> 定義「應該怎麼做」（正面原則）</li>
<li>本檢查清單定義「不應該怎麼做」（負面模式識別）</li>
<li>兩者互補，共同建構完整的品質標準體系</li>
</ul>
<hr>
<h3 id="102-code-smell-理論文獻">10.2 Code Smell 理論文獻</h3>
<h4 id="martin-fowler---refactoring-improving-the-design-of-existing-code">Martin Fowler - Refactoring: Improving the Design of Existing Code</h4>
<p><strong>重要概念</strong>:</p>
<ul>
<li>Code Smell 定義和分類</li>
<li>重構模式目錄</li>
<li>Extract Method、Extract Class、Move Method 等重構技巧</li>
</ul>
<p><strong>本檢查清單應用</strong>:</p>
<ul>
<li>第四章重構模式對應表引用 Fowler 的重構模式</li>
<li>重構步驟設計參考 Fowler 的重構技巧</li>
</ul>
<p><strong>延伸閱讀</strong>: <a href="https://refactoring.com/">refactoring.com</a></p>
<hr>
<h4 id="robert-c-martin---clean-code">Robert C. Martin - Clean Code</h4>
<p><strong>重要概念</strong>:</p>
<ul>
<li>有意義的命名</li>
<li>函式應該短小</li>
<li>單一職責原則（SRP）</li>
<li>依賴倒置原則（DIP）</li>
</ul>
<p><strong>本檢查清單應用</strong>:</p>
<ul>
<li>Long Method 判斷標準（&lt; 50 行）</li>
<li>Divergent Change 檢測（SRP 違反）</li>
<li>Inappropriate Intimacy 檢測（DIP 違反）</li>
</ul>
<hr>
<h4 id="robert-c-martin---clean-architecture">Robert C. Martin - Clean Architecture</h4>
<p><strong>重要概念</strong>:</p>
<ul>
<li>分層架構設計</li>
<li>依賴規則（Dependency Rule）</li>
<li>介面隔離原則</li>
</ul>
<p><strong>本檢查清單應用</strong>:</p>
<ul>
<li>A 類 Code Smell 分類（跨層級問題）</li>
<li>Inappropriate Intimacy 檢測（依賴方向錯誤）</li>
<li>Leaky Abstraction 檢測（介面設計問題）</li>
</ul>
<hr>
<h3 id="103-重構模式參考">10.3 重構模式參考</h3>
<h4 id="extract-interface提取介面">Extract Interface（提取介面）</h4>
<p><strong>用途</strong>: 修正 Leaky Abstraction</p>
<p><strong>重構步驟</strong>:</p>
<ol>
<li>分析具體類別的公開方法</li>
<li>建立介面定義</li>
<li>提取抽象方法簽名</li>
<li>讓具體類別實作介面</li>
<li>更新依賴為使用介面</li>
</ol>
<p><strong>參考</strong>: Fowler, Refactoring (1999), p.341</p>
<hr>
<h4 id="extract-method提取方法">Extract Method（提取方法）</h4>
<p><strong>用途</strong>: 修正 Long Method</p>
<p><strong>重構步驟</strong>:</p>
<ol>
<li>識別邏輯區塊</li>
<li>為區塊建立新方法</li>
<li>傳遞必要參數</li>
<li>回傳必要值</li>
<li>替換原區塊為方法呼叫</li>
</ol>
<p><strong>參考</strong>: Fowler, Refactoring (1999), p.110</p>
<hr>
<h4 id="extract-class提取類別">Extract Class（提取類別）</h4>
<p><strong>用途</strong>: 修正 Large Class、Divergent Change</p>
<p><strong>重構步驟</strong>:</p>
<ol>
<li>分析方法分組</li>
<li>建立新類別</li>
<li>移動相關欄位和方法</li>
<li>建立委派方法（如需要）</li>
<li>更新依賴關係</li>
</ol>
<p><strong>參考</strong>: Fowler, Refactoring (1999), p.149</p>
<hr>
<h4 id="move-method移動方法">Move Method（移動方法）</h4>
<p><strong>用途</strong>: 修正 Feature Envy</p>
<p><strong>重構步驟</strong>:</p>
<ol>
<li>識別方法應該屬於哪個類別</li>
<li>在目標類別建立方法</li>
<li>調整參數和回傳值</li>
<li>移除原方法或建立委派</li>
<li>更新呼叫端</li>
</ol>
<p><strong>參考</strong>: Fowler, Refactoring (1999), p.142</p>
<hr>
<h4 id="introduce-facade引入外觀">Introduce Facade（引入外觀）</h4>
<p><strong>用途</strong>: 修正 Shotgun Surgery</p>
<p><strong>重構步驟</strong>:</p>
<ol>
<li>分析跨層操作的共同點</li>
<li>建立 Facade 介面</li>
<li>實作 Facade 封裝跨層操作</li>
<li>更新呼叫端使用 Facade</li>
<li>驗證未來變更只需修改 Facade</li>
</ol>
<p><strong>參考</strong>: Gang of Four, Design Patterns (1994), p.185</p>
]]></content:encoded></item><item><title>Ticket 設計派工方法論</title><link>https://tarrragon.github.io/blog/record/ticket-%E8%A8%AD%E8%A8%88%E6%B4%BE%E5%B7%A5%E6%96%B9%E6%B3%95%E8%AB%96/</link><pubDate>Sat, 11 Oct 2025 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/ticket-%E8%A8%AD%E8%A8%88%E6%B4%BE%E5%B7%A5%E6%96%B9%E6%B3%95%E8%AB%96/</guid><description>&lt;h2 id="方法論概述">方法論概述&lt;/h2>
&lt;p>這裡嘗試以 Ticket 設計和派工機制，解決大型開發任務的協作效率問題，特別是工作日誌臃腫和實作偏差風險。&lt;/p>
&lt;p>但是這篇方法論太長了，之後會分拆成多個方法論方便閱讀&lt;/p>
&lt;p>&lt;strong>適用場景&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>多人協作開發&lt;/li>
&lt;li>大型功能模組開發&lt;/li>
&lt;li>需要精細進度追蹤的專案&lt;/li>
&lt;li>需要即時 review 機制的團隊&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>核心目標&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>建立量化的 Ticket 拆分標準&lt;/li>
&lt;li>定義完整的 Ticket 生命週期&lt;/li>
&lt;li>建立即時 Review 機制&lt;/li>
&lt;li>避免工作日誌臃腫&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>方法論版本&lt;/strong>：v1.0.0&lt;/p>
&lt;hr>
&lt;h2 id="第一章ticket-機制核心原則">第一章：Ticket 機制核心原則&lt;/h2>
&lt;h3 id="11-ticket-vs-工作日誌的定位差異">1.1 Ticket vs 工作日誌的定位差異&lt;/h3>
&lt;p>&lt;strong>Ticket 定義&lt;/strong>：&lt;/p>
&lt;p>Ticket 是「最小可交付單元」（Minimal Deliverable Unit），代表一個可以獨立完成、驗收和追蹤的最小任務單位。&lt;/p>
&lt;p>&lt;strong>核心特徵&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>獨立性&lt;/strong>：可以獨立執行和驗收&lt;/li>
&lt;li>&lt;strong>原子性&lt;/strong>：不可再分割為更小的可交付單元&lt;/li>
&lt;li>&lt;strong>可驗證性&lt;/strong>：有明確的完成標準&lt;/li>
&lt;li>&lt;strong>複雜度限制&lt;/strong>：基於職責、檔案、測試、行數的量化標準&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>工作日誌定義&lt;/strong>：&lt;/p>
&lt;p>工作日誌是記錄整個開發過程的完整文檔，包含設計決策、實作細節、問題分析和解決方案。&lt;/p>
&lt;p>&lt;strong>核心特徵&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>完整性&lt;/strong>：記錄所有決策和過程&lt;/li>
&lt;li>&lt;strong>追溯性&lt;/strong>：提供歷史記錄和演進軌跡&lt;/li>
&lt;li>&lt;strong>知識傳承&lt;/strong>：為後續開發者提供上下文&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>定位差異總結&lt;/strong>：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>維度&lt;/th>
 &lt;th>Ticket&lt;/th>
 &lt;th>工作日誌&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;strong>範圍&lt;/strong>&lt;/td>
 &lt;td>單一具體任務&lt;/td>
 &lt;td>整個功能模組或版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>粒度&lt;/strong>&lt;/td>
 &lt;td>最小可交付單元（單一職責或少數相關職責）&lt;/td>
 &lt;td>完整開發週期（數天到數週）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>目的&lt;/strong>&lt;/td>
 &lt;td>任務執行和驗收&lt;/td>
 &lt;td>知識記錄和傳承&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>更新頻率&lt;/strong>&lt;/td>
 &lt;td>執行中持續更新&lt;/td>
 &lt;td>階段性更新&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>文件大小&lt;/strong>&lt;/td>
 &lt;td>100-200 行&lt;/td>
 &lt;td>500-6000 行&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>適用場景&lt;/strong>&lt;/td>
 &lt;td>協作開發、進度追蹤&lt;/td>
 &lt;td>決策記錄、技術傳承&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>互補關係&lt;/strong>：&lt;/p>
&lt;p>Ticket 和工作日誌是互補關係：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Ticket&lt;/strong> 負責「執行層面」：將大任務拆分為可管理的小單元&lt;/li>
&lt;li>&lt;strong>工作日誌&lt;/strong> 負責「記錄層面」：記錄決策過程和演進軌跡&lt;/li>
&lt;li>&lt;strong>主版本日誌&lt;/strong> 負責「總覽層面」：提供任務總覽和 Ticket 索引&lt;/li>
&lt;/ul>
&lt;h3 id="12-ticket-機制的三大目標">1.2 Ticket 機制的三大目標&lt;/h3>
&lt;h4 id="目標-1可追溯性traceability">目標 1：可追溯性（Traceability）&lt;/h4>
&lt;p>&lt;strong>定義&lt;/strong>：每個 Ticket 都有明確的來源和目標，可以追溯到需求、設計文件或問題報告。&lt;/p>
&lt;p>&lt;strong>實現方式&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>Ticket 必須包含「參考文件」欄位，連結到需求規格或設計文件&lt;/li>
&lt;li>Ticket 必須包含「背景」欄位，說明為什麼需要這個 Ticket&lt;/li>
&lt;li>主版本日誌維護完整的 Ticket 索引&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>範例&lt;/strong>：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="gu">### 參考文件
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>&lt;span class="k">-&lt;/span> v0.12.7-design-decisions.md &lt;span class="ni">#決策1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="k">-&lt;/span> docs/app-requirements-spec.md &lt;span class="ni">#UC&lt;/span>-01
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="gu">### 背景
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>根據 UC-01 需求，需要建立書籍資訊豐富化服務的介面契約&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>效益&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>開發者清楚知道「為什麼」要做這個任務&lt;/li>
&lt;li>PM 可以追蹤每個 Ticket 的需求來源&lt;/li>
&lt;li>後續維護者可以理解設計意圖&lt;/li>
&lt;/ul>
&lt;h4 id="目標-2可驗收性verifiability">目標 2：可驗收性（Verifiability）&lt;/h4>
&lt;p>&lt;strong>定義&lt;/strong>：每個 Ticket 都有明確、可驗證的完成標準，避免主觀判斷。&lt;/p>
&lt;p>&lt;strong>實現方式&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>Ticket 必須包含「驗收條件」欄位，列出所有可驗證的條件&lt;/li>
&lt;li>驗收條件必須是客觀可檢查的（檔案存在、測試通過、功能運作）&lt;/li>
&lt;li>Review 時逐項檢查驗收條件&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>範例&lt;/strong>：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="gu">### 驗收條件
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>&lt;span class="k">- [ ]&lt;/span> 介面檔案建立在 &lt;span class="sb">`lib/domains/import/services/`&lt;/span> 目錄
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="k">- [ ]&lt;/span> &lt;span class="sb">`enrichBook`&lt;/span> 方法簽名完整且明確
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="k">- [ ]&lt;/span> 輸入輸出類型定義清楚
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="k">- [ ]&lt;/span> 包含完整的文檔註解
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">- [ ] dart analyze 0 錯誤&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>效益&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>避免「看起來完成了」但實際未達標準&lt;/li>
&lt;li>提供明確的驗收依據&lt;/li>
&lt;li>減少 review 時的主觀爭議&lt;/li>
&lt;/ul>
&lt;h4 id="目標-3可協作性collaborability">目標 3：可協作性（Collaborability）&lt;/h4>
&lt;p>&lt;strong>定義&lt;/strong>：多個開發者可以並行執行不同的 Ticket，互不阻塞。&lt;/p>
&lt;p>&lt;strong>實現方式&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>Ticket 拆分時考慮依賴關係，最小化依賴&lt;/li>
&lt;li>明確標註 Ticket 間的依賴關係（必須先完成 / 可並行）&lt;/li>
&lt;li>使用 Interface-Driven 開發，內層未完成也可開發外層&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>範例&lt;/strong>：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="gu">### 依賴 Ticket
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="gu">&lt;/span>&lt;span class="k">-&lt;/span> Ticket &lt;span class="ni">#1:&lt;/span> 定義 IBookInfoEnrichmentService 介面（必須先完成）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">- Ticket &lt;span class="ni">#3:&lt;/span> 撰寫 EnrichmentProgress 測試（可並行）&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>效益&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>多人可同時開發不同模組&lt;/li>
&lt;li>減少等待時間&lt;/li>
&lt;li>提升開發效率&lt;/li>
&lt;/ul>
&lt;h3 id="13-基於-clean-architecture-的-ticket-設計哲學">1.3 基於 Clean Architecture 的 Ticket 設計哲學&lt;/h3>
&lt;h4 id="clean-architecture-核心原則回顧">Clean Architecture 核心原則回顧&lt;/h4>
&lt;p>&lt;strong>依賴方向規則&lt;/strong>：&lt;/p></description><content:encoded><![CDATA[<h2 id="方法論概述">方法論概述</h2>
<p>這裡嘗試以 Ticket 設計和派工機制，解決大型開發任務的協作效率問題，特別是工作日誌臃腫和實作偏差風險。</p>
<p>但是這篇方法論太長了，之後會分拆成多個方法論方便閱讀</p>
<p><strong>適用場景</strong>：</p>
<ul>
<li>多人協作開發</li>
<li>大型功能模組開發</li>
<li>需要精細進度追蹤的專案</li>
<li>需要即時 review 機制的團隊</li>
</ul>
<p><strong>核心目標</strong>：</p>
<ul>
<li>建立量化的 Ticket 拆分標準</li>
<li>定義完整的 Ticket 生命週期</li>
<li>建立即時 Review 機制</li>
<li>避免工作日誌臃腫</li>
</ul>
<p><strong>方法論版本</strong>：v1.0.0</p>
<hr>
<h2 id="第一章ticket-機制核心原則">第一章：Ticket 機制核心原則</h2>
<h3 id="11-ticket-vs-工作日誌的定位差異">1.1 Ticket vs 工作日誌的定位差異</h3>
<p><strong>Ticket 定義</strong>：</p>
<p>Ticket 是「最小可交付單元」（Minimal Deliverable Unit），代表一個可以獨立完成、驗收和追蹤的最小任務單位。</p>
<p><strong>核心特徵</strong>：</p>
<ul>
<li><strong>獨立性</strong>：可以獨立執行和驗收</li>
<li><strong>原子性</strong>：不可再分割為更小的可交付單元</li>
<li><strong>可驗證性</strong>：有明確的完成標準</li>
<li><strong>複雜度限制</strong>：基於職責、檔案、測試、行數的量化標準</li>
</ul>
<p><strong>工作日誌定義</strong>：</p>
<p>工作日誌是記錄整個開發過程的完整文檔，包含設計決策、實作細節、問題分析和解決方案。</p>
<p><strong>核心特徵</strong>：</p>
<ul>
<li><strong>完整性</strong>：記錄所有決策和過程</li>
<li><strong>追溯性</strong>：提供歷史記錄和演進軌跡</li>
<li><strong>知識傳承</strong>：為後續開發者提供上下文</li>
</ul>
<p><strong>定位差異總結</strong>：</p>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>Ticket</th>
          <th>工作日誌</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>範圍</strong></td>
          <td>單一具體任務</td>
          <td>整個功能模組或版本</td>
      </tr>
      <tr>
          <td><strong>粒度</strong></td>
          <td>最小可交付單元（單一職責或少數相關職責）</td>
          <td>完整開發週期（數天到數週）</td>
      </tr>
      <tr>
          <td><strong>目的</strong></td>
          <td>任務執行和驗收</td>
          <td>知識記錄和傳承</td>
      </tr>
      <tr>
          <td><strong>更新頻率</strong></td>
          <td>執行中持續更新</td>
          <td>階段性更新</td>
      </tr>
      <tr>
          <td><strong>文件大小</strong></td>
          <td>100-200 行</td>
          <td>500-6000 行</td>
      </tr>
      <tr>
          <td><strong>適用場景</strong></td>
          <td>協作開發、進度追蹤</td>
          <td>決策記錄、技術傳承</td>
      </tr>
  </tbody>
</table>
<p><strong>互補關係</strong>：</p>
<p>Ticket 和工作日誌是互補關係：</p>
<ul>
<li><strong>Ticket</strong> 負責「執行層面」：將大任務拆分為可管理的小單元</li>
<li><strong>工作日誌</strong> 負責「記錄層面」：記錄決策過程和演進軌跡</li>
<li><strong>主版本日誌</strong> 負責「總覽層面」：提供任務總覽和 Ticket 索引</li>
</ul>
<h3 id="12-ticket-機制的三大目標">1.2 Ticket 機制的三大目標</h3>
<h4 id="目標-1可追溯性traceability">目標 1：可追溯性（Traceability）</h4>
<p><strong>定義</strong>：每個 Ticket 都有明確的來源和目標，可以追溯到需求、設計文件或問題報告。</p>
<p><strong>實現方式</strong>：</p>
<ul>
<li>Ticket 必須包含「參考文件」欄位，連結到需求規格或設計文件</li>
<li>Ticket 必須包含「背景」欄位，說明為什麼需要這個 Ticket</li>
<li>主版本日誌維護完整的 Ticket 索引</li>
</ul>
<p><strong>範例</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gu">### 參考文件
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gu"></span><span class="k">-</span> v0.12.7-design-decisions.md <span class="ni">#決策1</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="k">-</span> docs/app-requirements-spec.md <span class="ni">#UC</span>-01
</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"><span class="gu">### 背景
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="gu"></span>根據 UC-01 需求，需要建立書籍資訊豐富化服務的介面契約</span></span></code></pre></div><p><strong>效益</strong>：</p>
<ul>
<li>開發者清楚知道「為什麼」要做這個任務</li>
<li>PM 可以追蹤每個 Ticket 的需求來源</li>
<li>後續維護者可以理解設計意圖</li>
</ul>
<h4 id="目標-2可驗收性verifiability">目標 2：可驗收性（Verifiability）</h4>
<p><strong>定義</strong>：每個 Ticket 都有明確、可驗證的完成標準，避免主觀判斷。</p>
<p><strong>實現方式</strong>：</p>
<ul>
<li>Ticket 必須包含「驗收條件」欄位，列出所有可驗證的條件</li>
<li>驗收條件必須是客觀可檢查的（檔案存在、測試通過、功能運作）</li>
<li>Review 時逐項檢查驗收條件</li>
</ul>
<p><strong>範例</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 介面檔案建立在 <span class="sb">`lib/domains/import/services/`</span> 目錄
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="k">- [ ]</span> <span class="sb">`enrichBook`</span> 方法簽名完整且明確
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="k">- [ ]</span> 輸入輸出類型定義清楚
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="k">- [ ]</span> 包含完整的文檔註解
</span></span><span class="line"><span class="ln">6</span><span class="cl">- [ ] dart analyze 0 錯誤</span></span></code></pre></div><p><strong>效益</strong>：</p>
<ul>
<li>避免「看起來完成了」但實際未達標準</li>
<li>提供明確的驗收依據</li>
<li>減少 review 時的主觀爭議</li>
</ul>
<h4 id="目標-3可協作性collaborability">目標 3：可協作性（Collaborability）</h4>
<p><strong>定義</strong>：多個開發者可以並行執行不同的 Ticket，互不阻塞。</p>
<p><strong>實現方式</strong>：</p>
<ul>
<li>Ticket 拆分時考慮依賴關係，最小化依賴</li>
<li>明確標註 Ticket 間的依賴關係（必須先完成 / 可並行）</li>
<li>使用 Interface-Driven 開發，內層未完成也可開發外層</li>
</ul>
<p><strong>範例</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gu">### 依賴 Ticket
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gu"></span><span class="k">-</span> Ticket <span class="ni">#1:</span> 定義 IBookInfoEnrichmentService 介面（必須先完成）
</span></span><span class="line"><span class="ln">3</span><span class="cl">- Ticket <span class="ni">#3:</span> 撰寫 EnrichmentProgress 測試（可並行）</span></span></code></pre></div><p><strong>效益</strong>：</p>
<ul>
<li>多人可同時開發不同模組</li>
<li>減少等待時間</li>
<li>提升開發效率</li>
</ul>
<h3 id="13-基於-clean-architecture-的-ticket-設計哲學">1.3 基於 Clean Architecture 的 Ticket 設計哲學</h3>
<h4 id="clean-architecture-核心原則回顧">Clean Architecture 核心原則回顧</h4>
<p><strong>依賴方向規則</strong>：</p>
<ul>
<li>外層依賴內層，內層不依賴外層</li>
<li>所有依賴都指向內層（Domain）</li>
</ul>
<p><strong>分層定義</strong>：</p>
<ul>
<li><strong>Entities（Domain）</strong>：核心業務邏輯</li>
<li><strong>Use Cases（Application）</strong>：應用業務規則</li>
<li><strong>Interface Adapters（Presentation/Infrastructure）</strong>：轉接層</li>
<li><strong>Frameworks &amp; Drivers（External）</strong>：外部實作</li>
</ul>
<h4 id="ticket-設計對應-clean-architecture">Ticket 設計對應 Clean Architecture</h4>
<h5 id="原則-1interface-定義優先於具體實作">原則 1：Interface 定義優先於具體實作</h5>
<p>每個功能模組的開發順序：</p>
<ol>
<li><strong>先定義 Interface</strong>（Ticket 類型：Interface 定義）</li>
<li><strong>再實作具體邏輯</strong>（Ticket 類型：具體實作）</li>
</ol>
<p><strong>範例</strong>：</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">Ticket #1: 定義 IBookRepository 介面
</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">Ticket #2: 實作 SQLiteBookRepository（依賴 #1）</span></span></code></pre></div><p><strong>好處</strong>：</p>
<ul>
<li>外層可依賴 Interface 先行開發（使用 Mock）</li>
<li>內層實作延後，不阻塞外層開發</li>
<li>符合 Dependency Inversion Principle</li>
</ul>
<h6 id="原則-2測試驅動-ticket-拆分">原則 2：測試驅動 Ticket 拆分</h6>
<p>每個實作 Ticket 都應該有對應的測試 Ticket：</p>
<ol>
<li><strong>Interface 定義 Ticket</strong> → 定義契約</li>
<li><strong>測試撰寫 Ticket</strong> → 驗證契約</li>
<li><strong>具體實作 Ticket</strong> → 實現契約</li>
<li><strong>整合驗證 Ticket</strong> → 確認整合</li>
</ol>
<p><strong>範例</strong>：</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">Ticket #1: 定義 IBookRepository 介面
</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">Ticket #2: 撰寫 BookRepository 測試
</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">Ticket #3: 實作 SQLiteBookRepository
</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">Ticket #4: 整合測試驗證</span></span></code></pre></div><p><strong>好處</strong>：</p>
<ul>
<li>測試先行，確保需求清晰</li>
<li>實作以測試為目標，不過度設計</li>
<li>整合驗證確保模組間正確協作</li>
</ul>
<h6 id="原則-3分層拆分-ticket">原則 3：分層拆分 Ticket</h6>
<p>基於 Clean Architecture 分層，將任務拆分為不同層次的 Ticket：</p>
<p><strong>Domain 層 Ticket</strong>：</p>
<ul>
<li>定義 Entity</li>
<li>定義 Value Object</li>
<li>定義 Domain Event</li>
<li>定義 Repository Interface</li>
</ul>
<p><strong>Application 層 Ticket</strong>：</p>
<ul>
<li>定義 Use Case Interface</li>
<li>實作 Use Case Interactor</li>
<li>定義 Input/Output Port</li>
</ul>
<p><strong>Infrastructure 層 Ticket</strong>：</p>
<ul>
<li>實作 Repository</li>
<li>實作 External Service</li>
<li>實作 Database Mapper</li>
</ul>
<p><strong>Presentation 層 Ticket</strong>：</p>
<ul>
<li>實作 Controller/Presenter</li>
<li>實作 ViewModel</li>
<li>實作 UI Component</li>
</ul>
<p><strong>好處</strong>：</p>
<ul>
<li>職責清晰，不跨層混合</li>
<li>符合 Clean Architecture 分層原則</li>
<li>易於並行開發</li>
</ul>
<h3 id="14-ticket-機制與-tdd-階段的關係">1.4 Ticket 機制與 TDD 階段的關係</h3>
<h4 id="tdd-階段回顧">TDD 階段回顧</h4>
<ul>
<li><strong>Phase 1</strong>：功能設計（設計 Interface、規劃架構）</li>
<li><strong>Phase 2</strong>：測試設計（撰寫測試案例）</li>
<li><strong>Phase 3</strong>：實作執行（實作功能、通過測試）</li>
<li><strong>Phase 4</strong>：重構優化（改善品質、消除技術債務）</li>
</ul>
<h4 id="ticket-與-tdd-階段對應">Ticket 與 TDD 階段對應</h4>
<p><strong>Phase 1 產出的 Ticket</strong>：</p>
<p>Phase 1 設計階段產出「Interface 定義 Ticket」：</p>
<ul>
<li>定義 Domain Interface</li>
<li>定義 Use Case Interface</li>
<li>定義 Input/Output Port</li>
</ul>
<p><strong>特徵</strong>：</p>
<ul>
<li>無具體實作，只有介面簽名</li>
<li>明確定義輸入輸出</li>
<li>建立契約規範</li>
</ul>
<p><strong>Phase 2 產出的 Ticket</strong>：</p>
<p>Phase 2 測試設計階段產出「測試撰寫 Ticket」：</p>
<ul>
<li>撰寫 Entity 測試</li>
<li>撰寫 Use Case 測試</li>
<li>撰寫 Repository 測試</li>
</ul>
<p><strong>特徵</strong>：</p>
<ul>
<li>測試先行，定義預期行為</li>
<li>覆蓋所有 Interface 方法</li>
<li>包含正常流程和異常處理</li>
</ul>
<p><strong>Phase 3 產出的 Ticket</strong>：</p>
<p>Phase 3 實作階段產出「具體實作 Ticket」：</p>
<ul>
<li>實作 Entity 邏輯</li>
<li>實作 Use Case Interactor</li>
<li>實作 Repository</li>
</ul>
<p><strong>特徵</strong>：</p>
<ul>
<li>以測試通過為目標</li>
<li>最小可行實作</li>
<li>100% 測試通過</li>
</ul>
<p><strong>Phase 4 產出的 Ticket</strong>：</p>
<p>Phase 4 重構階段產出「品質改善 Ticket」：</p>
<ul>
<li>重構複雜邏輯</li>
<li>消除程式異味</li>
<li>改善錯誤處理</li>
</ul>
<p><strong>特徵</strong>：</p>
<ul>
<li>保持測試通過</li>
<li>改善程式品質</li>
<li>不新增功能</li>
</ul>
<h4 id="ticket-機制支援-tdd-流程">Ticket 機制支援 TDD 流程</h4>
<h5 id="支援-1明確的階段產出">支援 1：明確的階段產出</h5>
<p>每個 TDD Phase 都產出對應的 Ticket 類型，職責清晰。</p>
<h6 id="支援-2可並行執行">支援 2：可並行執行</h6>
<p>Phase 1 完成後，多個開發者可並行執行 Phase 2-3 的不同 Ticket。</p>
<h6 id="支援-3即時-review">支援 3：即時 Review</h6>
<p>每完成一個 Ticket 觸發 review，確保品質。</p>
<hr>
<h2 id="第二章ticket-拆分標準">第二章：Ticket 拆分標準</h2>
<h3 id="21-量化指標定義">2.1 量化指標定義</h3>
<p>為了確保 Ticket 拆分的一致性和可操作性，定義以下 <strong>4 個量化指標</strong>。</p>
<h4 id="核心原則基於客觀的工作內容評估">核心原則**：**基於客觀的工作內容評估</h4>
<ul>
<li><strong>職責數量</strong>：最客觀，不受個人能力影響- <strong>程式碼行數</strong>：可量化，可驗證- <strong>檔案數量</strong>：架構層級指標- <strong>測試數量</strong>：品質保證指標</li>
</ul>
<hr>
<h4 id="指標-1職責數量responsibilities最優先">指標 1：職責數量（Responsibilities）（最優先）</h4>
<p><strong>定義</strong>：Ticket 需要完成的獨立職責數量。</p>
<p><strong>標準</strong>：</p>
<ul>
<li><strong>簡單 Ticket</strong>：1 個明確職責</li>
<li><strong>中等 Ticket</strong>：2-3 個相關職責</li>
<li><strong>複雜 Ticket</strong>：3-5 個相關職責</li>
<li><strong>超過 5 個職責</strong>：必須拆分</li>
</ul>
<p><strong>識別方式</strong>：</p>
<ul>
<li>每個「需要實作的功能點」算一個職責</li>
<li>每個「需要驗證的邊界條件」算一個職責</li>
<li>每個「需要處理的錯誤情境」算一個職責</li>
</ul>
<p><strong>範例</strong>：</p>
<ul>
<li><strong>簡單（1 職責）</strong>：定義 SelectionManager 介面方法簽名</li>
<li><strong>中等（2-3 職責）</strong>：
<ul>
<li>職責 1: 實作 toggleSelection 方法</li>
<li>職責 2: 實作 clearSelection 方法</li>
<li>職責 3: 通知狀態變更（ChangeNotifier）</li>
</ul>
</li>
<li><strong>複雜（3-5 職責）</strong>：
<ul>
<li>職責 1: Repository CRUD 實作</li>
<li>職責 2: Data Mapper 轉換</li>
<li>職責 3: 錯誤處理</li>
<li>職責 4: Cache 管理</li>
<li>職責 5: 單元測試</li>
</ul>
</li>
</ul>
<p><strong>為什麼職責是第一指標</strong>：</p>
<ul>
<li><strong>最客觀</strong>：不受個人能力影響- <strong>最穩定</strong>：不受環境和參考資料影響- <strong>最易溝通</strong>：PM 和工程師都能理解</li>
</ul>
<h4 id="指標-2程式碼行數lines-of-code">指標 2：程式碼行數（Lines of Code）</h4>
<p><strong>定義</strong>：Ticket 涉及的程式碼修改行數（新增 + 修改 + 刪除）。</p>
<p><strong>標準</strong>：</p>
<ul>
<li><strong>簡單 Ticket</strong>：&lt; 30 行</li>
<li><strong>中等 Ticket</strong>：30-50 行</li>
<li><strong>複雜 Ticket</strong>：50-100 行</li>
<li><strong>超過 100 行</strong>：必須拆分</li>
</ul>
<p><strong>測量方式</strong>：</p>
<ul>
<li>使用 <code>git diff --stat</code> 統計</li>
<li>包含新增、修改、刪除的行數總和</li>
<li>不包含空行和註解</li>
</ul>
<p><strong>範例</strong>：</p>
<ul>
<li>簡單：定義 Interface（~20 行）</li>
<li>中等：實作 Value Object（~40 行）</li>
<li>複雜：實作 Repository（~80 行）</li>
</ul>
<h4 id="指標-3涉及檔案數files">指標 3：涉及檔案數（Files）</h4>
<p><strong>定義</strong>：Ticket 需要修改的檔案數量。</p>
<p><strong>標準</strong>：</p>
<ul>
<li><strong>簡單 Ticket</strong>：1 個檔案</li>
<li><strong>中等 Ticket</strong>：2-3 個檔案</li>
<li><strong>複雜 Ticket</strong>：3-5 個檔案</li>
<li><strong>超過 5 個檔案</strong>：必須拆分</li>
</ul>
<p><strong>測量方式</strong>：</p>
<ul>
<li>計算 <code>git diff --name-only</code> 的檔案數</li>
<li>包含新建檔案</li>
<li>不包含測試檔案（測試檔案另計）</li>
</ul>
<p><strong>範例</strong>：</p>
<ul>
<li>簡單：新建一個 Interface 檔案（1 個）</li>
<li>中等：修改 Entity 和 Repository Interface（2 個）</li>
<li>複雜：實作 Use Case，修改 Interactor、Input Port、Output Port（3 個）</li>
</ul>
<h4 id="指標-4測試用例數tests">指標 4：測試用例數（Tests）</h4>
<p><strong>定義</strong>：Ticket 對應的測試用例數量。</p>
<p><strong>標準</strong>：</p>
<ul>
<li><strong>簡單 Ticket</strong>：1-3 個測試</li>
<li><strong>中等 Ticket</strong>：3-5 個測試</li>
<li><strong>複雜 Ticket</strong>：5-10 個測試</li>
<li><strong>超過 10 個測試</strong>：必須拆分</li>
</ul>
<p><strong>測量方式</strong>：</p>
<ul>
<li>計算 test 檔案中的測試方法數</li>
<li>包含單元測試和整合測試</li>
<li>每個 <code>test('...', () {...})</code> 算一個</li>
</ul>
<p><strong>範例</strong>：</p>
<ul>
<li>簡單：測試 Value Object 的建立和驗證（2 個測試）</li>
<li>中等：測試 Repository 的 CRUD 操作（4 個測試）</li>
<li>複雜：測試 Use Case 的正常流程和異常處理（8 個測試）</li>
</ul>
<hr>
<h3 id="22-任務複雜度評估方法">2.2 任務複雜度評估方法</h3>
<p>基於 4 個量化指標，定義任務複雜度評估方法：</p>
<p><strong>複雜度等級定義</strong>：</p>
<table>
  <thead>
      <tr>
          <th>等級</th>
          <th>職責</th>
          <th>檔案</th>
          <th>測試</th>
          <th>行數</th>
          <th>描述</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>簡單</strong></td>
          <td>1 個</td>
          <td>1 個</td>
          <td>1-3 個</td>
          <td>&lt; 30 行</td>
          <td>單一職責，單一檔案</td>
      </tr>
      <tr>
          <td><strong>中等</strong></td>
          <td>2-3 個</td>
          <td>2-3 個</td>
          <td>3-6 個</td>
          <td>30-70 行</td>
          <td>少數職責，少數檔案</td>
      </tr>
      <tr>
          <td><strong>複雜</strong></td>
          <td>3-5 個</td>
          <td>3-5 個</td>
          <td>6-10 個</td>
          <td>70-100 行</td>
          <td>多職責，多檔案</td>
      </tr>
      <tr>
          <td><strong>需拆分</strong></td>
          <td>&gt; 5 個</td>
          <td>&gt; 5 個</td>
          <td>&gt; 10 個</td>
          <td>&gt; 100 行</td>
          <td>任一指標超標必須拆分</td>
      </tr>
  </tbody>
</table>
<p><strong>評估方法</strong>：</p>
<h4 id="步驟-1初步評估">步驟 1：初步評估</h4>
<ul>
<li>根據任務描述，估算 4 個指標</li>
<li>取最高的複雜度等級作為初步評估結果</li>
</ul>
<h5 id="步驟-2複雜度確認">步驟 2：複雜度確認</h5>
<ul>
<li>如果初步評估為「需拆分」，必須拆分</li>
<li>如果初步評估為「複雜」，檢查是否可拆分為「簡單」或「中等」</li>
<li>如果初步評估為「中等」或「簡單」，可直接建立 Ticket</li>
</ul>
<h6 id="步驟-3拆分決策">步驟 3：拆分決策</h6>
<ul>
<li>優先拆分為「簡單」Ticket（單一職責、單一檔案）</li>
<li>無法拆分為「簡單」時，拆分為「中等」Ticket（2-3 職責）</li>
<li>避免建立「複雜」Ticket（3-5 職責），除非無法再拆分</li>
</ul>
<h3 id="23-基於-clean-architecture-分層的拆分策略">2.3 基於 Clean Architecture 分層的拆分策略</h3>
<p>基於 Clean Architecture 的分層原則，定義 4 種標準拆分策略：</p>
<h4 id="策略-1interface-定義-ticket">策略 1：Interface 定義 Ticket</h4>
<p><strong>目的</strong>：定義一個介面及其輸入輸出契約。</p>
<p><strong>範圍</strong>：</p>
<ul>
<li>定義 Interface 簽名</li>
<li>定義輸入參數類型</li>
<li>定義回傳類型</li>
<li>撰寫文檔註解</li>
</ul>
<p><strong>範例</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #1: 定義 IBookRepository 介面
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 目標
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>建立 <span class="sb">`IBookRepository`</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="gu">### 步驟
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 在 <span class="sb">`lib/domains/library/repositories/`</span> 建立 <span class="sb">`i_book_repository.dart`</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">2.</span> 定義 <span class="sb">`getBookByIsbn`</span> 方法簽名
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">3.</span> 定義 <span class="sb">`saveBook`</span> 方法簽名
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">4.</span> 定義 <span class="sb">`deleteBook`</span> 方法簽名
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">5.</span> 撰寫文檔註解
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> Interface 檔案建立在正確位置
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">- [ ]</span> 3 個方法簽名完整且明確
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">- [ ]</span> 輸入輸出類型定義清楚
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">- [ ]</span> 包含完整的文檔註解
</span></span><span class="line"><span class="ln">18</span><span class="cl">- [ ] dart analyze 0 錯誤</span></span></code></pre></div><h4 id="策略-2具體實作-ticket">策略 2：具體實作 Ticket</h4>
<p><strong>目的</strong>：實作一個類別的核心邏輯。</p>
<p><strong>範圍</strong>：</p>
<ul>
<li>實作類別邏輯</li>
<li>實現介面方法</li>
<li>處理異常</li>
<li>確保測試通過</li>
</ul>
<p><strong>範例</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #2: 實作 SQLiteBookRepository
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 目標
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>實作 <span class="sb">`SQLiteBookRepository`</span>，提供書籍資料的 SQLite 儲存
</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="gu">### 步驟
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 建立 <span class="sb">`SQLiteBookRepository`</span> 類別
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">2.</span> 實作 <span class="sb">`getBookByIsbn`</span> 方法
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">3.</span> 實作 <span class="sb">`saveBook`</span> 方法
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">4.</span> 實作 <span class="sb">`deleteBook`</span> 方法
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">5.</span> 處理 SQLite 異常
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">6.</span> 確保所有測試通過
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 實作所有 IBookRepository 方法
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">- [ ]</span> 異常處理完整（Database Exception）
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">- [ ]</span> 單元測試 100% 通過
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="gu">### 依賴 Ticket
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="gu"></span>- Ticket <span class="ni">#1:</span> 定義 IBookRepository 介面（必須先完成）</span></span></code></pre></div><h4 id="策略-3測試驗證-ticket">策略 3：測試驗證 Ticket</h4>
<p><strong>目的</strong>：撰寫一組相關的測試用例。</p>
<p><strong>範圍</strong>：</p>
<ul>
<li>撰寫單元測試</li>
<li>覆蓋正常流程</li>
<li>覆蓋異常處理</li>
<li>確保測試通過</li>
</ul>
<p><strong>範例</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #3: 撰寫 BookRepository 測試
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 目標
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>撰寫 <span class="sb">`BookRepository`</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="gu">### 步驟
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 建立測試檔案 <span class="sb">`book_repository_test.dart`</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">2.</span> 撰寫 <span class="sb">`getBookByIsbn`</span> 測試（正常流程 + 不存在情況）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">3.</span> 撰寫 <span class="sb">`saveBook`</span> 測試（新增 + 更新）
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">4.</span> 撰寫 <span class="sb">`deleteBook`</span> 測試（存在 + 不存在）
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">5.</span> 確保所有測試通過
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 測試檔案建立在 <span class="sb">`test/unit/domains/library/repositories/`</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">- [ ]</span> 至少 6 個測試用例
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">- [ ]</span> 覆蓋正常流程和異常處理
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">- [ ]</span> 所有測試 100% 通過
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="gu">### 依賴 Ticket
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="gu"></span>- Ticket <span class="ni">#1:</span> 定義 IBookRepository 介面（必須先完成）</span></span></code></pre></div><h4 id="策略-4整合連接-ticket">策略 4：整合連接 Ticket</h4>
<p><strong>目的</strong>：連接兩個模組並驗證整合。</p>
<p><strong>範圍</strong>：</p>
<ul>
<li>連接 Use Case 和 Repository</li>
<li>實作依賴注入</li>
<li>撰寫整合測試</li>
<li>驗證端到端流程</li>
</ul>
<p><strong>範例</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #4: 整合 BookRepository 到 GetBookUseCase
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 目標
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>將 <span class="sb">`BookRepository`</span> 整合到 <span class="sb">`GetBookUseCase`</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="gu">### 步驟
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 修改 <span class="sb">`GetBookInteractor`</span> 注入 <span class="sb">`IBookRepository`</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">2.</span> 在 <span class="sb">`execute`</span> 方法中呼叫 <span class="sb">`repository.getBookByIsbn`</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">3.</span> 處理 Repository 異常
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">4.</span> 撰寫整合測試驗證端到端流程
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">5.</span> 確保所有測試通過
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> <span class="sb">`GetBookInteractor`</span> 正確注入 <span class="sb">`IBookRepository`</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">- [ ]</span> 端到端流程正常運作
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">- [ ]</span> 整合測試 100% 通過
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">- [ ]</span> 異常處理完整
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="gu">### 依賴 Ticket
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="gu"></span>- Ticket <span class="ni">#2:</span> 實作 SQLiteBookRepository（必須先完成）</span></span></code></pre></div><h3 id="24-code-smell-品質閘門檢測">2.4 Code Smell 品質閘門檢測</h3>
<p>本節整合 v0.12.G.2「Code Smell 檢查清單」到 Ticket 設計流程，實現「設計階段就能發現 Code Smell」的品質管理策略。</p>
<p><strong>整合目標</strong>:</p>
<ul>
<li>在 Ticket 設計階段執行 Code Smell 檢測</li>
<li>降低修正成本（設計階段 vs 實作後修正）</li>
<li>提供明確的 Ticket 設計品質標準</li>
<li>實現預防勝於治療的品質管理</li>
</ul>
<p><strong>引用方法論</strong>:</p>
<ul>
<li>引用 v0.12.G.1「層級隔離派工方法論」的五層架構和單層修改原則</li>
<li>引用 v0.12.G.2「Code Smell 檢查清單」的 C 類 Code Smell 檢測方法</li>
</ul>
<hr>
<h4 id="241-品質閘門機制概述">2.4.1 品質閘門機制概述</h4>
<p><strong>品質閘門定義</strong>:</p>
<p>品質閘門（Quality Gate）是 Ticket 設計階段的強制檢查點，確保 Ticket 符合 Code Smell 品質標準後才能進入執行階段。</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">Ticket 設計流程整合品質閘門：
</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">Phase 1: 功能設計（lavender-interface-designer）
</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">  1. 撰寫 Ticket 清單
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  2. 定義驗收條件
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  3. 規劃步驟和檔案
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">【品質閘門檢測】← 新增檢測點
</span></span><span class="line"><span class="ln">10</span><span class="cl">  ├─ C1. God Ticket 檢測
</span></span><span class="line"><span class="ln">11</span><span class="cl">  ├─ C2. Incomplete Ticket 檢測
</span></span><span class="line"><span class="ln">12</span><span class="cl">  └─ C3. Ambiguous Responsibility 檢測
</span></span><span class="line"><span class="ln">13</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">14</span><span class="cl">  通過 → Phase 2: 測試驗證
</span></span><span class="line"><span class="ln">15</span><span class="cl">  未通過 → 修正 Ticket → 重新檢測</span></span></code></pre></div><p><strong>檢測時機</strong>:</p>
<table>
  <thead>
      <tr>
          <th>檢測時機</th>
          <th>說明</th>
          <th>執行者</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Ticket 撰寫完成後</strong></td>
          <td>所有 Ticket 的內容（背景、目標、步驟、驗收條件）都已撰寫完成</td>
          <td>lavender-interface-designer</td>
      </tr>
      <tr>
          <td><strong>分派執行前</strong></td>
          <td>在將 Ticket 分派給開發者執行前，必須先通過品質閘門</td>
          <td>lavender-interface-designer</td>
      </tr>
      <tr>
          <td><strong>PM 審查前</strong></td>
          <td>PM 審查時，品質閘門檢測報告是重要參考依據</td>
          <td>lavender-interface-designer</td>
      </tr>
  </tbody>
</table>
<p><strong>檢測執行者</strong>:</p>
<p><strong>責任歸屬</strong>: lavender-interface-designer（TDD Phase 1 功能設計專家）</p>
<p><strong>檢測職責</strong>:</p>
<ol>
<li>對每個 Ticket 執行 C1/C2/C3 檢測</li>
<li>記錄檢測結果到工作日誌</li>
<li>檢測失敗時執行修正</li>
<li>提供品質閘門報告給 PM</li>
</ol>
<p><strong>阻斷機制</strong>:</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><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">所有 Ticket 都通過 C1/C2/C3？
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  ├─ Yes → 提交給 PM 審查
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  │           └─ PM 批准 → 分派給開發者執行
</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">  └─ No → 阻止進入 Phase 2
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">              ├─ 記錄檢測失敗原因
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">              ├─ 執行修正（拆分/補充/重新定義）
</span></span><span class="line"><span class="ln">10</span><span class="cl">              └─ 重新檢測（直到通過）</span></span></code></pre></div><p><strong>阻斷原則</strong>:</p>
<ul>
<li><strong>強制阻斷</strong>: 任何 Ticket 未通過 C1/C2/C3 檢測，禁止進入 Phase 2</li>
<li><strong>修正優先</strong>: 發現問題後立即修正，不延後處理</li>
<li><strong>完整記錄</strong>: 所有檢測過程和修正過程都記錄到工作日誌</li>
</ul>
<p><strong>品質閘門價值</strong>:</p>
<table>
  <thead>
      <tr>
          <th>價值維度</th>
          <th>說明</th>
          <th>效益</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>及早發現</strong></td>
          <td>設計階段就能發現 Code Smell</td>
          <td>修正成本降低 80%</td>
      </tr>
      <tr>
          <td><strong>標準化</strong></td>
          <td>提供統一的檢測標準</td>
          <td>避免主觀判斷</td>
      </tr>
      <tr>
          <td><strong>可追溯</strong></td>
          <td>完整的檢測記錄</td>
          <td>提升品質可見性</td>
      </tr>
      <tr>
          <td><strong>預防性</strong></td>
          <td>預防問題進入實作階段</td>
          <td>減少返工時間</td>
      </tr>
  </tbody>
</table>
<hr>
<h4 id="242-c1-god-ticket-檢測">2.4.2 C1. God Ticket 檢測</h4>
<p><strong>定義</strong>（引用 v0.12.G.2 第 2.3.1 節 God Ticket 定義）:</p>
<p>God Ticket 是指單一 Ticket 修改過多檔案和層級，範圍失控，違反「單層修改原則」（引用 v0.12.G.1 第 3.1 節）。</p>
<p><strong>量化檢測指標</strong>:</p>
<table>
  <thead>
      <tr>
          <th>指標</th>
          <th>良好設計</th>
          <th>需要檢查</th>
          <th>God Ticket（必須拆分）</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>檔案數量</strong></td>
          <td>1-3 個（合格）</td>
          <td>4-6 個（需檢查）</td>
          <td><strong>&gt; 10 個</strong>（超標）</td>
      </tr>
      <tr>
          <td><strong>層級跨度</strong></td>
          <td>1 層（合格）</td>
          <td>2 層（需檢查）</td>
          <td><strong>&gt; 2 層</strong>（超標）</td>
      </tr>
      <tr>
          <td><strong>預估工時</strong></td>
          <td>2-4 小時（合格）</td>
          <td>4-8 小時（需檢查）</td>
          <td><strong>&gt; 16 小時</strong>（超標）</td>
      </tr>
  </tbody>
</table>
<p><strong>組合邏輯</strong>（Phase 2 補充說明）:</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">判斷標準：任一項目超標 = God Ticket（需要拆分）
</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">- 檔案數 = 8 個（未超標）、層級跨度 = 3 層（超標） → God Ticket
</span></span><span class="line"><span class="ln">6</span><span class="cl">- 檔案數 = 12 個（超標）、層級跨度 = 1 層（未超標） → God Ticket
</span></span><span class="line"><span class="ln">7</span><span class="cl">- 檔案數 = 5 個（未超標）、層級跨度 = 2 層（未超標）、工時 = 20 小時（超標） → God Ticket
</span></span><span class="line"><span class="ln">8</span><span class="cl">
</span></span><span class="line"><span class="ln">9</span><span class="cl">只要有任何一個指標超標，就判定為 God Ticket，必須拆分。</span></span></code></pre></div><p><strong>檢測方法（Ticket 設計階段）</strong>:</p>
<h5 id="步驟-1-列出-ticket-涉及的檔案清單">步驟 1: 列出 Ticket 涉及的檔案清單</h5>
<p>從 Ticket 的「步驟」章節中提取所有檔案路徑：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">範例 Ticket 步驟：
</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 class="k">1.</span> 建立 Rating Value Object（<span class="sb">`lib/domain/value_objects/rating_value.dart`</span>）
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="k">2.</span> 建立 Rating Entity（<span class="sb">`lib/domain/entities/rating.dart`</span>）
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">3.</span> 更新 Book Entity（<span class="sb">`lib/domain/entities/book.dart`</span>）
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">4.</span> 定義 IRatingRepository（<span class="sb">`lib/domain/repositories/i_rating_repository.dart`</span>）
</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">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">提取檔案清單：
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">-</span> lib/domain/value_objects/rating_value.dart
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">-</span> lib/domain/entities/rating.dart
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">-</span> lib/domain/entities/book.dart
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">-</span> lib/domain/repositories/i_rating_repository.dart
</span></span><span class="line"><span class="ln">15</span><span class="cl">...</span></span></code></pre></div><h6 id="步驟-2-計算檔案數量">步驟 2: 計算檔案數量</h6>





<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><span class="line"><span class="ln">2</span><span class="cl">  ├─ 1-3 個 → 良好設計
</span></span><span class="line"><span class="ln">3</span><span class="cl">  ├─ 4-6 個 → 需要檢查（評估是否可拆分）
</span></span><span class="line"><span class="ln">4</span><span class="cl">  ├─ 7-10 個 → 建議拆分
</span></span><span class="line"><span class="ln">5</span><span class="cl">  └─ &gt; 10 個 → God Ticket（強制拆分）</span></span></code></pre></div><h6 id="步驟-3-判斷層級跨度">步驟 3: 判斷層級跨度</h6>
<p>使用 v0.12.G.1 第 6.2 節「檔案路徑分析法」判斷每個檔案所屬層級：</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><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">Layer 1（UI）:
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">- lib/presentation/widgets/
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">- lib/presentation/pages/
</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">Layer 2（Behavior）:
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">- lib/presentation/controllers/
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">- lib/presentation/providers/
</span></span><span class="line"><span class="ln">10</span><span class="cl">- lib/presentation/view_models/
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">Layer 3（UseCase）:
</span></span><span class="line"><span class="ln">13</span><span class="cl">- lib/application/use_cases/
</span></span><span class="line"><span class="ln">14</span><span class="cl">- lib/application/services/
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">Layer 4（Domain Events/Interfaces）:
</span></span><span class="line"><span class="ln">17</span><span class="cl">- lib/domain/events/
</span></span><span class="line"><span class="ln">18</span><span class="cl">- lib/domain/repositories/ (介面)
</span></span><span class="line"><span class="ln">19</span><span class="cl">- lib/domain/services/ (介面)
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl">Layer 5（Domain Implementation）:
</span></span><span class="line"><span class="ln">22</span><span class="cl">- lib/domain/entities/
</span></span><span class="line"><span class="ln">23</span><span class="cl">- lib/domain/value_objects/
</span></span><span class="line"><span class="ln">24</span><span class="cl">- lib/infrastructure/repositories/ (實作)
</span></span><span class="line"><span class="ln">25</span><span class="cl">- lib/infrastructure/services/ (實作)</span></span></code></pre></div><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><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">- lib/presentation/widgets/rating_widget.dart → Layer 1
</span></span><span class="line"><span class="ln">5</span><span class="cl">- lib/presentation/controllers/rating_controller.dart → Layer 2
</span></span><span class="line"><span class="ln">6</span><span class="cl">- lib/application/use_cases/rate_book_use_case.dart → Layer 3
</span></span><span class="line"><span class="ln">7</span><span class="cl">- lib/domain/entities/rating.dart → Layer 5
</span></span><span class="line"><span class="ln">8</span><span class="cl">
</span></span><span class="line"><span class="ln">9</span><span class="cl">層級跨度 = max(5) - min(1) = 4 層（超標）→ God Ticket</span></span></code></pre></div><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">  ├─ 1 層（單層修改）→ 良好設計
</span></span><span class="line"><span class="ln">2</span><span class="cl">  ├─ 2 層（Facade 整合）→ 需要檢查（可能可接受）
</span></span><span class="line"><span class="ln">3</span><span class="cl">  └─ &gt; 2 層（跨多層修改）→ God Ticket（強制拆分）</span></span></code></pre></div><h6 id="步驟-4-評估預估工時">步驟 4: 評估預估工時</h6>
<p>根據 Ticket 的「步驟」章節複雜度評估：</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><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">1. 步驟數量：
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">   ├─ &lt; 10 項 → 簡單任務（2-4 小時）
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">   ├─ 10-20 項 → 中等任務（4-8 小時）
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">   └─ &gt; 20 項 → 複雜任務（&gt; 8 小時）
</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">2. 步驟複雜度：
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">   ├─ 單純檔案建立或方法定義 → 簡單（x1.0 係數）
</span></span><span class="line"><span class="ln">10</span><span class="cl">   ├─ 包含邏輯實作和測試 → 中等（x1.5 係數）
</span></span><span class="line"><span class="ln">11</span><span class="cl">   └─ 包含多層整合和異常處理 → 複雜（x2.0 係數）
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">3. 計算公式：
</span></span><span class="line"><span class="ln">14</span><span class="cl">   預估工時 = 步驟數量 × 平均每步驟時間 × 複雜度係數
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">範例：
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl">- 15 個步驟 × 30 分鐘 × 1.5 係數 = 11.25 小時（需檢查）
</span></span><span class="line"><span class="ln">19</span><span class="cl">- 25 個步驟 × 30 分鐘 × 2.0 係數 = 25 小時（超標）→ God Ticket</span></span></code></pre></div><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">  ├─ 2-4 小時 → 良好設計
</span></span><span class="line"><span class="ln">2</span><span class="cl">  ├─ 4-8 小時 → 需要檢查
</span></span><span class="line"><span class="ln">3</span><span class="cl">  ├─ 8-16 小時 → 建議拆分
</span></span><span class="line"><span class="ln">4</span><span class="cl">  └─ &gt; 16 小時 → God Ticket（強制拆分）</span></span></code></pre></div><p><strong>檢測失敗處理</strong>:</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">God Ticket 檢測失敗 → 執行拆分策略
</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">步驟 1: 選擇拆分策略
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  優先策略: 按層級拆分（引用 v0.12.G.1 第 3.1 節單層修改原則）
</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></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">步驟 2: 執行拆分（引用 v0.12.G.1 第 5.4 節 Ticket 拆分指引）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  範例：14 個檔案、4 層 → 拆分為 5 個 Ticket（每層級 1 個）
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">步驟 3: 重新檢測
</span></span><span class="line"><span class="ln">12</span><span class="cl">  對拆分後的每個 Ticket 重新執行 C1 檢測
</span></span><span class="line"><span class="ln">13</span><span class="cl">  確保所有 Ticket 都通過標準</span></span></code></pre></div><p><strong>拆分策略決策樹</strong>:</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">God Ticket 拆分決策：
</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">檔案數量 &gt; 10 或 層級跨度 &gt; 2？
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  └─ Yes → 按層級拆分（優先）
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">            ├─ Layer 5: Domain 層 Ticket
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">            ├─ Layer 4: Domain 介面層 Ticket
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">            ├─ Layer 3: UseCase 層 Ticket
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">            ├─ Layer 2: Behavior 層 Ticket
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">            └─ Layer 1: UI 層 Ticket
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">預估工時 &gt; 16 小時？
</span></span><span class="line"><span class="ln">12</span><span class="cl">  └─ Yes → 按職責拆分（次要）
</span></span><span class="line"><span class="ln">13</span><span class="cl">            ├─ 職責 1: 資料建模 Ticket
</span></span><span class="line"><span class="ln">14</span><span class="cl">            ├─ 職責 2: 業務邏輯 Ticket
</span></span><span class="line"><span class="ln">15</span><span class="cl">            ├─ 職責 3: 介面整合 Ticket
</span></span><span class="line"><span class="ln">16</span><span class="cl">            └─ 職責 4: UI 呈現 Ticket
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl">職責數量 &gt; 5？
</span></span><span class="line"><span class="ln">19</span><span class="cl">  └─ Yes → 按功能模組拆分（最終）
</span></span><span class="line"><span class="ln">20</span><span class="cl">            ├─ 模組 A: 核心功能 Ticket
</span></span><span class="line"><span class="ln">21</span><span class="cl">            ├─ 模組 B: 輔助功能 Ticket
</span></span><span class="line"><span class="ln">22</span><span class="cl">            └─ 模組 C: 整合驗證 Ticket</span></span></code></pre></div><p><strong>檢測範例（完整流程）</strong>:</p>
<p><strong>原始 Ticket（違反 C1 標準）</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket: 新增「書籍評分」完整功能
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 背景
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>根據 UC-03 需求，需要新增書籍評分功能。
</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="gu">### 目標
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span>實作書籍評分的完整功能，包含 UI、邏輯、資料儲存。
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 建立 Rating Value Object（<span class="sb">`lib/domain/value_objects/rating_value.dart`</span>）
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">2.</span> 建立 Rating Entity（<span class="sb">`lib/domain/entities/rating.dart`</span>）
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">3.</span> 更新 Book Entity（<span class="sb">`lib/domain/entities/book.dart`</span>）
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">4.</span> 定義 IRatingRepository（<span class="sb">`lib/domain/repositories/i_rating_repository.dart`</span>）
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">5.</span> 實作 RatingRepositoryImpl（<span class="sb">`lib/infrastructure/repositories/rating_repository_impl.dart`</span>）
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">6.</span> 建立 Rating 資料表（<span class="sb">`lib/infrastructure/database/rating_table.dart`</span>）
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">7.</span> 定義 RateBookUseCase 介面（<span class="sb">`lib/application/use_cases/i_rate_book_use_case.dart`</span>）
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">8.</span> 實作 RateBookUseCase（<span class="sb">`lib/application/use_cases/rate_book_use_case.dart`</span>）
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">9.</span> 定義 GetBookRatingUseCase 介面（<span class="sb">`lib/application/use_cases/i_get_book_rating_use_case.dart`</span>）
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">10.</span> 實作 GetBookRatingUseCase（<span class="sb">`lib/application/use_cases/get_book_rating_use_case.dart`</span>）
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">11.</span> 更新 BookDetailController（<span class="sb">`lib/presentation/controllers/book_detail_controller.dart`</span>）
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">12.</span> 建立 RatingController（<span class="sb">`lib/presentation/controllers/rating_controller.dart`</span>）
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">13.</span> 建立 RatingWidget（<span class="sb">`lib/presentation/widgets/rating_widget.dart`</span>）
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="k">14.</span> 更新 BookDetailWidget（<span class="sb">`lib/presentation/widgets/book_detail_widget.dart`</span>）
</span></span><span class="line"><span class="ln">24</span><span class="cl">15. 撰寫測試</span></span></code></pre></div><p><strong>C1 檢測執行</strong>:</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">步驟 1: 列出檔案清單
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  檔案數量 = 14 個
</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">步驟 2: 計算檔案數量
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  14 個 &gt; 10 個 → God Ticket
</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">步驟 3: 判斷層級跨度
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  檔案層級分布：
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  - Layer 5: rating_value.dart, rating.dart, book.dart (3 個)
</span></span><span class="line"><span class="ln">10</span><span class="cl">  - Layer 5: rating_repository_impl.dart, rating_table.dart (2 個)
</span></span><span class="line"><span class="ln">11</span><span class="cl">  - Layer 4: i_rating_repository.dart (1 個)
</span></span><span class="line"><span class="ln">12</span><span class="cl">  - Layer 3: i_rate_book_use_case.dart, rate_book_use_case.dart (2 個)
</span></span><span class="line"><span class="ln">13</span><span class="cl">  - Layer 3: i_get_book_rating_use_case.dart, get_book_rating_use_case.dart (2 個)
</span></span><span class="line"><span class="ln">14</span><span class="cl">  - Layer 2: book_detail_controller.dart, rating_controller.dart (2 個)
</span></span><span class="line"><span class="ln">15</span><span class="cl">  - Layer 1: rating_widget.dart, book_detail_widget.dart (2 個)
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl">  層級跨度 = 5 - 1 = 4 層（超標）→ God Ticket
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl">步驟 4: 評估預估工時
</span></span><span class="line"><span class="ln">20</span><span class="cl">  步驟數量 = 15 項
</span></span><span class="line"><span class="ln">21</span><span class="cl">  複雜度 = 包含多層整合（x2.0 係數）
</span></span><span class="line"><span class="ln">22</span><span class="cl">  預估工時 = 15 × 30 分鐘 × 2.0 = 15 小時（需檢查）
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl">結論: 檔案數量和層級跨度都超標 → God Ticket（必須拆分）</span></span></code></pre></div><p><strong>拆分策略執行</strong>:</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><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">拆分結果（5 個 Ticket）:
</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">Ticket 1 [Layer 5]: Rating Domain 模型
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  - rating_value.dart
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  - rating.dart
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  - book.dart（新增 rating 欄位）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">Ticket 2 [Layer 5 + 4]: Rating Repository
</span></span><span class="line"><span class="ln">11</span><span class="cl">  - i_rating_repository.dart（介面）
</span></span><span class="line"><span class="ln">12</span><span class="cl">  - rating_repository_impl.dart（實作）
</span></span><span class="line"><span class="ln">13</span><span class="cl">  - rating_table.dart（資料表）
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">Ticket 3 [Layer 3]: RateBook UseCase
</span></span><span class="line"><span class="ln">16</span><span class="cl">  - i_rate_book_use_case.dart（介面）
</span></span><span class="line"><span class="ln">17</span><span class="cl">  - rate_book_use_case.dart（實作）
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl">Ticket 4 [Layer 3]: GetBookRating UseCase
</span></span><span class="line"><span class="ln">20</span><span class="cl">  - i_get_book_rating_use_case.dart（介面）
</span></span><span class="line"><span class="ln">21</span><span class="cl">  - get_book_rating_use_case.dart（實作）
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl">Ticket 5 [Layer 2]: Rating Controller 整合
</span></span><span class="line"><span class="ln">24</span><span class="cl">  - book_detail_controller.dart（更新）
</span></span><span class="line"><span class="ln">25</span><span class="cl">  - rating_controller.dart（新建）
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl">Ticket 6 [Layer 1]: Rating UI 元件
</span></span><span class="line"><span class="ln">28</span><span class="cl">  - rating_widget.dart（新建）
</span></span><span class="line"><span class="ln">29</span><span class="cl">  - book_detail_widget.dart（更新）</span></span></code></pre></div><p><strong>拆分後重新檢測</strong>:</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">Ticket 1 [Layer 5]: Rating Domain 模型
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  檔案數量: 3 個（符合）
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  層級跨度: 1 層（符合）
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  預估工時: 3 小時（符合）
</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></span><span class="line"><span class="ln"> 7</span><span class="cl">Ticket 2 [Layer 5 + 4]: Rating Repository
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  檔案數量: 3 個（符合）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  層級跨度: 2 層（需檢查）（Repository 介面和實作可接受）
</span></span><span class="line"><span class="ln">10</span><span class="cl">  預估工時: 4 小時（符合）
</span></span><span class="line"><span class="ln">11</span><span class="cl">  結論: 通過
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">Ticket 3 [Layer 3]: RateBook UseCase
</span></span><span class="line"><span class="ln">14</span><span class="cl">  檔案數量: 2 個（符合）
</span></span><span class="line"><span class="ln">15</span><span class="cl">  層級跨度: 1 層（符合）
</span></span><span class="line"><span class="ln">16</span><span class="cl">  預估工時: 3 小時（符合）
</span></span><span class="line"><span class="ln">17</span><span class="cl">  結論: 通過
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl">Ticket 4 [Layer 3]: GetBookRating UseCase
</span></span><span class="line"><span class="ln">20</span><span class="cl">  檔案數量: 2 個（符合）
</span></span><span class="line"><span class="ln">21</span><span class="cl">  層級跨度: 1 層（符合）
</span></span><span class="line"><span class="ln">22</span><span class="cl">  預估工時: 2.5 小時（符合）
</span></span><span class="line"><span class="ln">23</span><span class="cl">  結論: 通過
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span><span class="line"><span class="ln">25</span><span class="cl">Ticket 5 [Layer 2]: Rating Controller 整合
</span></span><span class="line"><span class="ln">26</span><span class="cl">  檔案數量: 2 個（符合）
</span></span><span class="line"><span class="ln">27</span><span class="cl">  層級跨度: 1 層（符合）
</span></span><span class="line"><span class="ln">28</span><span class="cl">  預估工時: 3 小時（符合）
</span></span><span class="line"><span class="ln">29</span><span class="cl">  結論: 通過
</span></span><span class="line"><span class="ln">30</span><span class="cl">
</span></span><span class="line"><span class="ln">31</span><span class="cl">Ticket 6 [Layer 1]: Rating UI 元件
</span></span><span class="line"><span class="ln">32</span><span class="cl">  檔案數量: 2 個（符合）
</span></span><span class="line"><span class="ln">33</span><span class="cl">  層級跨度: 1 層（符合）
</span></span><span class="line"><span class="ln">34</span><span class="cl">  預估工時: 4 小時（符合）
</span></span><span class="line"><span class="ln">35</span><span class="cl">  結論: 通過
</span></span><span class="line"><span class="ln">36</span><span class="cl">
</span></span><span class="line"><span class="ln">37</span><span class="cl">最終結論: 所有 Ticket 都通過 C1 檢測</span></span></code></pre></div><p><strong>改善效果</strong>:</p>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>原始 Ticket</th>
          <th>拆分後（6 個 Ticket）</th>
          <th>改善</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>檔案數量</strong></td>
          <td>14 個（超標）</td>
          <td>2-3 個/Ticket（合格）</td>
          <td>符合標準</td>
      </tr>
      <tr>
          <td><strong>層級跨度</strong></td>
          <td>4 層（超標）</td>
          <td>1-2 層/Ticket（合格）</td>
          <td>符合單層修改原則</td>
      </tr>
      <tr>
          <td><strong>預估工時</strong></td>
          <td>15 小時（需檢查）</td>
          <td>2.5-4 小時/Ticket（合格）</td>
          <td>可在半天內完成</td>
      </tr>
      <tr>
          <td><strong>可並行執行</strong></td>
          <td>否</td>
          <td>是（Ticket 1-4 可並行）（合格）</td>
          <td>加速開發</td>
      </tr>
      <tr>
          <td><strong>風險控制</strong></td>
          <td>高風險</td>
          <td>低風險（合格）</td>
          <td>降低失敗影響</td>
      </tr>
  </tbody>
</table>
<hr>
<h4 id="243-c3-ambiguous-responsibility-檢測">2.4.3 C3. Ambiguous Responsibility 檢測</h4>
<p><strong>定義</strong>（引用 v0.12.G.2 第 2.3.3 節 Ambiguous Responsibility 定義）:</p>
<p>Ambiguous Responsibility 是指 Ticket 的職責定義不明確，無法判斷屬於哪一層級，違反「層級明確原則」。</p>
<p><strong>職責明確 Ticket 必要元素</strong>:</p>
<table>
  <thead>
      <tr>
          <th>必要元素</th>
          <th>檢查項目</th>
          <th>範例</th>
          <th>缺失後果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>層級標示</strong></td>
          <td>標題包含 <code>[Layer X]</code> 標籤</td>
          <td><code>[Layer 2] 實作書籍詳情事件處理</code></td>
          <td>無法判斷責任範圍</td>
      </tr>
      <tr>
          <td><strong>職責描述</strong></td>
          <td>目標章節明確定義單一職責</td>
          <td>實作 BookDetailController 的 loadBookDetail 方法</td>
          <td>職責模糊</td>
      </tr>
      <tr>
          <td><strong>檔案範圍</strong></td>
          <td>步驟章節明確列出檔案路徑</td>
          <td>修改 <code>lib/presentation/controllers/book_detail_controller.dart</code></td>
          <td>影響範圍不明</td>
      </tr>
      <tr>
          <td><strong>驗收限定</strong></td>
          <td>驗收條件限定在該層級</td>
          <td>BookDetailController.loadBookDetail() 正確呼叫 UseCase</td>
          <td>驗收標準模糊</td>
      </tr>
  </tbody>
</table>
<p><strong>檢測方法（Ticket 設計階段）</strong>:</p>
<h5 id="步驟-1-檢查層級標示">步驟 1: 檢查層級標示</h5>





<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><span class="line"><span class="ln"> 2</span><span class="cl">  合格範例：
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  - [Layer 1] 建立書籍評分 UI 元件
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  - [Layer 2] 實作書籍詳情 Controller 事件處理
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  - [Layer 3] 實作 GetBookDetail UseCase
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  - [Layer 5] 定義 Book Entity 和 Value Objects
</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">  不合格範例：
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  - 建立書籍評分功能（沒有層級標示）
</span></span><span class="line"><span class="ln">10</span><span class="cl">  - 實作書籍詳情（層級不明確）
</span></span><span class="line"><span class="ln">11</span><span class="cl">  - 整合評分功能（跨多層，職責模糊）</span></span></code></pre></div><h6 id="步驟-2-檢查職責描述">步驟 2: 檢查職責描述</h6>





<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><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">  實作 BookDetailController 的 loadBookDetail 方法，
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  負責接收使用者事件並呼叫 GetBookDetailUseCase。
</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">  - 明確說明「做什麼」：實作 loadBookDetail 方法
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  - 明確說明「責任範圍」：接收事件 + 呼叫 UseCase
</span></span><span class="line"><span class="ln">10</span><span class="cl">  - 明確說明「不做什麼」：不包含業務邏輯實作
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">  不合格範例：
</span></span><span class="line"><span class="ln">13</span><span class="cl">  ### 目標
</span></span><span class="line"><span class="ln">14</span><span class="cl">  實作書籍詳情功能
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">  職責定義模糊：
</span></span><span class="line"><span class="ln">17</span><span class="cl">  - 「書籍詳情功能」範圍太廣（UI? 邏輯? 資料？）
</span></span><span class="line"><span class="ln">18</span><span class="cl">  - 沒有說明具體職責
</span></span><span class="line"><span class="ln">19</span><span class="cl">  - 沒有說明責任邊界</span></span></code></pre></div><h4 id="步驟-3-檢查檔案範圍">步驟 3: 檢查檔案範圍</h4>





<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><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">  1. 修改 `lib/presentation/controllers/book_detail_controller.dart`
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  2. 新增 `loadBookDetail` 方法
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  3. 注入 `IGetBookDetailUseCase` 依賴
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  4. 實作事件處理邏輯
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  檔案範圍明確：
</span></span><span class="line"><span class="ln">10</span><span class="cl">  - 明確列出檔案路徑
</span></span><span class="line"><span class="ln">11</span><span class="cl">  - 避免籠統描述（如「修改相關檔案」）
</span></span><span class="line"><span class="ln">12</span><span class="cl">  - 檔案數量合理（1-3 個）
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl">  不合格範例：
</span></span><span class="line"><span class="ln">15</span><span class="cl">  ### 步驟
</span></span><span class="line"><span class="ln">16</span><span class="cl">  1. 修改書籍詳情相關檔案
</span></span><span class="line"><span class="ln">17</span><span class="cl">  2. 實作相關邏輯
</span></span><span class="line"><span class="ln">18</span><span class="cl">  3. 更新 UI
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl">  檔案範圍模糊：
</span></span><span class="line"><span class="ln">21</span><span class="cl">  - 「相關檔案」沒有具體說明是哪些檔案
</span></span><span class="line"><span class="ln">22</span><span class="cl">  - 「相關邏輯」沒有說明在哪裡實作
</span></span><span class="line"><span class="ln">23</span><span class="cl">  - 「更新 UI」沒有說明哪個 UI 檔案</span></span></code></pre></div><h4 id="步驟-4-檢查驗收限定">步驟 4: 檢查驗收限定</h4>





<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><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">  - [ ] BookDetailController.loadBookDetail() 方法建立
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  - [ ] 正確注入 IGetBookDetailUseCase 依賴
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  - [ ] loadBookDetail() 正確呼叫 UseCase.execute()
</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">  - [ ] dart analyze 0 錯誤
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">  驗收限定在 Layer 2（Behavior）：
</span></span><span class="line"><span class="ln">11</span><span class="cl">  - 只驗證 Controller 層的職責
</span></span><span class="line"><span class="ln">12</span><span class="cl">  - 不驗證 UI 呈現（Layer 1）
</span></span><span class="line"><span class="ln">13</span><span class="cl">  - 不驗證業務邏輯（Layer 3）
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">  不合格範例：
</span></span><span class="line"><span class="ln">16</span><span class="cl">  ### 驗收條件
</span></span><span class="line"><span class="ln">17</span><span class="cl">  - [ ] 書籍詳情功能正常運作
</span></span><span class="line"><span class="ln">18</span><span class="cl">  - [ ] 使用者可以看到書籍資訊
</span></span><span class="line"><span class="ln">19</span><span class="cl">  - [ ] 資料正確儲存
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl">  驗收未限定在單一層級：
</span></span><span class="line"><span class="ln">22</span><span class="cl">  - 「功能正常運作」涵蓋所有層級
</span></span><span class="line"><span class="ln">23</span><span class="cl">  - 「使用者可以看到」是 UI 層（Layer 1）驗收
</span></span><span class="line"><span class="ln">24</span><span class="cl">  - 「資料正確儲存」是 Repository 層（Layer 5）驗收</span></span></code></pre></div><p><strong>檢測失敗處理</strong>:</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">C3 檢測失敗 → 執行重新定義步驟
</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">步驟 1: 明確層級定位
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  使用 v0.12.G.1 第 6.2 節檔案路徑分析法判斷層級
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  在標題加上 [Layer X] 標籤
</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">步驟 2: 重新定義職責
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  基於該層級的職責範圍重寫目標章節
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  明確說明「做什麼」「責任範圍」「不做什麼」
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">步驟 3: 明確檔案範圍
</span></span><span class="line"><span class="ln">12</span><span class="cl">  列出具體的檔案路徑（避免籠統描述）
</span></span><span class="line"><span class="ln">13</span><span class="cl">  確保檔案都屬於同一層級
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">步驟 4: 限定驗收條件
</span></span><span class="line"><span class="ln">16</span><span class="cl">  驗收條件只驗證該層級的職責
</span></span><span class="line"><span class="ln">17</span><span class="cl">  移除跨層級的驗收項目
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl">步驟 5: 重新檢測
</span></span><span class="line"><span class="ln">20</span><span class="cl">  確認所有 4 個必要元素都已補充</span></span></code></pre></div><p><strong>檢測範例（完整流程）</strong>:</p>
<p><strong>原始 Ticket（違反 C3 標準）</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket: 實作書籍詳情功能
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 背景
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>根據 UC-02 需求，需要實作書籍詳情功能。
</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="gu">### 目標
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span>實作書籍詳情功能，讓使用者可以查看書籍的完整資訊。
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 修改相關檔案
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">2.</span> 實作查詢邏輯
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">3.</span> 更新 UI 顯示
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 使用者可以看到書籍詳情
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">- [ ]</span> 資料正確顯示
</span></span><span class="line"><span class="ln">17</span><span class="cl">- [ ] 功能正常運作</span></span></code></pre></div><p><strong>C3 檢測執行</strong>:</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">步驟 1: 檢查層級標示
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  標題: &#34;實作書籍詳情功能&#34;
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  結果: 沒有 [Layer X] 標籤（不符合）
</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">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">步驟 2: 檢查職責描述
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  目標: &#34;實作書籍詳情功能，讓使用者可以查看書籍的完整資訊&#34;
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  結果: 職責定義模糊（不符合）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  問題:
</span></span><span class="line"><span class="ln">10</span><span class="cl">  - &#34;書籍詳情功能&#34;範圍太廣（UI? 邏輯? 資料？）
</span></span><span class="line"><span class="ln">11</span><span class="cl">  - 沒有明確說明職責範圍
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">步驟 3: 檢查檔案範圍
</span></span><span class="line"><span class="ln">14</span><span class="cl">  步驟: &#34;修改相關檔案&#34;
</span></span><span class="line"><span class="ln">15</span><span class="cl">  結果: 檔案範圍模糊（不符合）
</span></span><span class="line"><span class="ln">16</span><span class="cl">  問題:
</span></span><span class="line"><span class="ln">17</span><span class="cl">  - &#34;相關檔案&#34;沒有具體說明
</span></span><span class="line"><span class="ln">18</span><span class="cl">  - 沒有明確的檔案路徑
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl">步驟 4: 檢查驗收限定
</span></span><span class="line"><span class="ln">21</span><span class="cl">  驗收條件: &#34;使用者可以看到書籍詳情&#34;、&#34;功能正常運作&#34;
</span></span><span class="line"><span class="ln">22</span><span class="cl">  結果: 驗收未限定在單一層級（不符合）
</span></span><span class="line"><span class="ln">23</span><span class="cl">  問題:
</span></span><span class="line"><span class="ln">24</span><span class="cl">  - &#34;使用者可以看到&#34;是 UI 層驗收
</span></span><span class="line"><span class="ln">25</span><span class="cl">  - &#34;功能正常運作&#34;涵蓋所有層級
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl">結論: Ambiguous Responsibility（必須重新定義）</span></span></code></pre></div><p><strong>重新定義執行</strong>:</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">步驟 1: 明確層級定位
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  分析 Ticket 內容 → 主要涉及 Controller 事件處理
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  判斷層級 → Layer 2（Behavior）
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  更新標題 → 加上 [Layer 2] 標籤
</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">步驟 2: 重新定義職責
</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">  - 明確說明「做什麼」
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  - 明確說明「責任範圍」
</span></span><span class="line"><span class="ln">10</span><span class="cl">  - 明確說明「不做什麼」
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">步驟 3: 明確檔案範圍
</span></span><span class="line"><span class="ln">13</span><span class="cl">  列出具體檔案路徑
</span></span><span class="line"><span class="ln">14</span><span class="cl">  確認都屬於 Layer 2
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">步驟 4: 限定驗收條件
</span></span><span class="line"><span class="ln">17</span><span class="cl">  只驗證 Layer 2 的職責
</span></span><span class="line"><span class="ln">18</span><span class="cl">  移除 UI 和業務邏輯驗收</span></span></code></pre></div><p><strong>修正後 Ticket（符合 C3 標準）</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket: [Layer 2] 實作書籍詳情 Controller 事件處理
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 背景
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>根據 UC-02 需求，需要實作 BookDetailController 的事件處理邏輯。
</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="gu">### 目標
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span>實作 BookDetailController 的 loadBookDetail 方法，
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">負責接收使用者的「查看書籍詳情」事件並呼叫 GetBookDetailUseCase。
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">職責範圍：
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">-</span> 接收使用者事件（如按鈕點擊）
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">-</span> 呼叫 GetBookDetailUseCase 獲取書籍資料
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">-</span> 將 UseCase 回傳的資料傳遞給 UI
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">-</span> 不包含業務邏輯實作（業務邏輯在 UseCase 層）
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 修改 <span class="sb">`lib/presentation/controllers/book_detail_controller.dart`</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">2.</span> 新增 <span class="sb">`loadBookDetail(String isbn)`</span> 方法
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">3.</span> 注入 <span class="sb">`IGetBookDetailUseCase`</span> 依賴
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">4.</span> 實作事件處理邏輯：
</span></span><span class="line"><span class="ln">22</span><span class="cl">   <span class="k">-</span> 呼叫 <span class="sb">`useCase.execute(isbn)`</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">   <span class="k">-</span> 處理回傳的 <span class="sb">`OperationResult&lt;Book&gt;`</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">   <span class="k">-</span> 更新 Controller 狀態（loading/success/error）
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="k">5.</span> 撰寫單元測試
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> BookDetailController.loadBookDetail() 方法建立在正確位置
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="k">- [ ]</span> 正確注入 IGetBookDetailUseCase 依賴
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="k">- [ ]</span> loadBookDetail() 正確呼叫 useCase.execute()
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="k">- [ ]</span> 正確處理 OperationResult（success/failure）
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="k">- [ ]</span> 事件處理邏輯正確（不包含業務邏輯）
</span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="k">- [ ]</span> 單元測試覆蓋率 &gt; 80%
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">35</span><span class="cl">
</span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="gu">### 依賴 Ticket
</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="gu"></span><span class="k">-</span> Ticket <span class="ni">#3:</span> 定義 IGetBookDetailUseCase 介面（必須先完成）
</span></span><span class="line"><span class="ln">38</span><span class="cl">
</span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="gu">### 參考文件
</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="gu"></span><span class="k">-</span> docs/app-requirements-spec.md <span class="ni">#UC</span>-02
</span></span><span class="line"><span class="ln">41</span><span class="cl">- docs/work-logs/v0.12.8-design-decisions.md <span class="ni">#決策2</span></span></span></code></pre></div><p><strong>修正後重新檢測</strong>:</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">步驟 1: 檢查層級標示
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  標題: &#34;[Layer 2] 實作書籍詳情 Controller 事件處理&#34;
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  結果: 有 [Layer 2] 標籤（符合）
</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">步驟 2: 檢查職責描述
</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">  - 明確說明「接收事件」「呼叫 UseCase」
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  - 明確說明「不包含業務邏輯」
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">步驟 3: 檢查檔案範圍
</span></span><span class="line"><span class="ln">12</span><span class="cl">  步驟: 明確列出檔案路徑
</span></span><span class="line"><span class="ln">13</span><span class="cl">  結果: 檔案範圍明確（符合）
</span></span><span class="line"><span class="ln">14</span><span class="cl">  - `lib/presentation/controllers/book_detail_controller.dart`
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">步驟 4: 檢查驗收限定
</span></span><span class="line"><span class="ln">17</span><span class="cl">  驗收條件: 限定在 Layer 2 職責
</span></span><span class="line"><span class="ln">18</span><span class="cl">  結果: 驗收限定正確（符合）
</span></span><span class="line"><span class="ln">19</span><span class="cl">  - 只驗證 Controller 層的職責
</span></span><span class="line"><span class="ln">20</span><span class="cl">  - 不包含 UI 或業務邏輯驗收
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl">結論: 通過 C3 檢測</span></span></code></pre></div><p><strong>改善效果</strong>:</p>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>原始 Ticket</th>
          <th>修正後 Ticket</th>
          <th>改善</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>層級標示</strong></td>
          <td>無（不合格）</td>
          <td>[Layer 2]（合格）</td>
          <td>明確定位</td>
      </tr>
      <tr>
          <td><strong>職責描述</strong></td>
          <td>模糊（不合格）</td>
          <td>明確定義範圍和邊界（合格）</td>
          <td>職責清晰</td>
      </tr>
      <tr>
          <td><strong>檔案範圍</strong></td>
          <td>&ldquo;相關檔案&rdquo;（不合格）</td>
          <td>具體檔案路徑（合格）</td>
          <td>影響範圍明確</td>
      </tr>
      <tr>
          <td><strong>驗收限定</strong></td>
          <td>跨層級（不合格）</td>
          <td>限定在 Layer 2（合格）</td>
          <td>驗收標準清晰</td>
      </tr>
      <tr>
          <td><strong>可執行性</strong></td>
          <td>低（不合格）</td>
          <td>高（合格）</td>
          <td>開發者可直接執行</td>
      </tr>
  </tbody>
</table>
<hr>
<h4 id="244-c2-incomplete-ticket-檢測">2.4.4 C2. Incomplete Ticket 檢測</h4>
<p><strong>定義</strong>（引用 v0.12.G.2 第 2.3.3 節 Incomplete Ticket 定義）:</p>
<p>Incomplete Ticket 是指 Ticket 內容缺失關鍵元素，導致開發者無法明確理解需求、驗收標準或測試方法。</p>
<p><strong>必要元素檢測清單</strong>:</p>
<table>
  <thead>
      <tr>
          <th>元素</th>
          <th>檢查內容</th>
          <th>通過標準</th>
          <th>重要性</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>驗收條件</strong></td>
          <td>是否有「### 驗收條件」章節</td>
          <td>至少 3 個可驗證的驗收項目</td>
          <td>必要</td>
      </tr>
      <tr>
          <td><strong>測試規劃</strong></td>
          <td>步驟中是否包含測試</td>
          <td>明確列出測試檔案和測試項目</td>
          <td>必要</td>
      </tr>
      <tr>
          <td><strong>工作日誌規劃</strong></td>
          <td>是否規劃工作日誌檔案</td>
          <td>明確工作日誌檔案名稱和記錄內容</td>
          <td>必要</td>
      </tr>
      <tr>
          <td><strong>參考文件</strong></td>
          <td>是否連結需求規格或設計文件</td>
          <td>至少 1 個有效的文件連結</td>
          <td>必要</td>
      </tr>
  </tbody>
</table>
<p><strong>檢測方法</strong>:</p>
<h5 id="步驟-1-檢查驗收條件完整性">步驟 1: 檢查驗收條件完整性</h5>





<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 內容
</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">  ├─ Yes → 檢查驗收項目數量
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  │         ├─ ≥ 3 個 → 通過
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  │         └─ &lt; 3 個 → 失敗（驗收條件不足）
</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">  └─ No → 失敗（缺少驗收條件章節）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">驗收條件品質檢查：
</span></span><span class="line"><span class="ln">11</span><span class="cl">  ├─ 是否可量化驗證？（避免模糊描述）
</span></span><span class="line"><span class="ln">12</span><span class="cl">  ├─ 是否限定在該層級？（不跨層級驗收）
</span></span><span class="line"><span class="ln">13</span><span class="cl">  └─ 是否涵蓋功能性、品質性、整合性？</span></span></code></pre></div><p><strong>範例</strong>:</p>
<p><strong>合格驗收條件</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> <span class="sb">`IBookRepository`</span> 介面檔案建立在 <span class="sb">`lib/domains/library/repositories/`</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="k">- [ ]</span> <span class="sb">`getBookByIsbn(String isbn)`</span> 方法簽名完整且明確
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="k">- [ ]</span> 方法包含完整的文檔註解（參數、回傳值、異常）
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="k">- [ ]</span> 測試覆蓋率 &gt; 80%
</span></span><span class="line"><span class="ln">7</span><span class="cl">- [ ] 不破壞既有功能（回歸測試通過）</span></span></code></pre></div><p><strong>不合格驗收條件</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 功能可以正常運作
</span></span><span class="line"><span class="ln">3</span><span class="cl">- [ ] 程式碼品質良好</span></span></code></pre></div><p>問題：驗收條件過於模糊，無法量化驗證</p>
<h4 id="步驟-2-檢查測試規劃完整性">步驟 2: 檢查測試規劃完整性</h4>





<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><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">  ├─ Yes → 檢查測試內容
</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></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">  │         └─ 通過
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  │
</span></span><span class="line"><span class="ln">10</span><span class="cl">  └─ No → 失敗（缺少測試規劃）
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">測試規劃品質檢查：
</span></span><span class="line"><span class="ln">13</span><span class="cl">  ├─ 是否包含單元測試？
</span></span><span class="line"><span class="ln">14</span><span class="cl">  ├─ 是否包含整合測試？（如需要）
</span></span><span class="line"><span class="ln">15</span><span class="cl">  └─ 是否定義測試覆蓋率標準？</span></span></code></pre></div><p><strong>範例</strong>:</p>
<p><strong>合格測試規劃</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gu"></span><span class="k">4.</span> 撰寫 <span class="sb">`test/domain/repositories/book_repository_test.dart`</span> 單元測試
</span></span><span class="line"><span class="ln">3</span><span class="cl">   <span class="k">-</span> 測試 <span class="sb">`getBookByIsbn`</span> 正常流程（書籍存在）
</span></span><span class="line"><span class="ln">4</span><span class="cl">   <span class="k">-</span> 測試書籍不存在異常處理
</span></span><span class="line"><span class="ln">5</span><span class="cl">   <span class="k">-</span> 測試 ISBN 格式錯誤異常處理
</span></span><span class="line"><span class="ln">6</span><span class="cl">   <span class="k">-</span> 測試網路錯誤異常處理
</span></span><span class="line"><span class="ln">7</span><span class="cl">5. 確保測試覆蓋率 &gt; 80%</span></span></code></pre></div><p><strong>不合格測試規劃</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gu"></span>4. 撰寫測試</span></span></code></pre></div><p>問題：沒有明確測試檔案路徑和測試項目</p>
<h4 id="步驟-3-檢查工作日誌規劃">步驟 3: 檢查工作日誌規劃</h4>





<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 內容
</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">  ├─ Yes → 檢查檔案名稱
</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">  │         │   格式: docs/work-logs/vX.Y.Z-feature-name.md
</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">  │
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  └─ No → 失敗（缺少工作日誌規劃）
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">命名規範檢查：
</span></span><span class="line"><span class="ln">12</span><span class="cl">  ├─ 是否包含版本號？
</span></span><span class="line"><span class="ln">13</span><span class="cl">  ├─ 是否描述性命名？
</span></span><span class="line"><span class="ln">14</span><span class="cl">  └─ 是否在 docs/work-logs/ 目錄下？</span></span></code></pre></div><p><strong>範例</strong>:</p>
<p><strong>合格工作日誌規劃</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gu">### 工作日誌
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gu"></span>檔案名稱: <span class="sb">`docs/work-logs/v0.12.8-book-repository-interface.md`</span>
</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"><span class="k">-</span> Ticket 執行過程
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="k">-</span> 設計決策和理由
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="k">-</span> 遇到的問題和解決方法
</span></span><span class="line"><span class="ln">8</span><span class="cl">- 測試結果和覆蓋率報告</span></span></code></pre></div><p><strong>不合格工作日誌規劃</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">（沒有工作日誌規劃）</span></span></code></pre></div><h4 id="步驟-4-檢查參考文件連結">步驟 4: 檢查參考文件連結</h4>





<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 內容
</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">  ├─ Yes → 檢查連結有效性
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  │         ├─ 至少 1 個連結（符合）
</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">  │
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  └─ No → 失敗（缺少參考文件）
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">連結品質檢查：
</span></span><span class="line"><span class="ln">12</span><span class="cl">  ├─ 是否連結需求規格？（建議）
</span></span><span class="line"><span class="ln">13</span><span class="cl">  ├─ 是否連結設計文件？（建議）
</span></span><span class="line"><span class="ln">14</span><span class="cl">  └─ 是否連結相關 Ticket？（如有依賴）</span></span></code></pre></div><p><strong>範例</strong>:</p>
<p><strong>合格參考文件</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gu">### 參考文件
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gu"></span><span class="k">-</span> <span class="sb">`docs/app-requirements-spec.md`</span> <span class="ni">#UC</span>-01 書籍查詢功能
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="k">-</span> <span class="sb">`docs/work-logs/v0.12.7-design-decisions.md`</span> <span class="ni">#決策1</span> Repository 設計模式
</span></span><span class="line"><span class="ln">4</span><span class="cl">- <span class="sb">`.claude/methodologies/layered-ticket-methodology.md`</span> 層級隔離原則</span></span></code></pre></div><p><strong>不合格參考文件</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">（沒有參考文件章節）</span></span></code></pre></div><p><strong>檢測流程完整範例</strong>:</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">Ticket #1: [Layer 5] 定義 IBookRepository 介面
</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">執行 C2. Incomplete Ticket 檢測
</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">【步驟 1】檢查驗收條件
</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">  ├─ 驗收項目數量: 6 個（符合）
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  ├─ 驗收條件可量化（符合）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  └─ 通過
</span></span><span class="line"><span class="ln">10</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">11</span><span class="cl">【步驟 2】檢查測試規劃
</span></span><span class="line"><span class="ln">12</span><span class="cl">  ├─ 步驟 4 包含測試（符合）
</span></span><span class="line"><span class="ln">13</span><span class="cl">  ├─ 測試檔案路徑明確（符合）
</span></span><span class="line"><span class="ln">14</span><span class="cl">  ├─ 測試項目完整（符合）
</span></span><span class="line"><span class="ln">15</span><span class="cl">  └─ 通過
</span></span><span class="line"><span class="ln">16</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">17</span><span class="cl">【步驟 3】檢查工作日誌規劃
</span></span><span class="line"><span class="ln">18</span><span class="cl">  ├─ 有「### 工作日誌」章節（符合）
</span></span><span class="line"><span class="ln">19</span><span class="cl">  ├─ 檔案名稱符合規範（符合）
</span></span><span class="line"><span class="ln">20</span><span class="cl">  └─ 通過
</span></span><span class="line"><span class="ln">21</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">22</span><span class="cl">【步驟 4】檢查參考文件
</span></span><span class="line"><span class="ln">23</span><span class="cl">  ├─ 有「### 參考文件」章節（符合）
</span></span><span class="line"><span class="ln">24</span><span class="cl">  ├─ 連結數量: 3 個（符合）
</span></span><span class="line"><span class="ln">25</span><span class="cl">  ├─ 連結格式正確（符合）
</span></span><span class="line"><span class="ln">26</span><span class="cl">  └─ 通過
</span></span><span class="line"><span class="ln">27</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">28</span><span class="cl">C2 檢測結論: 通過</span></span></code></pre></div><p><strong>修正方法</strong>:</p>
<h4 id="缺失項目-1-沒有驗收條件">缺失項目 1: 沒有驗收條件</h4>
<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">1. 新增「### 驗收條件」章節
</span></span><span class="line"><span class="ln">2</span><span class="cl">2. 撰寫 3-6 個可量化驗收項目
</span></span><span class="line"><span class="ln">3</span><span class="cl">3. 涵蓋三個維度:
</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">   - 品質性驗收（dart analyze、測試覆蓋率、文檔）
</span></span><span class="line"><span class="ln">6</span><span class="cl">   - 整合性驗收（整合測試、不破壞既有功能）</span></span></code></pre></div><h5 id="缺失項目-2-沒有測試規劃">缺失項目 2: 沒有測試規劃</h5>
<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">1. 在「### 步驟」章節新增測試步驟
</span></span><span class="line"><span class="ln">2</span><span class="cl">2. 明確列出測試檔案路徑
</span></span><span class="line"><span class="ln">3</span><span class="cl">3. 列出測試項目清單（正常流程 + 異常處理）
</span></span><span class="line"><span class="ln">4</span><span class="cl">4. 定義測試覆蓋率要求（建議 &gt; 80%）</span></span></code></pre></div><h6 id="缺失項目-3-沒有工作日誌規劃">缺失項目 3: 沒有工作日誌規劃</h6>
<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">1. 新增「### 工作日誌」章節
</span></span><span class="line"><span class="ln">2</span><span class="cl">2. 定義工作日誌檔案名稱
</span></span><span class="line"><span class="ln">3</span><span class="cl">   格式: docs/work-logs/vX.Y.Z-feature-name.md
</span></span><span class="line"><span class="ln">4</span><span class="cl">3. 說明記錄內容範圍</span></span></code></pre></div><h6 id="缺失項目-4-沒有參考文件">缺失項目 4: 沒有參考文件</h6>
<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">1. 新增「### 參考文件」章節
</span></span><span class="line"><span class="ln">2</span><span class="cl">2. 連結需求規格（必要）
</span></span><span class="line"><span class="ln">3</span><span class="cl">3. 連結設計文件（建議）
</span></span><span class="line"><span class="ln">4</span><span class="cl">4. 連結相關 Ticket（如有依賴）</span></span></code></pre></div><p><strong>完整修正範例</strong>:</p>
<p><strong>修正前（Incomplete Ticket）</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gu">## Ticket: 定義 IBookRepository 介面
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="gu">### 目標
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="gu"></span>定義書籍 Repository 的介面契約
</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="gu">### 步驟
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 建立介面檔案
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="k">2.</span> 定義方法簽名
</span></span><span class="line"><span class="ln">9</span><span class="cl">3. 撰寫文檔註解</span></span></code></pre></div><p><strong>修正後（Complete Ticket）</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket: [Layer 5] 定義 IBookRepository 介面
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 目標
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>定義書籍 Repository 的介面契約，規範 Domain 層與 Infrastructure 層的互動方式
</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="gu">### 步驟
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 建立 <span class="sb">`lib/domains/library/repositories/i_book_repository.dart`</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">2.</span> 定義 <span class="sb">`getBookByIsbn(String isbn)`</span> 方法簽名
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">3.</span> 定義 <span class="sb">`getAllBooks()`</span> 方法簽名
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">4.</span> 撰寫完整的文檔註解（參數、回傳值、異常）
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">5.</span> 撰寫 <span class="sb">`test/domain/repositories/book_repository_test.dart`</span> 單元測試
</span></span><span class="line"><span class="ln">12</span><span class="cl">   <span class="k">-</span> 測試 getBookByIsbn 正常流程
</span></span><span class="line"><span class="ln">13</span><span class="cl">   <span class="k">-</span> 測試書籍不存在異常處理
</span></span><span class="line"><span class="ln">14</span><span class="cl">   <span class="k">-</span> 測試 ISBN 格式錯誤異常處理
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">6.</span> 確保測試覆蓋率 &gt; 80%
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> <span class="sb">`IBookRepository`</span> 介面檔案建立在 <span class="sb">`lib/domains/library/repositories/`</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">- [ ]</span> <span class="sb">`getBookByIsbn(String isbn)`</span> 方法簽名完整且明確
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">- [ ]</span> 方法包含完整的文檔註解
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">- [ ]</span> 測試覆蓋率 &gt; 80%
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="k">- [ ]</span> 不破壞既有功能
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="gu">### 工作日誌
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="gu"></span>檔案名稱: <span class="sb">`docs/work-logs/v0.12.8-book-repository-interface.md`</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="gu">### 參考文件
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="gu"></span><span class="k">-</span> <span class="sb">`docs/app-requirements-spec.md`</span> <span class="ni">#UC</span>-01 書籍查詢功能
</span></span><span class="line"><span class="ln">30</span><span class="cl">- <span class="sb">`.claude/methodologies/layered-ticket-methodology.md`</span> 層級隔離原則</span></span></code></pre></div><hr>
<h4 id="245-品質閘門執行流程">2.4.5 品質閘門執行流程</h4>
<p><strong>完整執行流程</strong>:</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">Phase 1: 功能設計（lavender-interface-designer）
</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">Ticket 清單撰寫完成
</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">┌─────────────────────────────────────┐
</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">│   （lavender-interface-designer）    │
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">└─────────────────────────────────────┘
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">10</span><span class="cl">對每個 Ticket 執行檢測（按順序）
</span></span><span class="line"><span class="ln">11</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">12</span><span class="cl">┌─────────────────────────────────────┐
</span></span><span class="line"><span class="ln">13</span><span class="cl">│ 【檢測 1】C1. God Ticket 檢測        │
</span></span><span class="line"><span class="ln">14</span><span class="cl">│                                     │
</span></span><span class="line"><span class="ln">15</span><span class="cl">│ 檢查指標:                            │
</span></span><span class="line"><span class="ln">16</span><span class="cl">│  - 檔案數量 ≤ 10？                   │
</span></span><span class="line"><span class="ln">17</span><span class="cl">│  - 層級跨度 ≤ 2？                    │
</span></span><span class="line"><span class="ln">18</span><span class="cl">│  - 預估工時 ≤ 16h？                  │
</span></span><span class="line"><span class="ln">19</span><span class="cl">│                                     │
</span></span><span class="line"><span class="ln">20</span><span class="cl">│ 組合邏輯: 任一項目超標 = God Ticket   │
</span></span><span class="line"><span class="ln">21</span><span class="cl">└─────────────────────────────────────┘
</span></span><span class="line"><span class="ln">22</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">23</span><span class="cl">  ├─ 通過 → 繼續檢測 C3
</span></span><span class="line"><span class="ln">24</span><span class="cl">  └─ 失敗 → 執行拆分 → 重新檢測 C1
</span></span><span class="line"><span class="ln">25</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">26</span><span class="cl">┌─────────────────────────────────────┐
</span></span><span class="line"><span class="ln">27</span><span class="cl">│ 【檢測 2】C3. Ambiguous              │
</span></span><span class="line"><span class="ln">28</span><span class="cl">│           Responsibility 檢測         │
</span></span><span class="line"><span class="ln">29</span><span class="cl">│                                     │
</span></span><span class="line"><span class="ln">30</span><span class="cl">│ 檢查要素:                            │
</span></span><span class="line"><span class="ln">31</span><span class="cl">│  - 有層級標示 [Layer X]？            │
</span></span><span class="line"><span class="ln">32</span><span class="cl">│  - 職責描述明確？                    │
</span></span><span class="line"><span class="ln">33</span><span class="cl">│  - 檔案範圍明確？                    │
</span></span><span class="line"><span class="ln">34</span><span class="cl">│  - 驗收條件限定在該層？              │
</span></span><span class="line"><span class="ln">35</span><span class="cl">└─────────────────────────────────────┘
</span></span><span class="line"><span class="ln">36</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">37</span><span class="cl">  ├─ 通過 → 繼續檢測 C2
</span></span><span class="line"><span class="ln">38</span><span class="cl">  └─ 失敗 → 重新定義職責 → 重新檢測 C3
</span></span><span class="line"><span class="ln">39</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">40</span><span class="cl">┌─────────────────────────────────────┐
</span></span><span class="line"><span class="ln">41</span><span class="cl">│ 【檢測 3】C2. Incomplete Ticket 檢測 │
</span></span><span class="line"><span class="ln">42</span><span class="cl">│                                     │
</span></span><span class="line"><span class="ln">43</span><span class="cl">│ 檢查要素:                            │
</span></span><span class="line"><span class="ln">44</span><span class="cl">│  - 有驗收條件章節？                  │
</span></span><span class="line"><span class="ln">45</span><span class="cl">│  - 有測試規劃？                      │
</span></span><span class="line"><span class="ln">46</span><span class="cl">│  - 有工作日誌規劃？                  │
</span></span><span class="line"><span class="ln">47</span><span class="cl">│  - 有參考文件連結？                  │
</span></span><span class="line"><span class="ln">48</span><span class="cl">└─────────────────────────────────────┘
</span></span><span class="line"><span class="ln">49</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">50</span><span class="cl">  ├─ 通過 → Ticket 通過品質閘門
</span></span><span class="line"><span class="ln">51</span><span class="cl">  └─ 失敗 → 補充遺漏項目 → 重新檢測 C2
</span></span><span class="line"><span class="ln">52</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">53</span><span class="cl">所有 Ticket 都通過 C1/C2/C3？
</span></span><span class="line"><span class="ln">54</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">55</span><span class="cl">  ├─ Yes → 提交品質閘門報告給 PM
</span></span><span class="line"><span class="ln">56</span><span class="cl">  │           ↓
</span></span><span class="line"><span class="ln">57</span><span class="cl">  │         PM 審查
</span></span><span class="line"><span class="ln">58</span><span class="cl">  │           ↓
</span></span><span class="line"><span class="ln">59</span><span class="cl">  │         PM 批准 → 進入 Phase 2: 測試驗證
</span></span><span class="line"><span class="ln">60</span><span class="cl">  │
</span></span><span class="line"><span class="ln">61</span><span class="cl">  └─ No → 繼續修正未通過的 Ticket</span></span></code></pre></div><p><strong>檢測順序說明</strong>:</p>
<table>
  <thead>
      <tr>
          <th>順序</th>
          <th>檢測項目</th>
          <th>檢測原因</th>
          <th>失敗影響</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>1 C1</strong></td>
          <td>God Ticket</td>
          <td>檔案數和層級是最基礎的結構問題</td>
          <td>必須拆分，否則後續檢測無意義</td>
      </tr>
      <tr>
          <td><strong>2 C3</strong></td>
          <td>Ambiguous Responsibility</td>
          <td>職責定義是 Ticket 品質的核心</td>
          <td>必須重新定義，避免跨層級實作</td>
      </tr>
      <tr>
          <td><strong>3 C2</strong></td>
          <td>Incomplete Ticket</td>
          <td>完整性是 Ticket 可執行性的保證</td>
          <td>補充遺漏項目，確保開發者理解需求</td>
      </tr>
  </tbody>
</table>
<p><strong>檢測順序設計理由</strong>:</p>
<ul>
<li><strong>C1 優先</strong>: 檔案數和層級問題會影響職責定義，必須先解決結構問題</li>
<li><strong>C3 次之</strong>: 職責明確後才能評估驗收條件和測試規劃是否完整</li>
<li><strong>C2 最後</strong>: 完整性檢測依賴明確的職責定義</li>
</ul>
<p><strong>檢測失敗處理流程</strong>:</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><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">根據失敗類型選擇修正方法
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  ├─ C1 失敗 → 執行 Ticket 拆分
</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">  │             ├─ 按職責拆分（次要）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  │             └─ 按 TDD 階段拆分（特殊）
</span></span><span class="line"><span class="ln">10</span><span class="cl">  │
</span></span><span class="line"><span class="ln">11</span><span class="cl">  ├─ C3 失敗 → 重新定義職責
</span></span><span class="line"><span class="ln">12</span><span class="cl">  │             ├─ 確認主要職責層級
</span></span><span class="line"><span class="ln">13</span><span class="cl">  │             ├─ 加入層級標示 [Layer X]
</span></span><span class="line"><span class="ln">14</span><span class="cl">  │             ├─ 限定職責範圍
</span></span><span class="line"><span class="ln">15</span><span class="cl">  │             └─ 明確列出檔案
</span></span><span class="line"><span class="ln">16</span><span class="cl">  │
</span></span><span class="line"><span class="ln">17</span><span class="cl">  └─ C2 失敗 → 補充遺漏項目
</span></span><span class="line"><span class="ln">18</span><span class="cl">                ├─ 補充驗收條件
</span></span><span class="line"><span class="ln">19</span><span class="cl">                ├─ 補充測試規劃
</span></span><span class="line"><span class="ln">20</span><span class="cl">                ├─ 補充工作日誌規劃
</span></span><span class="line"><span class="ln">21</span><span class="cl">                └─ 補充參考文件連結
</span></span><span class="line"><span class="ln">22</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">23</span><span class="cl">修正完成
</span></span><span class="line"><span class="ln">24</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">25</span><span class="cl">重新執行失敗的檢測項目
</span></span><span class="line"><span class="ln">26</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">27</span><span class="cl">  ├─ 通過 → 繼續下一個檢測
</span></span><span class="line"><span class="ln">28</span><span class="cl">  └─ 失敗 → 再次修正（直到通過）</span></span></code></pre></div><p><strong>PM 審查標準</strong>:</p>
<p><strong>審查清單</strong>:</p>
<table>
  <thead>
      <tr>
          <th>審查項目</th>
          <th>檢查內容</th>
          <th>通過標準</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>品質閘門報告</strong></td>
          <td>每個 Ticket 是否都執行了 C1/C2/C3 檢測</td>
          <td>所有 Ticket 都有檢測記錄（合格）</td>
      </tr>
      <tr>
          <td><strong>檢測結果</strong></td>
          <td>所有 Ticket 是否都通過檢測</td>
          <td>所有 Ticket 標記為「通過」</td>
      </tr>
      <tr>
          <td><strong>修正記錄</strong></td>
          <td>檢測失敗的 Ticket 是否記錄修正過程</td>
          <td>修正過程完整記錄（問題 + 修正方法 + 結果）</td>
      </tr>
      <tr>
          <td><strong>Ticket 數量</strong></td>
          <td>拆分後的 Ticket 數量是否合理</td>
          <td>不過度拆分（&lt; 10 個）、不過度合併（&gt; 1 個）</td>
      </tr>
      <tr>
          <td><strong>依賴關係</strong></td>
          <td>Ticket 間的依賴關係是否明確</td>
          <td>依賴順序符合架構層級（L5→L3→L2→L1）</td>
      </tr>
  </tbody>
</table>
<p><strong>PM 審查決策流程</strong>:</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">PM 收到品質閘門報告
</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">【檢查 1】品質閘門報告完整性
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  ├─ 所有 Ticket 都有 C1/C2/C3 檢測記錄？
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  │   ├─ Yes → 繼續檢查 2
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  │   └─ No → 要求 lavender 補充檢測
</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">【檢查 2】檢測結果正確性
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  ├─ 所有 Ticket 都標記為「通過」？
</span></span><span class="line"><span class="ln">10</span><span class="cl">  │   ├─ Yes → 繼續檢查 3
</span></span><span class="line"><span class="ln">11</span><span class="cl">  │   └─ No → 檢查未通過原因
</span></span><span class="line"><span class="ln">12</span><span class="cl">  │                ├─ 未修正 → 要求 lavender 修正
</span></span><span class="line"><span class="ln">13</span><span class="cl">  │                └─ 修正後仍不符合 → 要求重新設計
</span></span><span class="line"><span class="ln">14</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">15</span><span class="cl">【檢查 3】修正記錄完整性
</span></span><span class="line"><span class="ln">16</span><span class="cl">  ├─ 檢測失敗的 Ticket 是否記錄修正過程？
</span></span><span class="line"><span class="ln">17</span><span class="cl">  │   ├─ Yes → 繼續檢查 4
</span></span><span class="line"><span class="ln">18</span><span class="cl">  │   └─ No → 要求補充修正記錄
</span></span><span class="line"><span class="ln">19</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">20</span><span class="cl">【檢查 4】Ticket 數量合理性
</span></span><span class="line"><span class="ln">21</span><span class="cl">  ├─ 拆分後數量是否合理（1-10 個）？
</span></span><span class="line"><span class="ln">22</span><span class="cl">  │   ├─ Yes → 繼續檢查 5
</span></span><span class="line"><span class="ln">23</span><span class="cl">  │   └─ No → 要求調整拆分策略
</span></span><span class="line"><span class="ln">24</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">25</span><span class="cl">【檢查 5】依賴關係正確性
</span></span><span class="line"><span class="ln">26</span><span class="cl">  ├─ Ticket 依賴順序是否符合架構層級？
</span></span><span class="line"><span class="ln">27</span><span class="cl">  │   ├─ Yes → PM 批准 Ticket 清單
</span></span><span class="line"><span class="ln">28</span><span class="cl">  │   └─ No → 要求調整依賴順序
</span></span><span class="line"><span class="ln">29</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">30</span><span class="cl">PM 批准
</span></span><span class="line"><span class="ln">31</span><span class="cl">  ↓
</span></span><span class="line"><span class="ln">32</span><span class="cl">分派給開發者執行（進入 Phase 2）</span></span></code></pre></div><p><strong>工作日誌記錄範例</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">  1</span><span class="cl"><span class="gu">## Code Smell 品質閘門檢測
</span></span></span><span class="line"><span class="ln">  2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">  3</span><span class="cl"><span class="gu">### Ticket #1: [Layer 5] 定義 Book Entity
</span></span></span><span class="line"><span class="ln">  4</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">  5</span><span class="cl"><span class="gu">#### 檢測記錄（2025-10-11）
</span></span></span><span class="line"><span class="ln">  6</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">  7</span><span class="cl"><span class="gs">**C1. God Ticket 檢測**</span>:
</span></span><span class="line"><span class="ln">  8</span><span class="cl"><span class="k">-</span> 檔案數量: 2 個（符合）（book.dart + isbn.dart）
</span></span><span class="line"><span class="ln">  9</span><span class="cl"><span class="k">-</span> 層級跨度: 1 層（Layer 5）（符合）
</span></span><span class="line"><span class="ln"> 10</span><span class="cl"><span class="k">-</span> 預估工時: 4 小時（符合）
</span></span><span class="line"><span class="ln"> 11</span><span class="cl"><span class="k">-</span> 結論: 通過
</span></span><span class="line"><span class="ln"> 12</span><span class="cl">
</span></span><span class="line"><span class="ln"> 13</span><span class="cl"><span class="gs">**C3. Ambiguous Responsibility 檢測**</span>:
</span></span><span class="line"><span class="ln"> 14</span><span class="cl"><span class="k">-</span> 層級標示: [Layer 5]（符合）
</span></span><span class="line"><span class="ln"> 15</span><span class="cl"><span class="k">-</span> 職責描述: 明確（定義 Book Entity 和 ISBN Value Object）（符合）
</span></span><span class="line"><span class="ln"> 16</span><span class="cl"><span class="k">-</span> 檔案範圍: 明確列出 2 個檔案路徑（符合）
</span></span><span class="line"><span class="ln"> 17</span><span class="cl"><span class="k">-</span> 驗收限定: 限定在 Domain 層（符合）
</span></span><span class="line"><span class="ln"> 18</span><span class="cl"><span class="k">-</span> 結論: 通過
</span></span><span class="line"><span class="ln"> 19</span><span class="cl">
</span></span><span class="line"><span class="ln"> 20</span><span class="cl"><span class="gs">**C2. Incomplete Ticket 檢測**</span>:
</span></span><span class="line"><span class="ln"> 21</span><span class="cl"><span class="k">-</span> 驗收條件: 6 個驗收項目（符合）
</span></span><span class="line"><span class="ln"> 22</span><span class="cl"><span class="k">-</span> 測試規劃: 包含測試步驟和覆蓋率要求（符合）
</span></span><span class="line"><span class="ln"> 23</span><span class="cl"><span class="k">-</span> 工作日誌: 規劃檔案名稱（符合）
</span></span><span class="line"><span class="ln"> 24</span><span class="cl"><span class="k">-</span> 參考文件: 連結需求規格和設計文件（符合）
</span></span><span class="line"><span class="ln"> 25</span><span class="cl"><span class="k">-</span> 結論: 通過
</span></span><span class="line"><span class="ln"> 26</span><span class="cl">
</span></span><span class="line"><span class="ln"> 27</span><span class="cl"><span class="gs">**最終結論**</span>: Ticket <span class="ni">#1</span> 通過品質閘門
</span></span><span class="line"><span class="ln"> 28</span><span class="cl">
</span></span><span class="line"><span class="ln"> 29</span><span class="cl">---
</span></span><span class="line"><span class="ln"> 30</span><span class="cl">
</span></span><span class="line"><span class="ln"> 31</span><span class="cl"><span class="gu">### Ticket #2: [Layer 3/5] 實作書籍查詢功能（原始設計）
</span></span></span><span class="line"><span class="ln"> 32</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 33</span><span class="cl"><span class="gu">#### 檢測記錄（2025-10-11）
</span></span></span><span class="line"><span class="ln"> 34</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 35</span><span class="cl"><span class="gs">**C1. God Ticket 檢測**</span>:
</span></span><span class="line"><span class="ln"> 36</span><span class="cl"><span class="k">-</span> 檔案數量: 15 個（不符合）（超標，標準 ≤ 10）
</span></span><span class="line"><span class="ln"> 37</span><span class="cl"><span class="k">-</span> 層級跨度: 3 層（Layer 1/2/3/5）（不符合）（超標，標準 ≤ 2）
</span></span><span class="line"><span class="ln"> 38</span><span class="cl"><span class="k">-</span> 預估工時: 24 小時（不符合）（超標，標準 ≤ 16h）
</span></span><span class="line"><span class="ln"> 39</span><span class="cl"><span class="k">-</span> 結論: 失敗（3 個指標都超標）
</span></span><span class="line"><span class="ln"> 40</span><span class="cl">
</span></span><span class="line"><span class="ln"> 41</span><span class="cl"><span class="gs">**修正方法**</span>: 按層級拆分為 4 個 Ticket
</span></span><span class="line"><span class="ln"> 42</span><span class="cl"><span class="k">-</span> Ticket <span class="ni">#2a:</span> [Layer 5] 定義 Book Entity
</span></span><span class="line"><span class="ln"> 43</span><span class="cl"><span class="k">-</span> Ticket <span class="ni">#2b:</span> [Layer 3] 實作 GetBookUseCase
</span></span><span class="line"><span class="ln"> 44</span><span class="cl"><span class="k">-</span> Ticket <span class="ni">#2c:</span> [Layer 2] 實作 BookController
</span></span><span class="line"><span class="ln"> 45</span><span class="cl"><span class="k">-</span> Ticket <span class="ni">#2d:</span> [Layer 1] 實作 BookListWidget
</span></span><span class="line"><span class="ln"> 46</span><span class="cl">
</span></span><span class="line"><span class="ln"> 47</span><span class="cl"><span class="gs">**拆分後重新檢測**</span>:
</span></span><span class="line"><span class="ln"> 48</span><span class="cl">
</span></span><span class="line"><span class="ln"> 49</span><span class="cl"><span class="gs">**Ticket #2a 檢測**</span>:
</span></span><span class="line"><span class="ln"> 50</span><span class="cl"><span class="k">-</span> C1: 通過（2 個檔案、1 層、4h）
</span></span><span class="line"><span class="ln"> 51</span><span class="cl"><span class="k">-</span> C3: 通過（職責明確）
</span></span><span class="line"><span class="ln"> 52</span><span class="cl"><span class="k">-</span> C2: 通過（4 項必要元素齊全）
</span></span><span class="line"><span class="ln"> 53</span><span class="cl">
</span></span><span class="line"><span class="ln"> 54</span><span class="cl"><span class="gs">**Ticket #2b 檢測**</span>:
</span></span><span class="line"><span class="ln"> 55</span><span class="cl"><span class="k">-</span> C1: 通過（3 個檔案、1 層、6h）
</span></span><span class="line"><span class="ln"> 56</span><span class="cl"><span class="k">-</span> C3: 通過（職責明確）
</span></span><span class="line"><span class="ln"> 57</span><span class="cl"><span class="k">-</span> C2: 通過（4 項必要元素齊全）
</span></span><span class="line"><span class="ln"> 58</span><span class="cl">
</span></span><span class="line"><span class="ln"> 59</span><span class="cl"><span class="gs">**Ticket #2c 檢測**</span>:
</span></span><span class="line"><span class="ln"> 60</span><span class="cl"><span class="k">-</span> C1: 通過（2 個檔案、1 層、4h）
</span></span><span class="line"><span class="ln"> 61</span><span class="cl"><span class="k">-</span> C3: 通過（職責明確）
</span></span><span class="line"><span class="ln"> 62</span><span class="cl"><span class="k">-</span> C2: 通過（4 項必要元素齊全）
</span></span><span class="line"><span class="ln"> 63</span><span class="cl">
</span></span><span class="line"><span class="ln"> 64</span><span class="cl"><span class="gs">**Ticket #2d 檢測**</span>:
</span></span><span class="line"><span class="ln"> 65</span><span class="cl"><span class="k">-</span> C1: 通過（3 個檔案、1 層、5h）
</span></span><span class="line"><span class="ln"> 66</span><span class="cl"><span class="k">-</span> C3: 通過（職責明確）
</span></span><span class="line"><span class="ln"> 67</span><span class="cl"><span class="k">-</span> C2: 通過（4 項必要元素齊全）
</span></span><span class="line"><span class="ln"> 68</span><span class="cl">
</span></span><span class="line"><span class="ln"> 69</span><span class="cl"><span class="gs">**最終結論**</span>: 原 Ticket <span class="ni">#2</span> 拆分為 4 個 Ticket，全部通過品質閘門
</span></span><span class="line"><span class="ln"> 70</span><span class="cl">
</span></span><span class="line"><span class="ln"> 71</span><span class="cl">---
</span></span><span class="line"><span class="ln"> 72</span><span class="cl">
</span></span><span class="line"><span class="ln"> 73</span><span class="cl"><span class="gu">### PM 審查報告
</span></span></span><span class="line"><span class="ln"> 74</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 75</span><span class="cl"><span class="gs">**審查日期**</span>: 2025-10-11
</span></span><span class="line"><span class="ln"> 76</span><span class="cl"><span class="gs">**審查 Ticket 清單**</span>: v0.12.8 書籍查詢功能（5 個 Ticket）
</span></span><span class="line"><span class="ln"> 77</span><span class="cl">
</span></span><span class="line"><span class="ln"> 78</span><span class="cl"><span class="gs">**品質閘門檢測確認**</span>:
</span></span><span class="line"><span class="ln"> 79</span><span class="cl"><span class="k">-</span> Ticket <span class="ni">#1:</span> 通過 C1/C2/C3 檢測- Ticket <span class="ni">#2a:</span> 通過 C1/C2/C3 檢測（拆分後）- Ticket <span class="ni">#2b:</span> 通過 C1/C2/C3 檢測（拆分後）- Ticket <span class="ni">#2c:</span> 通過 C1/C2/C3 檢測（拆分後）- Ticket <span class="ni">#2d:</span> 通過 C1/C2/C3 檢測（拆分後）
</span></span><span class="line"><span class="ln"> 80</span><span class="cl">**拆分合理性**:
</span></span><span class="line"><span class="ln"> 81</span><span class="cl"><span class="k">-</span> 原 Ticket 數量: 2 個（1 個正常 + 1 個 God Ticket）
</span></span><span class="line"><span class="ln"> 82</span><span class="cl"><span class="k">-</span> 拆分後數量: 5 個
</span></span><span class="line"><span class="ln"> 83</span><span class="cl"><span class="k">-</span> 評估: 合理（符合）
</span></span><span class="line"><span class="ln"> 84</span><span class="cl"><span class="k">-</span> 理由: God Ticket 按層級拆分為 4 個，符合單層修改原則
</span></span><span class="line"><span class="ln"> 85</span><span class="cl">
</span></span><span class="line"><span class="ln"> 86</span><span class="cl"><span class="gs">**依賴關係檢查**</span>:
</span></span><span class="line"><span class="ln"> 87</span><span class="cl"><span class="k">-</span> 依賴順序: Ticket <span class="ni">#1</span> → <span class="ni">#2a</span> → <span class="ni">#2b</span> → <span class="ni">#2c</span> → <span class="ni">#2d</span>
</span></span><span class="line"><span class="ln"> 88</span><span class="cl"><span class="k">-</span> 評估: 正確（符合）
</span></span><span class="line"><span class="ln"> 89</span><span class="cl"><span class="k">-</span> 理由: 遵循架構層級順序（Layer 5 → Layer 3 → Layer 2 → Layer 1）
</span></span><span class="line"><span class="ln"> 90</span><span class="cl">
</span></span><span class="line"><span class="ln"> 91</span><span class="cl"><span class="gs">**審查結論**</span>:
</span></span><span class="line"><span class="ln"> 92</span><span class="cl"><span class="k">-</span> 批准 Ticket 清單- 可分派給開發者執行（進入 Phase 2）```
</span></span><span class="line"><span class="ln"> 93</span><span class="cl">
</span></span><span class="line"><span class="ln"> 94</span><span class="cl">---
</span></span><span class="line"><span class="ln"> 95</span><span class="cl">
</span></span><span class="line"><span class="ln"> 96</span><span class="cl"><span class="gu">#### 2.4.6 自動化檢測準備（v0.12.G.4）
</span></span></span><span class="line"><span class="ln"> 97</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 98</span><span class="cl">本節為 v0.12.G.4「代理人和 Hook 機制調整」準備自動化檢測規則設計。
</span></span><span class="line"><span class="ln"> 99</span><span class="cl">
</span></span><span class="line"><span class="ln">100</span><span class="cl"><span class="gs">**自動化檢測目標**</span>:
</span></span><span class="line"><span class="ln">101</span><span class="cl">
</span></span><span class="line"><span class="ln">102</span><span class="cl"><span class="k">-</span> 減少人工檢測工作量
</span></span><span class="line"><span class="ln">103</span><span class="cl"><span class="k">-</span> 提升檢測一致性
</span></span><span class="line"><span class="ln">104</span><span class="cl"><span class="k">-</span> 即時反饋 Ticket 品質問題
</span></span><span class="line"><span class="ln">105</span><span class="cl"><span class="k">-</span> 支援 lavender-interface-designer 執行檢測
</span></span><span class="line"><span class="ln">106</span><span class="cl">
</span></span><span class="line"><span class="ln">107</span><span class="cl"><span class="gs">**可自動化項目**</span>:
</span></span><span class="line"><span class="ln">108</span><span class="cl">
</span></span><span class="line"><span class="ln">109</span><span class="cl"><span class="gs">**C1. God Ticket 自動化檢測（可自動化程度: 80%）**</span>:
</span></span><span class="line"><span class="ln">110</span><span class="cl">
</span></span><span class="line"><span class="ln">111</span><span class="cl">| 檢測指標     | 自動化程度 | 自動化方法                             | 需要人工判斷     |
</span></span><span class="line"><span class="ln">112</span><span class="cl">| ------------ | ---------- | -------------------------------------- | ---------------- |
</span></span><span class="line"><span class="ln">113</span><span class="cl">| <span class="gs">**檔案數量**</span> | 100%（合格）    | 掃描步驟章節，提取檔案路徑，計算數量   | 無               |
</span></span><span class="line"><span class="ln">114</span><span class="cl">| <span class="gs">**層級跨度**</span> | 100%（合格）    | 使用 v0.12.G.1 第 6.2 節決策樹判斷層級 | 無               |
</span></span><span class="line"><span class="ln">115</span><span class="cl">| <span class="gs">**預估工時**</span> | 60%（需檢查）      | 根據步驟數量估算（<span class="p">&lt;</span> <span class="nt">10</span> <span class="na">步 </span><span class="o">=</span> <span class="s">2-4h）</span>     <span class="err">|</span> <span class="na">複雜度需人工確認</span> <span class="err">|</span>
</span></span><span class="line"><span class="ln">116</span><span class="cl">
</span></span><span class="line"><span class="ln">117</span><span class="cl"><span class="gs">**自動化規則設計**</span><span class="na">:</span>
</span></span><span class="line"><span class="ln">118</span><span class="cl">
</span></span><span class="line"><span class="ln">119</span><span class="cl"><span class="err">```</span><span class="na">python</span>
</span></span><span class="line"><span class="ln">120</span><span class="cl"><span class="na">def</span> <span class="na">check_god_ticket_automated</span><span class="err">(</span><span class="na">ticket_content:</span> <span class="na">str</span><span class="err">)</span> <span class="na">-</span><span class="p">&gt;</span> dict:
</span></span><span class="line"><span class="ln">121</span><span class="cl">    &#34;&#34;&#34;
</span></span><span class="line"><span class="ln">122</span><span class="cl">    自動化檢測 God Ticket
</span></span><span class="line"><span class="ln">123</span><span class="cl">
</span></span><span class="line"><span class="ln">124</span><span class="cl">    回傳格式:
</span></span><span class="line"><span class="ln">125</span><span class="cl">    {
</span></span><span class="line"><span class="ln">126</span><span class="cl">        &#34;file_count&#34;: int,
</span></span><span class="line"><span class="ln">127</span><span class="cl">        &#34;layer_span&#34;: int,
</span></span><span class="line"><span class="ln">128</span><span class="cl">        &#34;estimated_hours&#34;: int,
</span></span><span class="line"><span class="ln">129</span><span class="cl">        &#34;is_god_ticket&#34;: bool,
</span></span><span class="line"><span class="ln">130</span><span class="cl">        &#34;confidence&#34;: float  # 0.0-1.0
</span></span><span class="line"><span class="ln">131</span><span class="cl">    }
</span></span><span class="line"><span class="ln">132</span><span class="cl">    &#34;&#34;&#34;
</span></span><span class="line"><span class="ln">133</span><span class="cl">    # 1. 提取檔案路徑
</span></span><span class="line"><span class="ln">134</span><span class="cl">    file_paths = extract_file_paths(ticket_content)
</span></span><span class="line"><span class="ln">135</span><span class="cl">    file_count = len(file_paths)
</span></span><span class="line"><span class="ln">136</span><span class="cl">
</span></span><span class="line"><span class="ln">137</span><span class="cl">    # 2. 判斷層級跨度
</span></span><span class="line"><span class="ln">138</span><span class="cl">    layers = [determine_layer(path) for path in file_paths]
</span></span><span class="line"><span class="ln">139</span><span class="cl">    layer_span = max(layers) - min(layers) + 1
</span></span><span class="line"><span class="ln">140</span><span class="cl">
</span></span><span class="line"><span class="ln">141</span><span class="cl">    # 3. 預估工時（簡化估算）
</span></span><span class="line"><span class="ln">142</span><span class="cl">    step_count = count_steps(ticket_content)
</span></span><span class="line"><span class="ln">143</span><span class="cl">    estimated_hours = estimate_hours_by_steps(step_count)
</span></span><span class="line"><span class="ln">144</span><span class="cl">
</span></span><span class="line"><span class="ln">145</span><span class="cl">    # 4. 判斷是否為 God Ticket（任一項目超標）
</span></span><span class="line"><span class="ln">146</span><span class="cl">    is_god_ticket = (
</span></span><span class="line"><span class="ln">147</span><span class="cl">        file_count &gt; 10 or
</span></span><span class="line"><span class="ln">148</span><span class="cl">        layer_span &gt; 2 or
</span></span><span class="line"><span class="ln">149</span><span class="cl">        estimated_hours &gt; 16
</span></span><span class="line"><span class="ln">150</span><span class="cl">    )
</span></span><span class="line"><span class="ln">151</span><span class="cl">
</span></span><span class="line"><span class="ln">152</span><span class="cl">    # 5. 計算信心度
</span></span><span class="line"><span class="ln">153</span><span class="cl">    confidence = calculate_confidence(step_count, file_paths)
</span></span><span class="line"><span class="ln">154</span><span class="cl">
</span></span><span class="line"><span class="ln">155</span><span class="cl">    return {
</span></span><span class="line"><span class="ln">156</span><span class="cl">        &#34;file_count&#34;: file_count,
</span></span><span class="line"><span class="ln">157</span><span class="cl">        &#34;layer_span&#34;: layer_span,
</span></span><span class="line"><span class="ln">158</span><span class="cl">        &#34;estimated_hours&#34;: estimated_hours,
</span></span><span class="line"><span class="ln">159</span><span class="cl">        &#34;is_god_ticket&#34;: is_god_ticket,
</span></span><span class="line"><span class="ln">160</span><span class="cl">        &#34;confidence&#34;: confidence
</span></span><span class="line"><span class="ln">161</span><span class="cl">    }</span></span></code></pre></div><p><strong>C2. Incomplete Ticket 自動化檢測（可自動化程度: 90%）</strong>:</p>
<table>
  <thead>
      <tr>
          <th>檢測指標</th>
          <th>自動化程度</th>
          <th>自動化方法</th>
          <th>需要人工判斷</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>驗收條件</strong></td>
          <td>100%（合格）</td>
          <td>檢查章節存在性、驗收項目數量</td>
          <td>無</td>
      </tr>
      <tr>
          <td><strong>測試規劃</strong></td>
          <td>80%（需檢查）</td>
          <td>檢查測試檔案和關鍵字</td>
          <td>測試完整性需人工確認</td>
      </tr>
      <tr>
          <td><strong>工作日誌</strong></td>
          <td>100%（合格）</td>
          <td>檢查章節存在性、檔案名稱格式</td>
          <td>無</td>
      </tr>
      <tr>
          <td><strong>參考文件</strong></td>
          <td>100%（合格）</td>
          <td>檢查章節存在性、連結數量</td>
          <td>無</td>
      </tr>
  </tbody>
</table>
<p><strong>自動化規則設計</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">def</span> <span class="nf">check_incomplete_ticket_automated</span><span class="p">(</span><span class="n">ticket_content</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">dict</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="s2">    自動化檢測 Incomplete Ticket
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="s2">    回傳格式:
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="s2">    {
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="s2">        &#34;has_acceptance_criteria&#34;: bool,
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="s2">        &#34;acceptance_count&#34;: int,
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="s2">        &#34;has_test_plan&#34;: bool,
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="s2">        &#34;test_files&#34;: list,
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="s2">        &#34;has_work_log&#34;: bool,
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="s2">        &#34;work_log_file&#34;: str,
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="s2">        &#34;has_references&#34;: bool,
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="s2">        &#34;reference_count&#34;: int,
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="s2">        &#34;is_incomplete&#34;: bool
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="s2">    }
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="c1"># 1. 檢查驗收條件</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="n">has_acceptance</span> <span class="o">=</span> <span class="n">has_section</span><span class="p">(</span><span class="n">ticket_content</span><span class="p">,</span> <span class="s2">&#34;### 驗收條件&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="n">acceptance_count</span> <span class="o">=</span> <span class="n">count_acceptance_items</span><span class="p">(</span><span class="n">ticket_content</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="c1"># 2. 檢查測試規劃</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">    <span class="n">has_test_plan</span> <span class="o">=</span> <span class="n">has_test_keywords</span><span class="p">(</span><span class="n">ticket_content</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="n">test_files</span> <span class="o">=</span> <span class="n">extract_test_files</span><span class="p">(</span><span class="n">ticket_content</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl">    <span class="c1"># 3. 檢查工作日誌</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">    <span class="n">has_work_log</span> <span class="o">=</span> <span class="n">has_section</span><span class="p">(</span><span class="n">ticket_content</span><span class="p">,</span> <span class="s2">&#34;### 工作日誌&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">    <span class="n">work_log_file</span> <span class="o">=</span> <span class="n">extract_work_log_file</span><span class="p">(</span><span class="n">ticket_content</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">
</span></span><span class="line"><span class="ln">30</span><span class="cl">    <span class="c1"># 4. 檢查參考文件</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">    <span class="n">has_references</span> <span class="o">=</span> <span class="n">has_section</span><span class="p">(</span><span class="n">ticket_content</span><span class="p">,</span> <span class="s2">&#34;### 參考文件&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">    <span class="n">reference_count</span> <span class="o">=</span> <span class="n">count_references</span><span class="p">(</span><span class="n">ticket_content</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">
</span></span><span class="line"><span class="ln">34</span><span class="cl">    <span class="c1"># 5. 判斷是否為 Incomplete Ticket</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">    <span class="n">is_incomplete</span> <span class="o">=</span> <span class="ow">not</span> <span class="p">(</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">        <span class="n">has_acceptance</span> <span class="ow">and</span> <span class="n">acceptance_count</span> <span class="o">&gt;=</span> <span class="mi">3</span> <span class="ow">and</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">        <span class="n">has_test_plan</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">test_files</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="ow">and</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">        <span class="n">has_work_log</span> <span class="ow">and</span> <span class="n">work_log_file</span> <span class="ow">and</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">        <span class="n">has_references</span> <span class="ow">and</span> <span class="n">reference_count</span> <span class="o">&gt;=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">
</span></span><span class="line"><span class="ln">42</span><span class="cl">    <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl">        <span class="s2">&#34;has_acceptance_criteria&#34;</span><span class="p">:</span> <span class="n">has_acceptance</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">44</span><span class="cl">        <span class="s2">&#34;acceptance_count&#34;</span><span class="p">:</span> <span class="n">acceptance_count</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl">        <span class="s2">&#34;has_test_plan&#34;</span><span class="p">:</span> <span class="n">has_test_plan</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">46</span><span class="cl">        <span class="s2">&#34;test_files&#34;</span><span class="p">:</span> <span class="n">test_files</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">47</span><span class="cl">        <span class="s2">&#34;has_work_log&#34;</span><span class="p">:</span> <span class="n">has_work_log</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl">        <span class="s2">&#34;work_log_file&#34;</span><span class="p">:</span> <span class="n">work_log_file</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl">        <span class="s2">&#34;has_references&#34;</span><span class="p">:</span> <span class="n">has_references</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">50</span><span class="cl">        <span class="s2">&#34;reference_count&#34;</span><span class="p">:</span> <span class="n">reference_count</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">51</span><span class="cl">        <span class="s2">&#34;is_incomplete&#34;</span><span class="p">:</span> <span class="n">is_incomplete</span>
</span></span><span class="line"><span class="ln">52</span><span class="cl">    <span class="p">}</span></span></span></code></pre></div><p><strong>C3. Ambiguous Responsibility 自動化檢測（可自動化程度: 70%）</strong>:</p>
<table>
  <thead>
      <tr>
          <th>檢測指標</th>
          <th>自動化程度</th>
          <th>自動化方法</th>
          <th>需要人工判斷</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>層級標示</strong></td>
          <td>100%（合格）</td>
          <td>正則匹配 <code>\[Layer \d+\]</code></td>
          <td>無</td>
      </tr>
      <tr>
          <td><strong>職責描述</strong></td>
          <td>50%（需檢查）</td>
          <td>檢查字數、關鍵字</td>
          <td>明確性需人工確認</td>
      </tr>
      <tr>
          <td><strong>檔案範圍</strong></td>
          <td>100%（合格）</td>
          <td>檢查具體檔案路徑</td>
          <td>無</td>
      </tr>
      <tr>
          <td><strong>驗收限定</strong></td>
          <td>70%（需檢查）</td>
          <td>提取驗收條件的類別名稱</td>
          <td>層級一致性需人工確認</td>
      </tr>
  </tbody>
</table>
<p><strong>自動化規則設計</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">def</span> <span class="nf">check_ambiguous_responsibility_automated</span><span class="p">(</span><span class="n">ticket_content</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">dict</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="s2">    自動化檢測 Ambiguous Responsibility
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="s2">    回傳格式:
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="s2">    {
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="s2">        &#34;has_layer_tag&#34;: bool,
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="s2">        &#34;layer_number&#34;: int,
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="s2">        &#34;has_clear_responsibility&#34;: bool,
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="s2">        &#34;responsibility_length&#34;: int,
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="s2">        &#34;has_file_paths&#34;: bool,
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="s2">        &#34;file_count&#34;: int,
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="s2">        &#34;acceptance_limited_to_layer&#34;: bool,
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="s2">        &#34;is_ambiguous&#34;: bool,
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="s2">        &#34;confidence&#34;: float
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="s2">    }
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="c1"># 1. 檢查層級標示</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="n">has_layer_tag</span><span class="p">,</span> <span class="n">layer_number</span> <span class="o">=</span> <span class="n">extract_layer_tag</span><span class="p">(</span><span class="n">ticket_content</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="c1"># 2. 檢查職責描述（簡化檢查）</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="n">responsibility</span> <span class="o">=</span> <span class="n">extract_responsibility</span><span class="p">(</span><span class="n">ticket_content</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">    <span class="n">has_clear_responsibility</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">responsibility</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">20</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span><span class="line"><span class="ln">25</span><span class="cl">    <span class="c1"># 3. 檢查檔案範圍</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">    <span class="n">file_paths</span> <span class="o">=</span> <span class="n">extract_file_paths</span><span class="p">(</span><span class="n">ticket_content</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">    <span class="n">has_file_paths</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">file_paths</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">
</span></span><span class="line"><span class="ln">29</span><span class="cl">    <span class="c1"># 4. 檢查驗收限定（需要層級判斷）</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">    <span class="n">acceptance_items</span> <span class="o">=</span> <span class="n">extract_acceptance_items</span><span class="p">(</span><span class="n">ticket_content</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">    <span class="n">acceptance_layers</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">        <span class="n">determine_layer_from_acceptance</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">        <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">acceptance_items</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">    <span class="p">]</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">    <span class="n">acceptance_limited</span> <span class="o">=</span> <span class="nb">all</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">        <span class="n">layer</span> <span class="o">==</span> <span class="n">layer_number</span> <span class="k">for</span> <span class="n">layer</span> <span class="ow">in</span> <span class="n">acceptance_layers</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">
</span></span><span class="line"><span class="ln">39</span><span class="cl">    <span class="c1"># 5. 判斷是否為 Ambiguous Responsibility</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl">    <span class="n">is_ambiguous</span> <span class="o">=</span> <span class="ow">not</span> <span class="p">(</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">        <span class="n">has_layer_tag</span> <span class="ow">and</span>
</span></span><span class="line"><span class="ln">42</span><span class="cl">        <span class="n">has_clear_responsibility</span> <span class="ow">and</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl">        <span class="n">has_file_paths</span> <span class="ow">and</span>
</span></span><span class="line"><span class="ln">44</span><span class="cl">        <span class="n">acceptance_limited</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="ln">46</span><span class="cl">
</span></span><span class="line"><span class="ln">47</span><span class="cl">    <span class="c1"># 6. 計算信心度（職責描述需人工確認）</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl">    <span class="n">confidence</span> <span class="o">=</span> <span class="mf">0.7</span> <span class="k">if</span> <span class="n">has_clear_responsibility</span> <span class="k">else</span> <span class="mf">0.5</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl">
</span></span><span class="line"><span class="ln">50</span><span class="cl">    <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">51</span><span class="cl">        <span class="s2">&#34;has_layer_tag&#34;</span><span class="p">:</span> <span class="n">has_layer_tag</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">52</span><span class="cl">        <span class="s2">&#34;layer_number&#34;</span><span class="p">:</span> <span class="n">layer_number</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">53</span><span class="cl">        <span class="s2">&#34;has_clear_responsibility&#34;</span><span class="p">:</span> <span class="n">has_clear_responsibility</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">54</span><span class="cl">        <span class="s2">&#34;responsibility_length&#34;</span><span class="p">:</span> <span class="nb">len</span><span class="p">(</span><span class="n">responsibility</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">55</span><span class="cl">        <span class="s2">&#34;has_file_paths&#34;</span><span class="p">:</span> <span class="n">has_file_paths</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">56</span><span class="cl">        <span class="s2">&#34;file_count&#34;</span><span class="p">:</span> <span class="nb">len</span><span class="p">(</span><span class="n">file_paths</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">57</span><span class="cl">        <span class="s2">&#34;acceptance_limited_to_layer&#34;</span><span class="p">:</span> <span class="n">acceptance_limited</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">58</span><span class="cl">        <span class="s2">&#34;is_ambiguous&#34;</span><span class="p">:</span> <span class="n">is_ambiguous</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">59</span><span class="cl">        <span class="s2">&#34;confidence&#34;</span><span class="p">:</span> <span class="n">confidence</span>
</span></span><span class="line"><span class="ln">60</span><span class="cl">    <span class="p">}</span></span></span></code></pre></div><p><strong>需要人工判斷的項目</strong>:</p>
<table>
  <thead>
      <tr>
          <th>項目</th>
          <th>原因</th>
          <th>人工判斷者</th>
          <th>自動化支援</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Ticket 拆分合理性</strong></td>
          <td>需要業務知識和架構經驗</td>
          <td>PM 或架構師</td>
          <td>提供拆分建議</td>
      </tr>
      <tr>
          <td><strong>職責定義明確性</strong></td>
          <td>需要理解業務語意</td>
          <td>lavender-interface-designer</td>
          <td>檢測關鍵字</td>
      </tr>
      <tr>
          <td><strong>預估工時準確性</strong></td>
          <td>需要評估任務複雜度</td>
          <td>PM 或開發者</td>
          <td>根據步驟數量估算</td>
      </tr>
      <tr>
          <td><strong>依賴關係正確性</strong></td>
          <td>需要理解架構層級關係</td>
          <td>lavender-interface-designer</td>
          <td>檢查依賴順序</td>
      </tr>
  </tbody>
</table>
<p><strong>Hook 系統檢測規則（v0.12.G.4 規劃）</strong>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># Hook 觸發時機: Ticket 檔案修改時（PostEdit Hook）</span>
</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 class="k">def</span> <span class="nf">ticket_quality_gate_hook</span><span class="p">(</span><span class="n">ticket_file_path</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">dict</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="s2">&#34;&#34;&#34;
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="s2">    Ticket 品質閘門 Hook
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="s2">    執行時機:
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="s2">    - Ticket 檔案儲存時
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="s2">    - lavender-interface-designer 提交工作日誌時
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="s2">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="s2">    回傳格式:
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="s2">    {
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="s2">        &#34;passed&#34;: bool,
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="s2">        &#34;c1_result&#34;: dict,
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="s2">        &#34;c2_result&#34;: dict,
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="s2">        &#34;c3_result&#34;: dict,
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="s2">        &#34;suggestions&#34;: list,
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="s2">        &#34;blocking_issues&#34;: list
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="s2">    }
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="s2">    &#34;&#34;&#34;</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="c1"># 讀取 Ticket 內容</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="n">ticket_content</span> <span class="o">=</span> <span class="n">read_ticket_file</span><span class="p">(</span><span class="n">ticket_file_path</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="c1"># 執行自動化檢測</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">    <span class="n">c1_result</span> <span class="o">=</span> <span class="n">check_god_ticket_automated</span><span class="p">(</span><span class="n">ticket_content</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">    <span class="n">c2_result</span> <span class="o">=</span> <span class="n">check_incomplete_ticket_automated</span><span class="p">(</span><span class="n">ticket_content</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">    <span class="n">c3_result</span> <span class="o">=</span> <span class="n">check_ambiguous_responsibility_automated</span><span class="p">(</span><span class="n">ticket_content</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">
</span></span><span class="line"><span class="ln">29</span><span class="cl">    <span class="c1"># 判斷是否通過</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">    <span class="n">passed</span> <span class="o">=</span> <span class="ow">not</span> <span class="p">(</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">        <span class="n">c1_result</span><span class="p">[</span><span class="s2">&#34;is_god_ticket&#34;</span><span class="p">]</span> <span class="ow">or</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">        <span class="n">c2_result</span><span class="p">[</span><span class="s2">&#34;is_incomplete&#34;</span><span class="p">]</span> <span class="ow">or</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">        <span class="n">c3_result</span><span class="p">[</span><span class="s2">&#34;is_ambiguous&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">    <span class="p">)</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">
</span></span><span class="line"><span class="ln">36</span><span class="cl">    <span class="c1"># 生成建議</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">    <span class="n">suggestions</span> <span class="o">=</span> <span class="n">generate_suggestions</span><span class="p">(</span><span class="n">c1_result</span><span class="p">,</span> <span class="n">c2_result</span><span class="p">,</span> <span class="n">c3_result</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">
</span></span><span class="line"><span class="ln">39</span><span class="cl">    <span class="c1"># 生成阻斷問題（需要立即修正）</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl">    <span class="n">blocking_issues</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">    <span class="k">if</span> <span class="n">c1_result</span><span class="p">[</span><span class="s2">&#34;is_god_ticket&#34;</span><span class="p">]:</span>
</span></span><span class="line"><span class="ln">42</span><span class="cl">        <span class="n">blocking_issues</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl">            <span class="sa">f</span><span class="s2">&#34;God Ticket 檢測失敗: &#34;</span>
</span></span><span class="line"><span class="ln">44</span><span class="cl">            <span class="sa">f</span><span class="s2">&#34;檔案數=</span><span class="si">{</span><span class="n">c1_result</span><span class="p">[</span><span class="s1">&#39;file_count&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">, &#34;</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl">            <span class="sa">f</span><span class="s2">&#34;層級跨度=</span><span class="si">{</span><span class="n">c1_result</span><span class="p">[</span><span class="s1">&#39;layer_span&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">, &#34;</span>
</span></span><span class="line"><span class="ln">46</span><span class="cl">            <span class="sa">f</span><span class="s2">&#34;工時=</span><span class="si">{</span><span class="n">c1_result</span><span class="p">[</span><span class="s1">&#39;estimated_hours&#39;</span><span class="p">]</span><span class="si">}</span><span class="s2">h&#34;</span>
</span></span><span class="line"><span class="ln">47</span><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl">    <span class="k">if</span> <span class="n">c2_result</span><span class="p">[</span><span class="s2">&#34;is_incomplete&#34;</span><span class="p">]:</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl">        <span class="n">blocking_issues</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">50</span><span class="cl">            <span class="sa">f</span><span class="s2">&#34;Incomplete Ticket 檢測失敗: 缺少必要元素&#34;</span>
</span></span><span class="line"><span class="ln">51</span><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="ln">52</span><span class="cl">    <span class="k">if</span> <span class="n">c3_result</span><span class="p">[</span><span class="s2">&#34;is_ambiguous&#34;</span><span class="p">]:</span>
</span></span><span class="line"><span class="ln">53</span><span class="cl">        <span class="n">blocking_issues</span><span class="o">.</span><span class="n">append</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">54</span><span class="cl">            <span class="sa">f</span><span class="s2">&#34;Ambiguous Responsibility 檢測失敗: 職責不明確&#34;</span>
</span></span><span class="line"><span class="ln">55</span><span class="cl">        <span class="p">)</span>
</span></span><span class="line"><span class="ln">56</span><span class="cl">
</span></span><span class="line"><span class="ln">57</span><span class="cl">    <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">58</span><span class="cl">        <span class="s2">&#34;passed&#34;</span><span class="p">:</span> <span class="n">passed</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">59</span><span class="cl">        <span class="s2">&#34;c1_result&#34;</span><span class="p">:</span> <span class="n">c1_result</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">60</span><span class="cl">        <span class="s2">&#34;c2_result&#34;</span><span class="p">:</span> <span class="n">c2_result</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">61</span><span class="cl">        <span class="s2">&#34;c3_result&#34;</span><span class="p">:</span> <span class="n">c3_result</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">62</span><span class="cl">        <span class="s2">&#34;suggestions&#34;</span><span class="p">:</span> <span class="n">suggestions</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">63</span><span class="cl">        <span class="s2">&#34;blocking_issues&#34;</span><span class="p">:</span> <span class="n">blocking_issues</span>
</span></span><span class="line"><span class="ln">64</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">65</span><span class="cl">
</span></span><span class="line"><span class="ln">66</span><span class="cl"><span class="c1"># Hook 輸出範例:</span>
</span></span><span class="line"><span class="ln">67</span><span class="cl"><span class="c1"># - 品質閘門通過 → 記錄日誌，允許繼續</span>
</span></span><span class="line"><span class="ln">68</span><span class="cl"><span class="c1"># - 品質閘門失敗 → 阻止提交，提供修正建議</span></span></span></code></pre></div><p><strong>v0.12.G.4 實作準備清單</strong>:</p>
<ul>
<li><input disabled="" type="checkbox"> 實作檔案路徑提取函式</li>
<li><input disabled="" type="checkbox"> 實作層級判斷函式（基於 v0.12.G.1 第 6.2 節）</li>
<li><input disabled="" type="checkbox"> 實作步驟數量計算和工時估算函式</li>
<li><input disabled="" type="checkbox"> 實作章節檢測函式</li>
<li><input disabled="" type="checkbox"> 實作驗收條件提取和驗證函式</li>
<li><input disabled="" type="checkbox"> 實作測試檔案檢測函式</li>
<li><input disabled="" type="checkbox"> 實作 Hook 整合（PostEdit Hook）</li>
<li><input disabled="" type="checkbox"> 撰寫 Hook 測試（模擬 Ticket 檔案）</li>
<li><input disabled="" type="checkbox"> 建立檢測結果日誌機制</li>
<li><input disabled="" type="checkbox"> 提供修正建議生成器</li>
</ul>
<hr>
<h3 id="25-ticket-大小標準和範例">2.5 Ticket 大小標準和範例</h3>
<p><strong>Ticket 大小標準總結</strong>：</p>
<table>
  <thead>
      <tr>
          <th>類型</th>
          <th>職責</th>
          <th>檔案</th>
          <th>測試</th>
          <th>行數</th>
          <th>適用場景</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Interface 定義</strong></td>
          <td>1 個</td>
          <td>1 個</td>
          <td>0 個</td>
          <td>&lt; 30 行</td>
          <td>定義契約</td>
      </tr>
      <tr>
          <td><strong>Value Object 實作</strong></td>
          <td>1 個</td>
          <td>1 個</td>
          <td>2-3 個</td>
          <td>30-40 行</td>
          <td>簡單邏輯</td>
      </tr>
      <tr>
          <td><strong>Repository 實作</strong></td>
          <td>2-3 個</td>
          <td>1 個</td>
          <td>4-6 個</td>
          <td>50-80 行</td>
          <td>CRUD 操作</td>
      </tr>
      <tr>
          <td><strong>Use Case 實作</strong></td>
          <td>2-3 個</td>
          <td>2-3 個</td>
          <td>5-8 個</td>
          <td>40-70 行</td>
          <td>業務邏輯</td>
      </tr>
      <tr>
          <td><strong>整合驗證</strong></td>
          <td>2-3 個</td>
          <td>2-3 個</td>
          <td>3-5 個</td>
          <td>30-50 行</td>
          <td>端到端測試</td>
      </tr>
  </tbody>
</table>
<h3 id="26-拆分決策樹">2.6 拆分決策樹</h3>
<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><span class="line"><span class="ln"> 2</span><span class="cl">│
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">├─ 是否為單一職責且檔案數 ≤ 1？
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">│  └─ Yes → 建立單一 Ticket（簡單）
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">│  └─ No  → 繼續評估
</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">├─ 可按 Clean Architecture 分層拆分？
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">│  └─ Yes → 拆分為 4 種 Ticket
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">│  │        1. Interface 定義 Ticket
</span></span><span class="line"><span class="ln">10</span><span class="cl">│  │        2. 測試驗證 Ticket
</span></span><span class="line"><span class="ln">11</span><span class="cl">│  │        3. 具體實作 Ticket
</span></span><span class="line"><span class="ln">12</span><span class="cl">│  │        4. 整合連接 Ticket
</span></span><span class="line"><span class="ln">13</span><span class="cl">│  └─ No  → 繼續評估
</span></span><span class="line"><span class="ln">14</span><span class="cl">│
</span></span><span class="line"><span class="ln">15</span><span class="cl">├─ 可按功能模組拆分？
</span></span><span class="line"><span class="ln">16</span><span class="cl">│  └─ Yes → 拆分為多個功能 Ticket
</span></span><span class="line"><span class="ln">17</span><span class="cl">│  │        範例：User 模組、Book 模組、Order 模組
</span></span><span class="line"><span class="ln">18</span><span class="cl">│  └─ No  → 繼續評估
</span></span><span class="line"><span class="ln">19</span><span class="cl">│
</span></span><span class="line"><span class="ln">20</span><span class="cl">├─ 可按步驟順序拆分？
</span></span><span class="line"><span class="ln">21</span><span class="cl">│  └─ Yes → 拆分為多個步驟 Ticket
</span></span><span class="line"><span class="ln">22</span><span class="cl">│  │        範例：步驟1 資料準備、步驟2 邏輯處理、步驟3 結果輸出
</span></span><span class="line"><span class="ln">23</span><span class="cl">│  └─ No  → 繼續評估
</span></span><span class="line"><span class="ln">24</span><span class="cl">│
</span></span><span class="line"><span class="ln">25</span><span class="cl">└─ 可按 CRUD 操作拆分？
</span></span><span class="line"><span class="ln">26</span><span class="cl">   └─ Yes → 拆分為 Create、Read、Update、Delete Ticket
</span></span><span class="line"><span class="ln">27</span><span class="cl">   └─ No  → 重新分析任務結構或尋求 PM 協助</span></span></code></pre></div><h3 id="27-ticket-拆分檢查清單">2.7 Ticket 拆分檢查清單</h3>
<p><strong>拆分前檢查</strong>（5 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 任務描述是否清晰明確？</li>
<li><input disabled="" type="checkbox"> 是否有明確的需求來源？</li>
<li><input disabled="" type="checkbox"> 是否理解任務的技術實作方式？</li>
<li><input disabled="" type="checkbox"> 是否識別所有依賴關係？</li>
<li><input disabled="" type="checkbox"> 是否評估職責數量、檔案數、測試數、程式碼行數？</li>
</ul>
<p><strong>拆分策略檢查</strong>（5 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 是否嘗試按 Clean Architecture 分層拆分？</li>
<li><input disabled="" type="checkbox"> 是否嘗試按功能模組拆分？</li>
<li><input disabled="" type="checkbox"> 是否嘗試按步驟順序拆分？</li>
<li><input disabled="" type="checkbox"> 拆分後的 Ticket 是否都可獨立驗收？</li>
<li><input disabled="" type="checkbox"> 拆分後的 Ticket 是否符合複雜度標準（職責、檔案、測試、行數）？</li>
</ul>
<p><strong>拆分後檢查</strong>（4 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 每個 Ticket 是否都有明確的驗收條件？</li>
<li><input disabled="" type="checkbox"> Ticket 間的依賴關係是否明確標註？</li>
<li><input disabled="" type="checkbox"> 是否有並行執行的機會？</li>
<li><input disabled="" type="checkbox"> 所有 Ticket 加總是否完整覆蓋原任務？</li>
</ul>
<hr>
<h2 id="第三章ticket-生命週期管理">第三章：Ticket 生命週期管理</h2>
<h3 id="31-ticket-狀態定義">3.1 Ticket 狀態定義</h3>
<p>Ticket 從建立到關閉經歷 4 個狀態：</p>
<h4 id="狀態-1待執行pending">狀態 1：待執行（Pending）</h4>
<p><strong>定義</strong>：Ticket 已建立並通過準備度檢查，等待開發者執行。</p>
<p><strong>進入條件</strong>：</p>
<ul>
<li>Ticket 建立完成</li>
<li>驗收條件明確</li>
<li>依賴 Ticket 已完成（如適用）</li>
</ul>
<p><strong>可執行動作</strong>：</p>
<ul>
<li>指派開發者</li>
<li>調整優先級</li>
<li>開始執行（轉為「進行中」）</li>
</ul>
<p><strong>流程特性</strong>：快速流轉至 In Progress</p>
<h4 id="狀態-2進行中in-progress">狀態 2：進行中（In Progress）</h4>
<p><strong>定義</strong>：開發者正在執行 Ticket。</p>
<p><strong>進入條件</strong>：</p>
<ul>
<li>開發者開始執行</li>
<li>標記 Ticket 為「進行中」</li>
</ul>
<p><strong>可執行動作</strong>：</p>
<ul>
<li>持續更新進度</li>
<li>遇到問題記錄到 Ticket 日誌</li>
<li>完成後提交 review（轉為「Review 中」）</li>
<li>發現問題暫停（轉回「待執行」）</li>
</ul>
<p><strong>流程特性</strong>：快速完成並進入 Review</p>
<h4 id="狀態-3review-中in-review">狀態 3：Review 中（In Review）</h4>
<p><strong>定義</strong>：Ticket 已完成執行，等待 review 驗收。</p>
<p><strong>進入條件</strong>：</p>
<ul>
<li>開發者認為已完成</li>
<li>提交 review 請求</li>
</ul>
<p><strong>可執行動作</strong>：</p>
<ul>
<li>Review 檢查驗收條件</li>
<li>通過驗收（轉為「已完成」）</li>
<li>發現問題（轉回「進行中」，建立修正 Ticket）</li>
</ul>
<p><strong>流程特性</strong>：快速 Review 並決定結果</p>
<h4 id="狀態-4已完成completed">狀態 4：已完成（Completed）</h4>
<p><strong>定義</strong>：Ticket 通過 review 驗收，已關閉。</p>
<p><strong>進入條件</strong>：</p>
<ul>
<li>所有驗收條件滿足</li>
<li>Review 通過</li>
<li>相關測試 100% 通過</li>
</ul>
<p><strong>可執行動作</strong>：</p>
<ul>
<li>更新主版本日誌 Ticket 索引</li>
<li>更新 todolist 任務狀態</li>
<li>記錄完成時間和實際工時</li>
</ul>
<p><strong>停留時間</strong>：永久（已歸檔）</p>
<p><strong>生命週期流程圖</strong>：</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">建立 Ticket
</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">待執行（Pending）
</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">進行中（In Progress） ←─┐
</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">Review 中（In Review）   │
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    ↓                   │
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  通過？                │
</span></span><span class="line"><span class="ln">10</span><span class="cl">    ├─ Yes → 已完成（Completed）
</span></span><span class="line"><span class="ln">11</span><span class="cl">    └─ No  ──────────────┘
</span></span><span class="line"><span class="ln">12</span><span class="cl">       （建立修正 Ticket，重新執行）</span></span></code></pre></div><h3 id="32-ticket-建立標準">3.2 Ticket 建立標準</h3>
<h4 id="ticket-標題格式">Ticket 標題格式</h4>
<p><strong>格式</strong>：<code>Ticket #N: [動詞] [目標]</code></p>
<p><strong>動詞選擇</strong>：</p>
<ul>
<li><strong>定義</strong>：定義 Interface、定義 Entity</li>
<li><strong>撰寫</strong>：撰寫測試、撰寫文檔</li>
<li><strong>實作</strong>：實作 Repository、實作 Use Case</li>
<li><strong>整合</strong>：整合 Service、整合 Module</li>
<li><strong>修復</strong>：修復 Bug、修復測試</li>
<li><strong>重構</strong>：重構邏輯、重構架構</li>
</ul>
<p><strong>範例</strong>：</p>
<ul>
<li><code>Ticket #1: 定義 IBookRepository 介面</code>- <code>Ticket #2: 撰寫 BookRepository 測試</code>- <code>Ticket #3: 實作 SQLiteBookRepository</code>- <code>Ticket #1: BookRepository</code>（缺少動詞）- 反例：<code>做一下 Repository</code>（不明確）</li>
</ul>
<h4 id="ticket-描述內容">Ticket 描述內容</h4>
<p>完整的 Ticket 必須包含以下 5 個核心欄位：</p>
<p><strong>Ticket 建立模板</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #N: [動詞] [目標]
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 背景
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>[為什麼需要這個 Ticket？來自哪個需求或問題？]
</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="gu">### 目標
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span>[這個 Ticket 要達成什麼？明確且可驗證]
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span><span class="k">1.</span> [具體步驟 1]
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">2.</span> [具體步驟 2]
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">3.</span> [具體步驟 3]
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> [可驗證的條件 1]
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">- [ ]</span> [可驗證的條件 2]
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">- [ ]</span> [可驗證的條件 3]
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="gu">### 參考文件
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="gu"></span><span class="k">-</span> [設計文件連結]
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">-</span> [需求文件連結]
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="gu">### 依賴 Ticket
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="gu"></span><span class="k">-</span> Ticket <span class="ni">#X</span> (必須先完成)
</span></span><span class="line"><span class="ln">25</span><span class="cl">- Ticket <span class="ni">#Y</span> (可並行)</span></span></code></pre></div><p><strong>欄位說明</strong>：</p>
<p><strong>1. 背景</strong>：</p>
<ul>
<li>說明為什麼需要這個 Ticket</li>
<li>連結到需求或設計決策</li>
<li>提供上下文資訊</li>
</ul>
<p><strong>2. 目標</strong>：</p>
<ul>
<li>一句話說明要達成什麼</li>
<li>必須明確且可驗證</li>
<li>避免模糊用語</li>
</ul>
<p><strong>3. 步驟</strong>：</p>
<ul>
<li>列出具體執行步驟（3-5 步）</li>
<li>步驟必須可操作</li>
<li>幫助開發者快速理解如何執行</li>
</ul>
<p><strong>4. 驗收條件</strong>：</p>
<ul>
<li>列出所有可驗證的條件（3-5 項）</li>
<li>條件必須客觀可檢查</li>
<li>使用 checkbox 格式</li>
</ul>
<p><strong>5. 參考文件</strong>：</p>
<ul>
<li>連結到設計文件</li>
<li>連結到需求規格</li>
<li>連結到相關 Ticket</li>
</ul>
<p><strong>6. 依賴 Ticket</strong>：</p>
<ul>
<li>列出必須先完成的 Ticket</li>
<li>列出可並行的 Ticket</li>
<li>明確依賴關係</li>
</ul>
<h3 id="33-ticket-執行流程">3.3 Ticket 執行流程</h3>
<h4 id="步驟-1領取-ticket">步驟 1：領取 Ticket</h4>
<ul>
<li>開發者從「待執行」清單選擇 Ticket</li>
<li>確認依賴 Ticket 已完成</li>
<li>標記 Ticket 為「進行中」</li>
</ul>
<h5 id="步驟-2閱讀-ticket">步驟 2：閱讀 Ticket</h5>
<ul>
<li>閱讀背景和目標</li>
<li>檢查參考文件</li>
<li>理解驗收條件</li>
</ul>
<h6 id="步驟-3執行步驟">步驟 3：執行步驟</h6>
<ul>
<li>按照步驟執行</li>
<li>遇到問題記錄到 Ticket 日誌</li>
<li>持續更新進度</li>
</ul>
<h6 id="步驟-4自我檢查">步驟 4：自我檢查</h6>
<ul>
<li>逐項檢查驗收條件</li>
<li>確保所有條件滿足</li>
<li>執行相關測試</li>
</ul>
<h6 id="步驟-5提交-review">步驟 5：提交 Review</h6>
<ul>
<li>標記所有驗收條件為完成</li>
<li>標記 Ticket 為「Review 中」</li>
<li>通知 Reviewer</li>
</ul>
<h6 id="步驟-6處理-review-結果">步驟 6：處理 Review 結果</h6>
<ul>
<li>如果通過：Ticket 標記為「已完成」</li>
<li>如果未通過：根據 Review 意見修正，重新執行</li>
</ul>
<h3 id="34-ticket-驗收標準">3.4 Ticket 驗收標準</h3>
<p>驗收條件必須符合 SMART 原則：</p>
<p><strong>S - Specific（具體）</strong>：</p>
<ul>
<li>反例：「功能運作正常」（太模糊）- 正例：「呼叫 getBookByIsbn(&lsquo;123&rsquo;) 回傳正確的 Book 物件」</li>
</ul>
<p><strong>M - Measurable（可測量）</strong>：</p>
<ul>
<li>反例：「程式碼品質良好」（無法測量）- 正例：「dart analyze 0 錯誤，測試覆蓋率 &gt; 80%」</li>
</ul>
<p><strong>A - Achievable（可達成）</strong>：</p>
<ul>
<li>反例：「整合所有第三方 API」（範圍太大）- 正例：「整合 Google Books API 的書籍搜尋功能」</li>
</ul>
<p><strong>R - Relevant（相關）</strong>：</p>
<ul>
<li>反例：「優化 UI 顏色」（與 Repository Ticket 無關）- 正例：「Repository 實作完成，測試通過」</li>
</ul>
<p><strong>T - Time-bound（有明確完成標準）</strong>：</p>
<ul>
<li>反例：「未來會完成」（無明確標準）- 正例：「符合所有驗收條件即完成」</li>
</ul>
<h3 id="35-ticket-關閉條件">3.5 Ticket 關閉條件</h3>
<p>Ticket 必須滿足以下所有條件才能關閉：</p>
<p><strong>強制條件</strong>（5 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 所有驗收條件打勾完成</li>
<li><input disabled="" type="checkbox"> Review 通過</li>
<li><input disabled="" type="checkbox"> 相關測試 100% 通過</li>
<li><input disabled="" type="checkbox"> dart analyze 0 錯誤</li>
<li><input disabled="" type="checkbox"> 工作日誌已更新</li>
</ul>
<p><strong>建議條件</strong>（3 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 程式碼符合專案規範</li>
<li><input disabled="" type="checkbox"> 無技術債務產生</li>
<li><input disabled="" type="checkbox"> 文檔同步更新</li>
</ul>
<h3 id="36-生命週期管理檢查清單">3.6 生命週期管理檢查清單</h3>
<p><strong>Ticket 建立檢查</strong>（3 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 標題格式正確（動詞 + 目標）</li>
<li><input disabled="" type="checkbox"> 5 個核心欄位完整</li>
<li><input disabled="" type="checkbox"> 驗收條件符合 SMART 原則</li>
</ul>
<p><strong>Ticket 執行檢查</strong>（4 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 依賴 Ticket 已完成</li>
<li><input disabled="" type="checkbox"> 開發者理解目標和步驟</li>
<li><input disabled="" type="checkbox"> 進度持續更新</li>
<li><input disabled="" type="checkbox"> 遇到問題即時記錄</li>
</ul>
<p><strong>Ticket Review 檢查</strong>（4 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 所有驗收條件滿足</li>
<li><input disabled="" type="checkbox"> 測試 100% 通過</li>
<li><input disabled="" type="checkbox"> 程式碼品質符合標準</li>
<li><input disabled="" type="checkbox"> 文檔同步更新</li>
</ul>
<p><strong>Ticket 關閉檢查</strong>（3 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> Review 通過</li>
<li><input disabled="" type="checkbox"> 工作日誌已更新</li>
<li><input disabled="" type="checkbox"> 主版本日誌索引已更新</li>
</ul>
<hr>
<h2 id="第四章即時-review-機制">第四章：即時 Review 機制</h2>
<h3 id="41-一邊實作一邊-review-的原則">4.1 一邊實作一邊 review 的原則</h3>
<p><strong>傳統 Review 問題</strong>：</p>
<p>傳統的「實作完成後才 review」存在以下問題：</p>
<ol>
<li><strong>發現問題太晚</strong>：實作偏差已形成，修正成本高</li>
<li><strong>返工時間長</strong>：需要大幅修改已完成的程式碼</li>
<li><strong>士氣影響大</strong>：開發者認為「白做了」，影響積極性</li>
<li><strong>無法並行</strong>：必須等所有任務完成才能 review</li>
</ol>
<p><strong>即時 Review 原則</strong>：</p>
<p>「一邊實作一邊 review」的核心原則：</p>
<ol>
<li><strong>每完成一個 Ticket 觸發 review</strong></li>
<li><strong>review 快速完成，聚焦核心問題</strong></li>
<li><strong>只 review 當前 Ticket，不累積</strong></li>
<li><strong>發現問題立即建立修正 Ticket</strong></li>
</ol>
<p><strong>效益</strong>：</p>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>傳統 Review</th>
          <th>即時 Review</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>問題發現時機</strong></td>
          <td>實作完成後（數天後）</td>
          <td>每個 Ticket 完成後（即時）</td>
      </tr>
      <tr>
          <td><strong>修正成本</strong></td>
          <td>高（需大幅修改）</td>
          <td>低（只需修正單一 Ticket）</td>
      </tr>
      <tr>
          <td><strong>開發者體驗</strong></td>
          <td>挫折感（白做了）</td>
          <td>即時回饋（快速修正）</td>
      </tr>
      <tr>
          <td><strong>可並行性</strong></td>
          <td>低（必須等全部完成）</td>
          <td>高（可持續並行開發）</td>
      </tr>
  </tbody>
</table>
<h3 id="42-review-觸發時機">4.2 Review 觸發時機</h3>
<p><strong>觸發條件</strong>：</p>
<p>Review 在以下時機觸發：</p>
<ol>
<li><strong>每完成一個 Ticket</strong>：開發者標記 Ticket 為「Review 中」</li>
<li><strong>每完成 3-5 個 Ticket</strong>：觸發階段性 review（可選）</li>
<li><strong>每完成一個模組</strong>：觸發模組整合 review（可選）</li>
</ol>
<p><strong>主要觸發機制</strong>：</p>
<p><strong>每完成一個 Ticket 觸發</strong>：</p>
<ul>
<li>開發者完成 Ticket，自我檢查驗收條件</li>
<li>標記 Ticket 為「Review 中」</li>
<li>Reviewer 收到通知，開始 review</li>
<li>Review 快速完成，聚焦核心問題</li>
</ul>
<p><strong>不等待</strong>：</p>
<ul>
<li>不等待整個任務完成才 review</li>
<li>不累積多個 Ticket 一起 review</li>
<li>不延後 review 時機</li>
</ul>
<h3 id="43-review-檢查項目">4.3 Review 檢查項目</h3>
<p>Review 分為 4 大類，共 16 項檢查項：</p>
<h4 id="類別-1功能正確性檢查4-項">類別 1：功能正確性檢查（4 項）</h4>
<h5 id="檢查項-1ticket-描述的功能是否實現">檢查項 1：Ticket 描述的功能是否實現？</h5>
<ul>
<li>檢查方法：對照 Ticket 目標和實際程式碼</li>
<li>通過標準：功能完全符合 Ticket 描述</li>
</ul>
<h6 id="檢查項-2驗收條件是否全部滿足">檢查項 2：驗收條件是否全部滿足？</h6>
<ul>
<li>檢查方法：逐項檢查驗收條件 checkbox</li>
<li>通過標準：所有條件都打勾且確實滿足</li>
</ul>
<h6 id="檢查項-3是否有未處理的邊界情況">檢查項 3：是否有未處理的邊界情況？</h6>
<ul>
<li>檢查方法：檢查 null、empty、異常輸入處理</li>
<li>通過標準：所有邊界情況都有處理</li>
</ul>
<h6 id="檢查項-4錯誤處理是否完整">檢查項 4：錯誤處理是否完整？</h6>
<ul>
<li>檢查方法：檢查 try-catch、異常拋出</li>
<li>通過標準：所有異常都有妥善處理</li>
</ul>
<h4 id="類別-2架構合規性檢查4-項">類別 2：架構合規性檢查（4 項）</h4>
<h5 id="檢查項-5是否符合-clean-architecture-分層原則">檢查項 5：是否符合 Clean Architecture 分層原則？</h5>
<ul>
<li>檢查方法：檢查檔案位置、模組分層</li>
<li>通過標準：符合 Domain/Application/Infrastructure/Presentation 分層</li>
</ul>
<h6 id="檢查項-6依賴方向是否正確內層不依賴外層">檢查項 6：依賴方向是否正確（內層不依賴外層）？</h6>
<ul>
<li>檢查方法：檢查 import 語句</li>
<li>通過標準：所有依賴都指向內層或 Interface</li>
</ul>
<h6 id="檢查項-7是否使用-interface-driven-開發">檢查項 7：是否使用 Interface-Driven 開發？</h6>
<ul>
<li>檢查方法：檢查是否依賴 Interface 而非具體實作</li>
<li>通過標準：外層依賴 Interface，不直接依賴實作類別</li>
</ul>
<h6 id="檢查項-8是否有架構債務產生">檢查項 8：是否有架構債務產生？</h6>
<ul>
<li>檢查方法：檢查是否有違反 SOLID 原則的程式碼</li>
<li>通過標準：無明顯架構債務</li>
</ul>
<h4 id="類別-3測試通過率檢查4-項">類別 3：測試通過率檢查（4 項）</h4>
<h5 id="檢查項-9相關單元測試是否-100-通過">檢查項 9：相關單元測試是否 100% 通過？</h5>
<ul>
<li>檢查方法：執行 <code>dart test</code> 或 <code>flutter test</code></li>
<li>通過標準：所有測試通過，0 失敗</li>
</ul>
<h6 id="檢查項-10相關整合測試是否-100-通過">檢查項 10：相關整合測試是否 100% 通過？</h6>
<ul>
<li>檢查方法：執行整合測試</li>
<li>通過標準：所有整合測試通過</li>
</ul>
<h6 id="檢查項-11測試覆蓋率是否達標">檢查項 11：測試覆蓋率是否達標？</h6>
<ul>
<li>檢查方法：檢查測試覆蓋率報告</li>
<li>通過標準：覆蓋率 &gt; 80%（建議）</li>
</ul>
<h6 id="檢查項-12是否有測試被-skip">檢查項 12：是否有測試被 skip？</h6>
<ul>
<li>檢查方法：搜尋 <code>skip:</code> 或 <code>.skip</code></li>
<li>通過標準：無 skip 的測試</li>
</ul>
<h4 id="類別-4文檔同步性檢查4-項">類別 4：文檔同步性檢查（4 項）</h4>
<h5 id="檢查項-13ticket-工作日誌是否更新">檢查項 13：Ticket 工作日誌是否更新？</h5>
<ul>
<li>檢查方法：檢查對應的 Ticket 日誌檔案</li>
<li>通過標準：記錄執行過程和決策</li>
</ul>
<h6 id="檢查項-14設計決策是否記錄">檢查項 14：設計決策是否記錄？</h6>
<ul>
<li>檢查方法：檢查設計決策日誌</li>
<li>通過標準：重要決策都有記錄</li>
</ul>
<h6 id="檢查項-15api-文檔是否同步">檢查項 15：API 文檔是否同步？</h6>
<ul>
<li>檢查方法：檢查程式碼註解和 dartdoc</li>
<li>通過標準：公開 API 都有完整文檔註解</li>
</ul>
<h6 id="檢查項-16readme-是否需要更新">檢查項 16：README 是否需要更新？</h6>
<ul>
<li>檢查方法：檢查是否有新增功能或變更使用方式</li>
<li>通過標準：如需更新則已更新</li>
</ul>
<h3 id="44-偏差糾正流程">4.4 偏差糾正流程</h3>
<p>當 Review 發現問題時，執行以下偏差糾正流程：</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">Review 發現偏差
</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">暫停當前 Ticket
</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">記錄偏差問題（描述、影響、根因）
</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">    ├─ 理解錯誤？ → 釐清需求，重新執行
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    ├─ 技術問題？ → 尋求技術支援
</span></span><span class="line"><span class="ln">10</span><span class="cl">    └─ 架構問題？ → 修正架構設計
</span></span><span class="line"><span class="ln">11</span><span class="cl">    ↓
</span></span><span class="line"><span class="ln">12</span><span class="cl">建立修正 Ticket
</span></span><span class="line"><span class="ln">13</span><span class="cl">    ↓
</span></span><span class="line"><span class="ln">14</span><span class="cl">修正 Ticket 執行
</span></span><span class="line"><span class="ln">15</span><span class="cl">    ↓
</span></span><span class="line"><span class="ln">16</span><span class="cl">再次 Review
</span></span><span class="line"><span class="ln">17</span><span class="cl">    ├─ 通過 → 標記原 Ticket 為「已完成」
</span></span><span class="line"><span class="ln">18</span><span class="cl">    └─ 未通過 → 重複偏差糾正流程
</span></span><span class="line"><span class="ln">19</span><span class="cl">    ↓
</span></span><span class="line"><span class="ln">20</span><span class="cl">總結經驗教訓（更新檢查清單）</span></span></code></pre></div><p><strong>偏差記錄格式</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">### Review 偏差記錄 #N
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gs">**發現時間**</span>：2025-10-10 14:30
</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"><span class="gs">**偏差描述**</span>：
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">實作的 Repository 方法簽名與 Interface 不一致
</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"><span class="gs">**影響範圍**</span>：
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">-</span> SQLiteBookRepository.getBookByIsbn 方法
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">-</span> 相關測試需要調整
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gs">**根因分析**</span>：
</span></span><span class="line"><span class="ln">14</span><span class="cl">開發者未參考最新的 Interface 定義
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="gs">**糾正措施**</span>：
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">-</span> 建立修正 Ticket：修正 Repository 方法簽名
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">-</span> 更新相關測試
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">-</span> 加入檢查清單：實作前必須確認 Interface 最新版本
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="gs">**責任歸屬**</span>：
</span></span><span class="line"><span class="ln">23</span><span class="cl">開發者（未確認 Interface）+ Reviewer（未及時發現）
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="gs">**經驗教訓**</span>：
</span></span><span class="line"><span class="ln">26</span><span class="cl">實作前必須先閱讀最新的 Interface 定義</span></span></code></pre></div><h3 id="45-review-記錄格式">4.5 Review 記錄格式</h3>
<p>每次 Review 都必須記錄，格式如下：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">### Review 記錄 - Ticket #N
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gs">**Review 時間**</span>：2025-10-10 15:00
</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"><span class="gs">**Reviewer**</span>：張三
</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 class="gs">**Ticket 標題**</span>：實作 SQLiteBookRepository
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gs">**Review 結果**</span>：通過 / 未通過
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gs">**檢查項目**</span>：
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">- [x]</span> 功能正確性（4/4 通過）
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">- [x]</span> 架構合規性（4/4 通過）
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">- [x]</span> 測試通過率（4/4 通過）
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">- [x]</span> 文檔同步性（4/4 通過）
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="gs">**發現問題**</span>：
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">-</span> 無（如果有，列出問題清單）
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="gs">**建議改善**</span>：
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="k">-</span> 建議補充更多邊界情況測試（可選）
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="gs">**Review 時間**</span>：8 分鐘
</span></span><span class="line"><span class="ln">27</span><span class="cl">
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="gs">**下一步行動**</span>：
</span></span><span class="line"><span class="ln">29</span><span class="cl">
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="k">-</span> 標記 Ticket 為「已完成」
</span></span><span class="line"><span class="ln">31</span><span class="cl">- 更新主版本日誌索引</span></span></code></pre></div><h3 id="46-即時-review-檢查清單">4.6 即時 Review 檢查清單</h3>
<p><strong>Review 前準備</strong>（3 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> Reviewer 收到 review 通知</li>
<li><input disabled="" type="checkbox"> Ticket 標記為「Review 中」</li>
<li><input disabled="" type="checkbox"> 所有驗收條件已打勾</li>
</ul>
<p><strong>Review 執行</strong>（4 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 逐項檢查 16 個檢查項目</li>
<li><input disabled="" type="checkbox"> 記錄 Review 結果</li>
<li><input disabled="" type="checkbox"> 發現問題建立偏差記錄</li>
<li><input disabled="" type="checkbox"> Review 快速完成，聚焦核心問題</li>
</ul>
<p><strong>Review 後處理</strong>（2 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 通過：標記 Ticket 為「已完成」</li>
<li><input disabled="" type="checkbox"> 未通過：建立修正 Ticket</li>
</ul>
<p><strong>持續改善</strong>（5 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 總結經驗教訓</li>
<li><input disabled="" type="checkbox"> 更新檢查清單（如需要）</li>
<li><input disabled="" type="checkbox"> 優化 Review 流程</li>
<li><input disabled="" type="checkbox"> 提升 Review 效率</li>
<li><input disabled="" type="checkbox"> 降低偏差發生率</li>
</ul>
<h2 id="第五章文件管理策略">第五章：文件管理策略</h2>
<h3 id="51-避免工作日誌臃腫的三層文件結構">5.1 避免工作日誌臃腫的三層文件結構</h3>
<h4 id="問題分析v0127-工作日誌臃腫案例">問題分析：v0.12.7 工作日誌臃腫案例</h4>
<p><strong>v0.12.7 工作日誌問題</strong>：</p>
<ul>
<li><strong>檔案大小</strong>：6000 行</li>
<li><strong>問題根因</strong>：
<ul>
<li>所有 Ticket 日誌都寫在主版本日誌</li>
<li>設計迭代過程全部記錄在同一檔案</li>
<li>缺乏分層管理機制</li>
</ul>
</li>
</ul>
<p><strong>影響</strong>：</p>
<ul>
<li>開發者難以快速找到關鍵資訊</li>
<li>檔案過大導致編輯器效能下降</li>
<li>歷史記錄難以追溯</li>
</ul>
<h4 id="三層文件結構設計">三層文件結構設計</h4>
<p>為了解決工作日誌臃腫問題，採用三層文件結構：</p>
<h5 id="第-1-層主版本日誌main-version-log">第 1 層：主版本日誌（Main Version Log）</h5>
<p><strong>檔案命名</strong>：<code>vX.Y.Z-main.md</code>
<strong>目標大小</strong>：500-800 行
<strong>內容範圍</strong>：</p>
<ul>
<li>版本目標和規劃</li>
<li>Ticket 索引（只記錄 Ticket 編號和標題）</li>
<li>設計決策索引（連結到決策日誌）</li>
<li>版本總結</li>
</ul>
<p><strong>範例</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gh"># v0.12.7 主版本日誌
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gh"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">## 版本目標
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></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="gu">## Ticket 索引
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span><span class="k">-</span> Ticket <span class="ni">#1:</span> 定義 IBookInfoEnrichmentService 介面 → [<span class="nt">詳細日誌</span>](<span class="na">/record/ticket_v2/v0.12.7-ticket-001.md</span>)
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">-</span> Ticket <span class="ni">#2:</span> 撰寫 BookInfoEnrichmentService 測試 → [<span class="nt">詳細日誌</span>](<span class="na">/record/ticket_v2/v0.12.7-ticket-002.md</span>)
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">-</span> Ticket <span class="ni">#3:</span> 實作 GoogleBooksEnrichmentService → [<span class="nt">詳細日誌</span>](<span class="na">/record/ticket_v2/v0.12.7-ticket-003.md</span>)
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu">## 設計決策索引
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu"></span><span class="k">-</span> 決策 <span class="ni">#1:</span> 選擇 Strategy Pattern 處理多資料源 → [<span class="nt">詳細日誌</span>](<span class="na">/record/ticket_v2/v0.12.7-design-decisions.md#決策1</span>)
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">-</span> 決策 <span class="ni">#2:</span> 使用 Riverpod 管理服務註冊 → [<span class="nt">詳細日誌</span>](<span class="na">/record/ticket_v2/v0.12.7-design-decisions.md#決策2</span>)
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="gu">## 版本總結
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="gu"></span><span class="k">-</span> 完成 12 個 Ticket
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">-</span> 總開發時間：4.5 小時
</span></span><span class="line"><span class="ln">18</span><span class="cl">- 測試覆蓋率：92%</span></span></code></pre></div><h3 id="第-2-層ticket-工作日誌ticket-work-log">第 2 層：Ticket 工作日誌（Ticket Work Log）</h3>
<p><strong>檔案命名</strong>：<code>vX.Y.Z-ticket-NNN.md</code>
<strong>目標大小</strong>：100-200 行
<strong>內容範圍</strong>：</p>
<ul>
<li>Ticket 描述（背景、目標、步驟、驗收條件）</li>
<li>執行過程記錄</li>
<li>遇到的問題和解決方案</li>
<li>Review 結果</li>
</ul>
<p><strong>範例</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gh"># Ticket #1 工作日誌
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gh"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">## Ticket 描述
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>定義 IBookInfoEnrichmentService 介面
</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="gu">### 背景
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span>根據 UC-05 需求，需要建立書籍資訊豐富化服務的介面契約
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">### 目標
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span>建立 <span class="sb">`IBookInfoEnrichmentService`</span> 介面，定義書籍資訊豐富化的契約
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 建立介面檔案
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">2.</span> 定義 <span class="sb">`enrichBook`</span> 方法
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">3.</span> 定義 <span class="sb">`enrichProgress`</span> 方法
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">4.</span> 撰寫文檔註解
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="gu"></span><span class="k">- [x]</span> 介面檔案建立在正確位置
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">- [x]</span> 方法簽名完整且明確
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">- [x]</span> 包含完整的文檔註解
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">- [x]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="gu">## 執行過程
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 建立 <span class="sb">`lib/domains/import/services/i_book_info_enrichment_service.dart`</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="k">2.</span> 定義 <span class="sb">`enrichBook`</span> 方法：接收 ISBN，回傳豐富化後的 Book
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="k">3.</span> 定義 <span class="sb">`enrichProgress`</span> 方法：回傳豐富化進度 Stream
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="k">4.</span> 補充文檔註解說明每個方法的用途
</span></span><span class="line"><span class="ln">29</span><span class="cl">
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="gu">## Review 結果
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="gu"></span>通過（8 分鐘）
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="k">-</span> 功能正確性：4/4 通過
</span></span><span class="line"><span class="ln">33</span><span class="cl">- 架構合規性：4/4 通過</span></span></code></pre></div><h3 id="第-3-層設計決策日誌design-decision-log">第 3 層：設計決策日誌（Design Decision Log）</h3>
<p><strong>檔案命名</strong>：<code>vX.Y.Z-design-decisions.md</code>
<strong>目標大小</strong>：300-500 行
<strong>內容範圍</strong>：</p>
<ul>
<li>重要設計決策記錄</li>
<li>技術選型理由</li>
<li>架構調整說明</li>
<li>決策的影響分析</li>
</ul>
<p><strong>範例</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gh"># v0.12.7 設計決策日誌
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gh"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">## 決策 #1: 選擇 Strategy Pattern 處理多資料源
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gu">### 決策時間
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="gu"></span>2025-10-09 14:30
</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"><span class="gu">### 決策背景
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu"></span>書籍資訊豐富化需要支援多個資料來源（Google Books、Open Library、豆瓣讀書）
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu">### 可選方案
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu"></span><span class="k">1.</span> <span class="gs">**方案 A**</span>：使用 if-else 判斷資料來源
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">2.</span> <span class="gs">**方案 B**</span>：使用 Strategy Pattern 封裝每個資料來源
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">3.</span> <span class="gs">**方案 C**</span>：使用 Chain of Responsibility Pattern
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="gu">### 選擇理由
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="gu"></span>選擇方案 B（Strategy Pattern）：
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">-</span> 每個資料來源獨立封裝，符合 Open-Closed Principle
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">-</span> 新增資料來源只需新增 Strategy，不修改現有程式碼
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">-</span> 可以動態切換資料來源策略
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="gu">### 影響範圍
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="gu"></span><span class="k">-</span> 建立 <span class="sb">`IEnrichmentStrategy`</span> 介面
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="k">-</span> 實作 <span class="sb">`GoogleBooksStrategy`</span>、<span class="sb">`OpenLibraryStrategy`</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="k">-</span> <span class="sb">`BookInfoEnrichmentService`</span> 依賴 <span class="sb">`IEnrichmentStrategy`</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="gu">### 實作 Ticket
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="gu"></span><span class="k">-</span> Ticket <span class="ni">#4:</span> 定義 IEnrichmentStrategy 介面
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="k">-</span> Ticket <span class="ni">#5:</span> 實作 GoogleBooksStrategy
</span></span><span class="line"><span class="ln">31</span><span class="cl">- Ticket <span class="ni">#6:</span> 實作 OpenLibraryStrategy</span></span></code></pre></div><h4 id="三層結構效益">三層結構效益</h4>
<p><strong>效益總結</strong>：</p>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>v0.12.7 單一檔案（6000 行）</th>
          <th>三層結構（800+200*N+400 行）</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>可讀性</strong></td>
          <td>低（難以快速找到資訊）</td>
          <td>高（分層清晰）</td>
      </tr>
      <tr>
          <td><strong>維護性</strong></td>
          <td>低（修改影響範圍大）</td>
          <td>高（獨立修改）</td>
      </tr>
      <tr>
          <td><strong>查找效率</strong></td>
          <td>低（需搜尋整個檔案）</td>
          <td>高（索引快速定位）</td>
      </tr>
      <tr>
          <td><strong>協作友善</strong></td>
          <td>低（衝突機率高）</td>
          <td>高（不同檔案並行編輯）</td>
      </tr>
  </tbody>
</table>
<p><strong>改善數據</strong>：</p>
<ul>
<li>主版本日誌：6000 行 → 800 行（減少 87%）</li>
<li>Ticket 日誌：分散到 12 個檔案，每個 150 行</li>
<li>設計決策日誌：獨立 400 行</li>
</ul>
<h3 id="52-設計迭代文件管理">5.2 設計迭代文件管理</h3>
<h4 id="設計迭代的挑戰">設計迭代的挑戰</h4>
<p><strong>問題</strong>：
設計過程中會產生多個版本的設計文件：</p>
<ul>
<li>初版設計（可能有缺陷）</li>
<li>修正版設計（發現問題後調整）</li>
<li>最終設計（經過 review 確認）</li>
</ul>
<p><strong>如果不管理</strong>：</p>
<ul>
<li>開發者不知道哪個是最終版本</li>
<li>過時的設計誤導後續開發</li>
<li>設計演進過程難以追溯</li>
</ul>
<h4 id="版本管理策略">版本管理策略</h4>
<h5 id="策略-1設計檔案版本標記">策略 1：設計檔案版本標記</h5>
<p>在設計決策日誌中明確標記版本狀態：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 決策 #1: 選擇 Strategy Pattern 處理多資料來源
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gs">**決策狀態**</span>：最終決策（已實作）
</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"><span class="gs">**版本歷史**</span>：
</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 class="k">-</span> v1 (2025-10-09 14:30): 初版設計 - 使用 if-else 判斷（已廢棄）
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">-</span> v2 (2025-10-09 15:00): 修正版 - 改用 Strategy Pattern（最終版本）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gs">**當前版本**</span>: v2</span></span></code></pre></div><h3 id="策略-2最終設計明確標記">策略 2：最終設計明確標記</h3>
<p>在主版本日誌中明確標記最終設計：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gu">## 設計決策索引
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gu"></span><span class="k">-</span> 決策 <span class="ni">#1:</span> 選擇 Strategy Pattern（最終決策）→ [<span class="nt">詳細日誌</span>](<span class="na">/record/ticket_v2/v0.12.7-design-decisions.md#決策1</span>)
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="k">-</span> 決策 <span class="ni">#2:</span> 使用 Riverpod 管理服務（最終決策）→ [<span class="nt">詳細日誌</span>](<span class="na">/record/ticket_v2/v0.12.7-design-decisions.md#決策2</span>)
</span></span><span class="line"><span class="ln">4</span><span class="cl">- <span class="gd">~~決策 #3: 使用 GetIt 管理服務~~</span>（已廢棄，改用決策 <span class="ni">#2</span>）</span></span></code></pre></div><h3 id="策略-3廢棄設計保留但標記">策略 3：廢棄設計保留但標記</h3>
<p>不刪除過時設計，而是標記為廢棄並說明原因：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## ~~決策 #3: 使用 GetIt 管理服務~~（已廢棄）
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gs">**廢棄原因**</span>：
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">發現 Riverpod 更適合 Flutter 3.x，提供更好的型別安全
</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="gs">**廢棄時間**</span>：2025-10-09 16:00
</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"><span class="gs">**替代決策**</span>：決策 <span class="ni">#2</span>（使用 Riverpod）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gs">**保留原因**</span>：
</span></span><span class="line"><span class="ln">11</span><span class="cl">記錄設計演進過程，避免重複評估相同方案</span></span></code></pre></div><h3 id="53-開發者查找設計的指引">5.3 開發者查找設計的指引</h3>
<h4 id="設計查找流程">設計查找流程</h4>
<h5 id="步驟-1從主版本日誌開始">步驟 1：從主版本日誌開始</h5>
<p>開發者接到任務時，先查看主版本日誌：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gh"># v0.12.7 主版本日誌
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gh"></span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="gu">## 快速導航
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="gu"></span><span class="k">-</span> [<span class="nt">版本目標</span>](<span class="na">#版本目標</span>)
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="k">-</span> [<span class="nt">Ticket 索引</span>](<span class="na">#Ticket索引</span>)
</span></span><span class="line"><span class="ln">6</span><span class="cl">- [<span class="nt">設計決策索引</span>](<span class="na">#設計決策索引</span>)（開發者從這裡開始）</span></span></code></pre></div><h3 id="步驟-2查看設計決策索引">步驟 2：查看設計決策索引</h3>
<p>根據任務相關的領域，找到對應的設計決策：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gu">## 設計決策索引
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="gu">### 服務層設計
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="gu"></span><span class="k">-</span> 決策 <span class="ni">#1:</span> 選擇 Strategy Pattern（最終決策）→ [<span class="nt">詳細</span>](<span class="na">/record/ticket_v2/v0.12.7-design-decisions.md#決策1</span>)
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="k">-</span> 決策 <span class="ni">#2:</span> 使用 Riverpod 管理服務（最終決策）→ [<span class="nt">詳細</span>](<span class="na">/record/ticket_v2/v0.12.7-design-decisions.md#決策2</span>)
</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 class="gu">### 資料層設計
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="gu"></span>- 決策 <span class="ni">#4:</span> 選擇 SQLite 儲存（最終決策）→ [<span class="nt">詳細</span>](<span class="na">/record/ticket_v2/v0.12.7-design-decisions.md#決策4</span>)</span></span></code></pre></div><h4 id="步驟-3閱讀設計決策詳細內容">步驟 3：閱讀設計決策詳細內容</h4>
<p>點擊連結進入設計決策日誌，閱讀詳細內容：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 決策 #1: 選擇 Strategy Pattern 處理多資料來源
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gs">**決策狀態**</span>：最終決策（已實作）
</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"><span class="gu">### 決策背景
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="gu"></span>...
</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"><span class="gu">### 選擇理由
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu"></span>...
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu">### 影響範圍
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu"></span>...
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu">### 實作 Ticket
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="gu"></span><span class="k">-</span> Ticket <span class="ni">#4:</span> 定義 IEnrichmentStrategy 介面
</span></span><span class="line"><span class="ln">16</span><span class="cl">- Ticket <span class="ni">#5:</span> 實作 GoogleBooksStrategy</span></span></code></pre></div><h4 id="步驟-4查看相關-ticket">步驟 4：查看相關 Ticket</h4>
<p>根據設計決策中的「實作 Ticket」，查看具體實作細節：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gh"># Ticket #4 工作日誌
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gh"></span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="gu">## Ticket 描述
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="gu"></span>定義 IEnrichmentStrategy 介面
</span></span><span class="line"><span class="ln">5</span><span class="cl">...</span></span></code></pre></div><h4 id="設計文件索引規範">設計文件索引規範</h4>
<p><strong>主版本日誌索引格式</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 設計決策索引
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### [領域名稱]
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span><span class="k">-</span> 決策 <span class="ni">#N:</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="gs">**索引規則**</span>：
</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"><span class="k">1.</span> 按領域分組（服務層、資料層、UI 層）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">2.</span> 標記決策狀態（最終決策、已廢棄）
</span></span><span class="line"><span class="ln">10</span><span class="cl">3. 提供直接連結到詳細日誌</span></span></code></pre></div><p><strong>設計決策日誌格式</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 決策 #N: [決策標題]
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gs">**決策狀態**</span>：最終決策 / 已廢棄
</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"><span class="gs">**相關 Ticket**</span>：
</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 class="k">-</span> Ticket <span class="ni">#X:</span> [Ticket 標題]
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">-</span> Ticket <span class="ni">#Y:</span> [Ticket 標題]
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gs">**決策背景**</span>：...
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gs">**選擇理由**</span>：...
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gs">**影響範圍**</span>：...</span></span></code></pre></div><h3 id="54-文件管理檢查清單">5.4 文件管理檢查清單</h3>
<p><strong>主版本日誌檢查</strong>（4 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 版本目標明確且可驗證</li>
<li><input disabled="" type="checkbox"> Ticket 索引完整（所有 Ticket 都有連結）</li>
<li><input disabled="" type="checkbox"> 設計決策索引清晰（按領域分組）</li>
<li><input disabled="" type="checkbox"> 檔案大小控制在 500-800 行</li>
</ul>
<p><strong>Ticket 工作日誌檢查</strong>（4 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 檔案命名符合規範（vX.Y.Z-ticket-NNN.md）</li>
<li><input disabled="" type="checkbox"> Ticket 描述完整（6 個核心欄位）</li>
<li><input disabled="" type="checkbox"> 執行過程記錄清楚</li>
<li><input disabled="" type="checkbox"> 檔案大小控制在 100-200 行</li>
</ul>
<p><strong>設計決策日誌檢查</strong>（4 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 每個決策都有明確的狀態標記</li>
<li><input disabled="" type="checkbox"> 廢棄決策保留但標記廢棄原因</li>
<li><input disabled="" type="checkbox"> 版本歷史記錄完整</li>
<li><input disabled="" type="checkbox"> 檔案大小控制在 300-500 行</li>
</ul>
<p><strong>設計文件索引檢查</strong>（4 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 主版本日誌有「快速導航」區塊</li>
<li><input disabled="" type="checkbox"> 設計決策索引按領域分組</li>
<li><input disabled="" type="checkbox"> 所有連結都正確指向目標</li>
<li><input disabled="" type="checkbox"> 最終決策明確標記</li>
</ul>
<hr>
<h2 id="第六章與敏捷重構和-tdd-的整合">第六章：與敏捷重構和 TDD 的整合</h2>
<h3 id="61-ticket-機制與三重文件原則的關係">6.1 Ticket 機制與三重文件原則的關係</h3>
<h4 id="三重文件原則回顧">三重文件原則回顧</h4>
<p>本專案採用三重文件原則（CHANGELOG + todolist + work-log）進行全方位進度管理：</p>
<p><strong>1. CHANGELOG.md</strong>：</p>
<ul>
<li>面向用戶的版本功能描述</li>
<li>只記錄「做了什麼」，不記錄「怎麼做」</li>
</ul>
<p><strong>2. todolist.md</strong>：</p>
<ul>
<li>記錄整個開發過程所有待處理任務</li>
<li>任務狀態追蹤（待執行/進行中/已完成）</li>
</ul>
<p><strong>3. work-log/</strong>：</p>
<ul>
<li>完整的技術實作細節和決策過程</li>
<li>TDD 四階段進度追蹤</li>
</ul>
<h4 id="ticket-機制如何融入三重文件">Ticket 機制如何融入三重文件</h4>
<p><strong>Ticket 機制定位</strong>：</p>
<p>Ticket 機制是「work-log 層」的細化管理工具：</p>
<ul>
<li><strong>work-log 主版本日誌</strong> = Ticket 索引 + 版本總覽</li>
<li><strong>work-log Ticket 日誌</strong> = 單一 Ticket 的執行記錄</li>
<li><strong>work-log 設計決策日誌</strong> = 設計迭代過程記錄</li>
</ul>
<p><strong>整合關係</strong>：</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">CHANGELOG.md（版本功能描述）
</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">work-log/vX.Y.Z-main.md（主版本日誌）
</span></span><span class="line"><span class="ln">4</span><span class="cl">    ↓ Ticket 索引
</span></span><span class="line"><span class="ln">5</span><span class="cl">work-log/vX.Y.Z-ticket-NNN.md（Ticket 日誌）
</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">todolist.md（任務狀態追蹤）</span></span></code></pre></div><p><strong>協作流程</strong>：</p>
<ol>
<li>
<p><strong>PM 規劃階段</strong>：</p>
<ul>
<li>在 <code>todolist.md</code> 建立任務項目</li>
<li>在 <code>vX.Y.Z-main.md</code> 規劃 Ticket 索引</li>
</ul>
</li>
<li>
<p><strong>開發執行階段</strong>：</p>
<ul>
<li>建立 <code>vX.Y.Z-ticket-NNN.md</code> Ticket 日誌</li>
<li>執行 Ticket，記錄過程</li>
<li>完成後更新 <code>todolist.md</code> 狀態</li>
</ul>
</li>
<li>
<p><strong>版本發布階段</strong>：</p>
<ul>
<li>從 <code>vX.Y.Z-main.md</code> 提取功能變動</li>
<li>更新 <code>CHANGELOG.md</code> 版本說明</li>
</ul>
</li>
</ol>
<h3 id="62-ticket-與-tdd-階段的對應關係">6.2 Ticket 與 TDD 階段的對應關係</h3>
<h4 id="tdd-階段與-ticket-類型對應">TDD 階段與 Ticket 類型對應</h4>
<p><strong>Phase 1（功能設計）→ Interface 定義 Ticket</strong>：</p>
<p>Phase 1 產出的設計決策 → 轉化為 Interface 定義 Ticket：</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">Phase 1 產出：
</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">- IBookRepository 介面設計
</span></span><span class="line"><span class="ln">4</span><span class="cl">- IBookInfoEnrichmentService 介面設計
</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">↓ 轉化為 Ticket
</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">Ticket #1: 定義 IBookRepository 介面
</span></span><span class="line"><span class="ln">9</span><span class="cl">Ticket #2: 定義 IBookInfoEnrichmentService 介面</span></span></code></pre></div><p><strong>Phase 2（測試設計）→ 測試撰寫 Ticket</strong>：</p>
<p>Phase 2 產出的測試設計 → 轉化為測試撰寫 Ticket：</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">Phase 2 產出：
</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">- BookRepository 測試用例設計
</span></span><span class="line"><span class="ln">4</span><span class="cl">- BookInfoEnrichmentService 測試用例設計
</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">↓ 轉化為 Ticket
</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">Ticket #3: 撰寫 BookRepository 測試
</span></span><span class="line"><span class="ln">9</span><span class="cl">Ticket #4: 撰寫 BookInfoEnrichmentService 測試</span></span></code></pre></div><p><strong>Phase 3（實作執行）→ 具體實作 Ticket + 整合 Ticket</strong>：</p>
<p>Phase 3 產出的實作 → 拆分為具體實作和整合 Ticket：</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">Phase 3 產出：
</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">- SQLiteBookRepository 實作
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">- GoogleBooksEnrichmentService 實作
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">- 整合到 Use Case
</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">↓ 轉化為 Ticket
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">Ticket #5: 實作 SQLiteBookRepository
</span></span><span class="line"><span class="ln">10</span><span class="cl">Ticket #6: 實作 GoogleBooksEnrichmentService
</span></span><span class="line"><span class="ln">11</span><span class="cl">Ticket #7: 整合到 GetBookUseCase</span></span></code></pre></div><p><strong>Phase 4（重構優化）→ 重構 Ticket</strong>：</p>
<p>Phase 4 發現的重構需求 → 建立重構 Ticket：</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">Phase 4 發現：
</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">- BookRepository 邏輯複雜，需要拆分
</span></span><span class="line"><span class="ln">4</span><span class="cl">- EnrichmentService 錯誤處理不完整
</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">↓ 轉化為 Ticket
</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">Ticket #8: 重構 BookRepository，拆分 CRUD 邏輯
</span></span><span class="line"><span class="ln">9</span><span class="cl">Ticket #9: 補充 EnrichmentService 錯誤處理</span></span></code></pre></div><h4 id="tdd-階段完成標準與-ticket-關係">TDD 階段完成標準與 Ticket 關係</h4>
<p><strong>Phase 1 完成標準</strong>：</p>
<ul>
<li>所有 Interface 定義 Ticket 建立並標記為「待執行」</li>
<li>設計決策日誌記錄完整</li>
</ul>
<p><strong>Phase 2 完成標準</strong>：</p>
<ul>
<li>所有測試撰寫 Ticket 建立並標記為「待執行」</li>
<li>測試覆蓋所有 Interface 方法</li>
</ul>
<p><strong>Phase 3 完成標準</strong>：</p>
<ul>
<li>所有具體實作 Ticket 完成並通過 review</li>
<li>所有整合 Ticket 完成並通過測試</li>
<li>測試 100% 通過</li>
</ul>
<p><strong>Phase 4 完成標準</strong>：</p>
<ul>
<li>所有重構 Ticket 完成並通過 review</li>
<li>程式碼品質達標，無技術債務</li>
</ul>
<h3 id="63-ticket-派工前的準備度檢查">6.3 Ticket 派工前的準備度檢查</h3>
<h4 id="準備度檢查清單">準備度檢查清單</h4>
<p>在建立和派工 Ticket 前，必須檢查以下準備度：</p>
<p><strong>設計準備度</strong>（4 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 相關設計決策已完成且標記為「最終決策」</li>
<li><input disabled="" type="checkbox"> Interface 定義已完成（如適用）</li>
<li><input disabled="" type="checkbox"> 設計決策日誌已更新</li>
<li><input disabled="" type="checkbox"> 沒有未解決的設計問題</li>
</ul>
<p><strong>依賴準備度</strong>（3 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 依賴的 Ticket 已完成</li>
<li><input disabled="" type="checkbox"> 依賴的 Interface 已定義</li>
<li><input disabled="" type="checkbox"> 依賴的測試已撰寫（如適用）</li>
</ul>
<p><strong>資源準備度</strong>（3 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 開發者已理解 Ticket 目標</li>
<li><input disabled="" type="checkbox"> 開發環境已準備就緒</li>
<li><input disabled="" type="checkbox"> 相關文檔已閱讀</li>
</ul>
<p><strong>品質準備度</strong>（2 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 驗收條件明確且可驗證</li>
<li><input disabled="" type="checkbox"> Review 機制已建立</li>
</ul>
<h4 id="準備度不足的處理">準備度不足的處理</h4>
<p><strong>發現準備度不足時</strong>：</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">Ticket 派工前檢查
</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">    └─ 不通過 → 暫停派工
</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">        ├─ 設計不完整 → 補充設計決策
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">        ├─ 依賴未完成 → 等待依賴 Ticket
</span></span><span class="line"><span class="ln">10</span><span class="cl">        └─ 資源不足 → 準備環境和文檔
</span></span><span class="line"><span class="ln">11</span><span class="cl">        ↓
</span></span><span class="line"><span class="ln">12</span><span class="cl">    重新檢查準備度</span></span></code></pre></div><h3 id="64-ticket-完成後的文件更新">6.4 Ticket 完成後的文件更新</h3>
<h4 id="更新流程">更新流程</h4>
<p>每完成一個 Ticket，必須更新以下文件：</p>
<h5 id="步驟-1更新-ticket-工作日誌">步驟 1：更新 Ticket 工作日誌</h5>
<p>在 <code>vX.Y.Z-ticket-NNN.md</code> 記錄：</p>
<ul>
<li>執行過程</li>
<li>Review 結果</li>
<li>遇到的問題和解決方案</li>
</ul>
<h6 id="步驟-2更新主版本日誌">步驟 2：更新主版本日誌</h6>
<p>在 <code>vX.Y.Z-main.md</code> 更新：</p>
<ul>
<li>Ticket 索引（標記為已完成）</li>
<li>如有新的設計決策，更新設計決策索引</li>
</ul>
<h6 id="步驟-3更新-todolist">步驟 3：更新 todolist</h6>
<p>在 <code>todolist.md</code> 更新：</p>
<ul>
<li>標記對應任務為「已完成」</li>
<li>如發現新任務，新增到待辦清單</li>
</ul>
<h6 id="步驟-4更新設計決策日誌如適用">步驟 4：更新設計決策日誌（如適用）</h6>
<p>在 <code>vX.Y.Z-design-decisions.md</code> 更新：</p>
<ul>
<li>如有設計調整，記錄新的決策版本</li>
<li>如廢棄舊決策，標記廢棄原因</li>
</ul>
<h3 id="65-整合檢查清單">6.5 整合檢查清單</h3>
<p><strong>Ticket 建立檢查</strong>（4 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> Ticket 來自 TDD Phase 1-4 的產出</li>
<li><input disabled="" type="checkbox"> Ticket 類型符合 Clean Architecture 分層</li>
<li><input disabled="" type="checkbox"> Ticket 已連結到設計決策日誌</li>
<li><input disabled="" type="checkbox"> Ticket 準備度檢查通過</li>
</ul>
<p><strong>Ticket 執行檢查</strong>（4 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 執行過程記錄到 Ticket 日誌</li>
<li><input disabled="" type="checkbox"> 完成後更新主版本日誌索引</li>
<li><input disabled="" type="checkbox"> 完成後更新 todolist 狀態</li>
<li><input disabled="" type="checkbox"> 如有設計調整，更新設計決策日誌</li>
</ul>
<p><strong>文件同步檢查</strong>（4 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> Ticket 日誌大小控制在 100-200 行</li>
<li><input disabled="" type="checkbox"> 主版本日誌索引保持最新</li>
<li><input disabled="" type="checkbox"> 設計決策狀態正確標記</li>
<li><input disabled="" type="checkbox"> todolist 任務狀態與實際一致</li>
</ul>
<hr>
<h2 id="第七章實務案例和模板">第七章：實務案例和模板</h2>
<h3 id="71-v0127-工作日誌臃腫問題分析">7.1 v0.12.7 工作日誌臃腫問題分析</h3>
<h4 id="原始問題描述">原始問題描述</h4>
<p><strong>v0.12.7 版本資訊</strong>：</p>
<ul>
<li><strong>功能範圍</strong>：書籍資訊豐富化功能</li>
<li><strong>工作日誌大小</strong>：6000 行</li>
<li><strong>開發時間</strong>：5 天</li>
<li><strong>Ticket 數量</strong>：未明確拆分</li>
</ul>
<p><strong>臃腫問題根因</strong>：</p>
<h5 id="根因-1缺乏-ticket-拆分機制">根因 1：缺乏 Ticket 拆分機制</h5>
<ul>
<li>所有實作細節都寫在主版本日誌</li>
<li>沒有建立獨立的 Ticket 日誌</li>
<li>設計迭代過程全部記錄在同一檔案</li>
</ul>
<h6 id="根因-2設計迭代未分層管理">根因 2：設計迭代未分層管理</h6>
<ul>
<li>初版設計、修正版設計、最終設計混在一起</li>
<li>廢棄的設計沒有明確標記</li>
<li>開發者難以找到最終設計</li>
</ul>
<h6 id="根因-3缺乏文件索引機制">根因 3：缺乏文件索引機制</h6>
<ul>
<li>沒有 Ticket 索引</li>
<li>沒有設計決策索引</li>
<li>開發者需搜尋整個檔案才能找到資訊</li>
</ul>
<h4 id="問題影響分析">問題影響分析</h4>
<h5 id="影響-1開發效率降低">影響 1：開發效率降低</h5>
<ul>
<li>開發者花費 30% 時間搜尋設計資訊</li>
<li>編輯器效能下降（檔案過大）</li>
<li>協作衝突頻繁（多人編輯同一檔案）</li>
</ul>
<h6 id="影響-2品質風險增加">影響 2：品質風險增加</h6>
<ul>
<li>使用過時的設計實作（找錯版本）</li>
<li>Review 困難（難以理解上下文）</li>
<li>技術債務累積（沒時間重構）</li>
</ul>
<h6 id="影響-3知識傳承困難">影響 3：知識傳承困難</h6>
<ul>
<li>新成員難以快速理解設計演進</li>
<li>歷史決策難以追溯</li>
<li>重複犯錯（不知道為什麼廢棄某設計）</li>
</ul>
<h3 id="72-基於-ticket-機制的改善方案">7.2 基於 Ticket 機制的改善方案</h3>
<h4 id="改善方案設計">改善方案設計</h4>
<p><strong>方案目標</strong>：
將 6000 行主版本日誌改善為三層文件結構，控制在約 2000 行總量（800+200*6+400）。</p>
<p><strong>改善步驟</strong>：</p>
<h5 id="步驟-1ticket-拆分回溯分析">步驟 1：Ticket 拆分（回溯分析）</h5>
<p>分析 v0.12.7 的開發過程，回溯拆分為 12 個 Ticket：</p>
<p><strong>Domain 層 Ticket</strong>（3 個）：</p>
<ul>
<li>Ticket #1: 定義 IBookInfoEnrichmentService 介面</li>
<li>Ticket #2: 定義 IEnrichmentStrategy 介面</li>
<li>Ticket #3: 定義 EnrichmentProgress Value Object</li>
</ul>
<p><strong>Application 層 Ticket</strong>（2 個）：</p>
<ul>
<li>Ticket #4: 定義 EnrichBookUseCase 介面</li>
<li>Ticket #5: 實作 EnrichBookInteractor</li>
</ul>
<p><strong>Infrastructure 層 Ticket</strong>（4 個）：</p>
<ul>
<li>Ticket #6: 實作 GoogleBooksStrategy</li>
<li>Ticket #7: 實作 OpenLibraryStrategy</li>
<li>Ticket #8: 實作 BookInfoEnrichmentService</li>
<li>Ticket #9: 實作 Riverpod Provider 註冊</li>
</ul>
<p><strong>測試 Ticket</strong>（3 個）：</p>
<ul>
<li>Ticket #10: 撰寫 BookInfoEnrichmentService 測試</li>
<li>Ticket #11: 撰寫 EnrichBookInteractor 測試</li>
<li>Ticket #12: 撰寫整合測試</li>
</ul>
<h6 id="步驟-2建立三層文件結構">步驟 2：建立三層文件結構</h6>
<p><strong>主版本日誌</strong>（800 行）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gh"># v0.12.7 主版本日誌
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gh"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">## 快速導航
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span><span class="k">-</span> [<span class="nt">版本目標</span>](<span class="na">#版本目標</span>)
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">-</span> [<span class="nt">Ticket 索引</span>](<span class="na">#Ticket索引</span>)（12 個 Ticket）
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">-</span> [<span class="nt">設計決策索引</span>](<span class="na">#設計決策索引</span>)（5 個決策）
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">-</span> [<span class="nt">版本總結</span>](<span class="na">#版本總結</span>)
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">## Ticket 索引
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu">### Domain 層
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu"></span><span class="k">-</span> Ticket <span class="ni">#1:</span> 定義 IBookInfoEnrichmentService 介面 → [<span class="nt">詳細</span>](<span class="na">/record/ticket_v2/v0.12.7-ticket-001.md</span>)
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">-</span> Ticket <span class="ni">#2:</span> 定義 IEnrichmentStrategy 介面 → [<span class="nt">詳細</span>](<span class="na">/record/ticket_v2/v0.12.7-ticket-002.md</span>)
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">-</span> Ticket <span class="ni">#3:</span> 定義 EnrichmentProgress Value Object → [<span class="nt">詳細</span>](<span class="na">/record/ticket_v2/v0.12.7-ticket-003.md</span>)
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="gu">### Application 層
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="gu"></span><span class="k">-</span> Ticket <span class="ni">#4:</span> 定義 EnrichBookUseCase 介面 → [<span class="nt">詳細</span>](<span class="na">/record/ticket_v2/v0.12.7-ticket-004.md</span>)
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">-</span> Ticket <span class="ni">#5:</span> 實作 EnrichBookInteractor → [<span class="nt">詳細</span>](<span class="na">/record/ticket_v2/v0.12.7-ticket-005.md</span>)
</span></span><span class="line"><span class="ln">19</span><span class="cl">...</span></span></code></pre></div><p><strong>Ticket 工作日誌</strong>（200 行 × 12 = 2400 行，分散到 12 個檔案）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gh"># Ticket #1 工作日誌
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gh"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">## Ticket 描述
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>定義 IBookInfoEnrichmentService 介面
</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="gu">### 背景
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span>根據 UC-05 需求，需要建立書籍資訊豐富化服務的介面契約
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">...
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu">## 執行過程
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 建立介面檔案
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">2.</span> 定義方法簽名
</span></span><span class="line"><span class="ln">13</span><span class="cl">...
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="gu">## Review 結果
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="gu"></span>通過（8 分鐘）</span></span></code></pre></div><p><strong>設計決策日誌</strong>（400 行）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gh"># v0.12.7 設計決策日誌
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gh"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">## 決策 #1: 選擇 Strategy Pattern 處理多資料源
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gs">**決策狀態**</span>：最終決策（已實作）
</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 class="gs">**版本歷史**</span>：
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">-</span> v1 (2025-10-09 14:30): 初版設計 - 使用 if-else 判斷（已廢棄）
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">-</span> v2 (2025-10-09 15:00): 修正版 - 改用 Strategy Pattern（最終版本）
</span></span><span class="line"><span class="ln">11</span><span class="cl">...</span></span></code></pre></div><h3 id="步驟-3建立設計文件索引">步驟 3：建立設計文件索引</h3>
<p>在主版本日誌建立清晰的索引：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gu">## 設計決策索引
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="gu">### 服務層設計
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="gu"></span><span class="k">-</span> 決策 <span class="ni">#1:</span> 選擇 Strategy Pattern（最終決策）→ [<span class="nt">詳細</span>](<span class="na">/record/ticket_v2/v0.12.7-design-decisions.md#決策1</span>)
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="k">-</span> 決策 <span class="ni">#2:</span> 使用 Riverpod 管理服務（最終決策）→ [<span class="nt">詳細</span>](<span class="na">/record/ticket_v2/v0.12.7-design-decisions.md#決策2</span>)
</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 class="gu">### 資料來源設計
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="gu"></span><span class="k">-</span> 決策 <span class="ni">#3:</span> Google Books 為主要資料源（最終決策）→ [<span class="nt">詳細</span>](<span class="na">/record/ticket_v2/v0.12.7-design-decisions.md#決策3</span>)
</span></span><span class="line"><span class="ln">9</span><span class="cl">- <span class="gd">~~決策 #4: 豆瓣讀書為備用資料源~~</span>（已廢棄，API 限制）</span></span></code></pre></div><h4 id="改善效果評估">改善效果評估</h4>
<p><strong>檔案大小改善</strong>：</p>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>改善前</th>
          <th>改善後</th>
          <th>改善幅度</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>主版本日誌</strong></td>
          <td>6000 行</td>
          <td>800 行</td>
          <td>-87%</td>
      </tr>
      <tr>
          <td><strong>Ticket 日誌</strong></td>
          <td>0（混在主日誌）</td>
          <td>200 行 × 12 = 2400 行</td>
          <td>分散管理</td>
      </tr>
      <tr>
          <td><strong>設計決策日誌</strong></td>
          <td>0（混在主日誌）</td>
          <td>400 行</td>
          <td>獨立管理</td>
      </tr>
      <tr>
          <td><strong>總計</strong></td>
          <td>6000 行（單一檔案）</td>
          <td>3600 行（14 個檔案）</td>
          <td>-40% 且分散</td>
      </tr>
  </tbody>
</table>
<p><strong>查找效率改善</strong>：</p>
<table>
  <thead>
      <tr>
          <th>任務</th>
          <th>改善前</th>
          <th>改善後</th>
          <th>效率提升</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>找到特定 Ticket</strong></td>
          <td>搜尋 6000 行</td>
          <td>查索引 → 直接開啟</td>
          <td>90% ↑</td>
      </tr>
      <tr>
          <td><strong>找到最終設計</strong></td>
          <td>搜尋全文</td>
          <td>查設計決策索引</td>
          <td>85% ↑</td>
      </tr>
      <tr>
          <td><strong>理解設計演進</strong></td>
          <td>難以追溯</td>
          <td>查版本歷史</td>
          <td>80% ↑</td>
      </tr>
  </tbody>
</table>
<h3 id="73-ticket-模板集合">7.3 Ticket 模板集合</h3>
<h4 id="模板-1interface-定義-ticket">模板 1：Interface 定義 Ticket</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #N: 定義 [Interface 名稱] 介面
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 背景
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></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="gu">### 目標
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span>建立 <span class="sb">`[Interface 名稱]`</span> 介面，定義 [功能描述] 的契約
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 在 <span class="sb">`lib/domains/[domain]/[layer]/`</span> 建立 <span class="sb">`[interface_file].dart`</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">2.</span> 定義 <span class="sb">`[method1]`</span> 方法簽名
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">3.</span> 定義 <span class="sb">`[method2]`</span> 方法簽名
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">4.</span> 撰寫文檔註解
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> Interface 檔案建立在正確位置
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">- [ ]</span> 所有方法簽名完整且明確
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">- [ ]</span> 輸入輸出類型定義清楚
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">- [ ]</span> 包含完整的文檔註解
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="gu">### 參考文件
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="gu"></span><span class="k">-</span> [設計文件連結]
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="k">-</span> [需求文件連結]
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="gu">### 依賴 Ticket
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="gu"></span>- 無（Interface 定義通常是第一個 Ticket）</span></span></code></pre></div><h4 id="模板-2具體實作-ticket">模板 2：具體實作 Ticket</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #N: 實作 [類別名稱]
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 背景
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>根據 [Interface 名稱] 介面契約，實作 [功能描述]
</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="gu">### 目標
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span>實作 <span class="sb">`[類別名稱]`</span>，提供 [功能描述]
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 建立 <span class="sb">`[類別名稱]`</span> 類別
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">2.</span> 實作 <span class="sb">`[method1]`</span> 方法
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">3.</span> 實作 <span class="sb">`[method2]`</span> 方法
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">4.</span> 處理異常情況
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">5.</span> 確保所有測試通過
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 實作所有 [Interface 名稱] 方法
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">- [ ]</span> 異常處理完整（列出可能的異常）
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">- [ ]</span> 單元測試 100% 通過
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="gu">### 參考文件
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="gu"></span><span class="k">-</span> [Interface 定義 Ticket]
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="k">-</span> [設計決策日誌]
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="gu">### 依賴 Ticket
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="gu"></span>- Ticket <span class="ni">#X:</span> 定義 [Interface 名稱] 介面（必須先完成）</span></span></code></pre></div><h4 id="模板-3測試撰寫-ticket">模板 3：測試撰寫 Ticket</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #N: 撰寫 [類別名稱] 測試
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 背景
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>為 <span class="sb">`[類別名稱]`</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="gu">### 目標
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span>撰寫 <span class="sb">`[類別名稱]`</span> 的完整測試用例
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 建立測試檔案 <span class="sb">`[test_file]_test.dart`</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">2.</span> 撰寫 <span class="sb">`[method1]`</span> 測試（正常流程 + 異常處理）
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">3.</span> 撰寫 <span class="sb">`[method2]`</span> 測試（正常流程 + 異常處理）
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">4.</span> 確保所有測試通過
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 測試檔案建立在 <span class="sb">`test/unit/[path]/`</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">- [ ]</span> 至少 [N] 個測試用例
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">- [ ]</span> 覆蓋所有方法的正常流程
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">- [ ]</span> 覆蓋所有方法的異常處理
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">- [ ]</span> 所有測試 100% 通過
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="gu">### 參考文件
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="gu"></span><span class="k">-</span> [Interface 定義 Ticket]
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="k">-</span> [測試設計文件]
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="gu">### 依賴 Ticket
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="gu"></span>- Ticket <span class="ni">#X:</span> 定義 [Interface 名稱] 介面（必須先完成）</span></span></code></pre></div><h4 id="模板-4整合連接-ticket">模板 4：整合連接 Ticket</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #N: 整合 [Module A] 到 [Module B]
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 背景
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>將 <span class="sb">`[Module A]`</span> 整合到 <span class="sb">`[Module B]`</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="gu">### 目標
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span>連接 [Module A] 和 [Module B]，實現端到端功能
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 修改 <span class="sb">`[Module B]`</span> 注入 <span class="sb">`[Module A Interface]`</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">2.</span> 在 <span class="sb">`[method]`</span> 方法中呼叫 <span class="sb">`[Module A].[method]`</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">3.</span> 處理 [Module A] 的異常
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">4.</span> 撰寫整合測試驗證端到端流程
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">5.</span> 確保所有測試通過
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> <span class="sb">`[Module B]`</span> 正確注入 <span class="sb">`[Module A Interface]`</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">- [ ]</span> 端到端流程正常運作
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">- [ ]</span> 整合測試 100% 通過
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">- [ ]</span> 異常處理完整
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="gu">### 參考文件
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="gu"></span><span class="k">-</span> [Module A 實作 Ticket]
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="k">-</span> [Module B 實作 Ticket]
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="k">-</span> [整合設計文件]
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="gu">### 依賴 Ticket
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="gu"></span><span class="k">-</span> Ticket <span class="ni">#X:</span> 實作 [Module A]（必須先完成）
</span></span><span class="line"><span class="ln">29</span><span class="cl">- Ticket <span class="ni">#Y:</span> 實作 [Module B]（必須先完成）</span></span></code></pre></div><h4 id="模板-5重構-ticket">模板 5：重構 Ticket</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Ticket #N: 重構 [功能描述]
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 背景
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span>Phase 4 發現 [問題描述]，需要重構改善
</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="gu">### 目標
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span>重構 <span class="sb">`[類別名稱]`</span>，[改善目標]
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">### 步驟
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 分析當前程式碼問題
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">2.</span> 設計重構方案
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">3.</span> 執行重構（保持測試通過）
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">4.</span> 驗證功能未受影響
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">5.</span> 確保所有測試通過
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="gu">### 驗收條件
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 重構完成，程式碼品質改善
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">- [ ]</span> 所有測試 100% 通過（功能未受影響）
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">- [ ]</span> 無新的技術債務產生
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="gu">### 參考文件
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="gu"></span><span class="k">-</span> [Phase 4 重構報告]
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="k">-</span> [程式碼品質標準]
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="gu">### 依賴 Ticket
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="gu"></span>- 無（重構可獨立進行）</span></span></code></pre></div><h3 id="74-常見錯誤和解決方案">7.4 常見錯誤和解決方案</h3>
<h4 id="錯誤-1ticket-拆分過細或過粗">錯誤 1：Ticket 拆分過細或過粗</h4>
<p><strong>問題描述</strong>：</p>
<ul>
<li><strong>過細</strong>：Ticket 的職責過於單一，失去獨立交付價值</li>
<li><strong>過粗</strong>：Ticket 包含過多職責（&gt;5 個）或檔案（&gt;5 個），失去即時 review 的效益</li>
</ul>
<p><strong>解決方案</strong>：</p>
<ul>
<li><strong>標準複雜度</strong>：控制在單一職責或少數相關職責</li>
<li><strong>過細時</strong>：合併相關的小 Ticket 形成完整職責</li>
<li><strong>過粗時</strong>：按 Clean Architecture 分層或步驟拆分</li>
</ul>
<p><strong>範例</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">反例 - 過細拆分：
</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 class="k">-</span> Ticket <span class="ni">#1:</span> 建立檔案（職責不完整）
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="k">-</span> Ticket <span class="ni">#2:</span> 定義 class（職責不完整）
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="k">-</span> Ticket <span class="ni">#3:</span> 撰寫註解（職責不完整）
</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">
</span></span><span class="line"><span class="ln">9</span><span class="cl">- Ticket <span class="ni">#1:</span> 定義 Interface（包含建立檔案、定義 class、撰寫註解 - 完整職責）</span></span></code></pre></div><h4 id="錯誤-2驗收條件模糊">錯誤 2：驗收條件模糊</h4>
<p><strong>問題描述</strong>：
驗收條件使用主觀描述，無法客觀驗證。</p>
<p><strong>錯誤範例</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">反例 - 模糊的驗收條件：
</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 class="k">- [ ]</span> 功能運作正常
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="k">- [ ]</span> 程式碼品質良好
</span></span><span class="line"><span class="ln">5</span><span class="cl">- [ ] 效能可接受</span></span></code></pre></div><p><strong>正確範例</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">正例 - 明確的驗收條件：
</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 class="k">- [ ]</span> 呼叫 getBookByIsbn(&#39;123&#39;) 回傳正確的 Book 物件
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="k">- [ ]</span> dart analyze 0 錯誤，測試覆蓋率 &gt; 80%
</span></span><span class="line"><span class="ln">5</span><span class="cl">- [ ] API 回應時間 <span class="p">&lt;</span> <span class="nt">500ms</span><span class="err">（</span><span class="na">在</span> <span class="na">100</span> <span class="na">筆資料下</span><span class="err">）</span></span></span></code></pre></div><p><strong>解決方案</strong>：
使用 SMART 原則撰寫驗收條件（Specific, Measurable, Achievable, Relevant, Time-bound）。</p>
<h4 id="錯誤-3忽略-ticket-依賴關係">錯誤 3：忽略 Ticket 依賴關係</h4>
<p><strong>問題描述</strong>：
沒有明確標註 Ticket 間的依賴關係，導致執行順序錯誤。</p>
<p><strong>錯誤範例</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">反例 - 缺少依賴標註：
</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 class="k">-</span> Ticket <span class="ni">#1:</span> 實作 Repository
</span></span><span class="line"><span class="ln">4</span><span class="cl">- Ticket <span class="ni">#2:</span> 定義 Repository Interface</span></span></code></pre></div><p><strong>正確範例</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">正例 - 明確依賴關係：
</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 class="k">-</span> Ticket <span class="ni">#1:</span> 定義 IBookRepository 介面
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="k">-</span> Ticket <span class="ni">#2:</span> 實作 SQLiteBookRepository
</span></span><span class="line"><span class="ln">5</span><span class="cl">  依賴：Ticket <span class="ni">#1</span>（必須先完成）</span></span></code></pre></div><p><strong>解決方案</strong>：</p>
<ul>
<li>Interface 定義 Ticket 優先於實作 Ticket</li>
<li>測試撰寫 Ticket 可與實作 Ticket 並行</li>
<li>整合 Ticket 必須等待所有依賴 Ticket 完成</li>
</ul>
<h4 id="錯誤-4review-累積延後">錯誤 4：Review 累積延後</h4>
<p><strong>問題描述</strong>：
等待多個 Ticket 完成後才一起 review，失去即時 review 的效益。</p>
<p><strong>錯誤做法</strong>：</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">完成 Ticket #1 → 繼續 Ticket #2 → 繼續 Ticket #3 → 一起 Review
</span></span><span class="line"><span class="ln">2</span><span class="cl">（問題：Ticket #1 的錯誤到 Review 時才發現，已完成 #2、#3，需大量返工）</span></span></code></pre></div><p><strong>正確做法</strong>：</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">完成 Ticket #1 → Review #1 → 完成 Ticket #2 → Review #2 → 完成 Ticket #3 → Review #3
</span></span><span class="line"><span class="ln">2</span><span class="cl">（好處：每個 Ticket 完成後立即發現問題，修正成本低）</span></span></code></pre></div><p><strong>解決方案</strong>：</p>
<ul>
<li>每完成一個 Ticket 立即觸發 review</li>
<li>Review 快速完成，聚焦核心問題</li>
<li>發現問題立即建立修正 Ticket</li>
</ul>
<h4 id="錯誤-5工作日誌未同步更新">錯誤 5：工作日誌未同步更新</h4>
<p><strong>問題描述</strong>：
Ticket 完成後未更新工作日誌，導致文件與實際進度不一致。</p>
<p><strong>解決方案</strong>：
每完成一個 Ticket 必須執行以下更新：</p>
<ol>
<li>更新 Ticket 工作日誌（記錄執行過程和 Review 結果）</li>
<li>更新主版本日誌 Ticket 索引（標記為已完成）</li>
<li>更新 todolist 任務狀態</li>
<li>如有設計調整，更新設計決策日誌</li>
</ol>
<h3 id="75-完整-ticket-設計檢查清單">7.5 完整 Ticket 設計檢查清單</h3>
<h4 id="ticket-建立階段檢查清單10-項">Ticket 建立階段檢查清單（10 項）</h4>
<p><strong>基本資訊</strong>（3 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> Ticket 標題格式正確（動詞 + 目標）</li>
<li><input disabled="" type="checkbox"> Ticket 編號唯一且連續</li>
<li><input disabled="" type="checkbox"> Ticket 分類明確（Interface/實作/測試/整合/重構）</li>
</ul>
<p><strong>內容完整性</strong>（6 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 背景欄位說明來源（需求或設計決策）</li>
<li><input disabled="" type="checkbox"> 目標欄位明確且可驗證</li>
<li><input disabled="" type="checkbox"> 步驟欄位具體且可操作（3-5 步）</li>
<li><input disabled="" type="checkbox"> 驗收條件符合 SMART 原則（3-5 項）</li>
<li><input disabled="" type="checkbox"> 參考文件連結正確</li>
<li><input disabled="" type="checkbox"> 依賴 Ticket 明確標註</li>
</ul>
<h4 id="ticket-執行階段檢查清單8-項">Ticket 執行階段檢查清單（8 項）</h4>
<p><strong>執行前檢查</strong>（3 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 依賴 Ticket 已完成</li>
<li><input disabled="" type="checkbox"> 相關設計決策已確認為「最終決策」</li>
<li><input disabled="" type="checkbox"> 開發者已理解目標和步驟</li>
</ul>
<p><strong>執行中檢查</strong>（3 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> Ticket 標記為「進行中」</li>
<li><input disabled="" type="checkbox"> 執行過程記錄到 Ticket 日誌</li>
<li><input disabled="" type="checkbox"> 遇到問題即時記錄</li>
</ul>
<p><strong>執行後檢查</strong>（2 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 所有驗收條件已滿足</li>
<li><input disabled="" type="checkbox"> Ticket 標記為「Review 中」</li>
</ul>
<h4 id="ticket-review-階段檢查清單16-項">Ticket Review 階段檢查清單（16 項）</h4>
<p><strong>功能正確性</strong>（4 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> Ticket 描述的功能是否實現？</li>
<li><input disabled="" type="checkbox"> 驗收條件是否全部滿足？</li>
<li><input disabled="" type="checkbox"> 是否有未處理的邊界情況？</li>
<li><input disabled="" type="checkbox"> 錯誤處理是否完整？</li>
</ul>
<p><strong>架構合規性</strong>（4 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 是否符合 Clean Architecture 分層原則？</li>
<li><input disabled="" type="checkbox"> 依賴方向是否正確（內層不依賴外層）？</li>
<li><input disabled="" type="checkbox"> 是否使用 Interface-Driven 開發？</li>
<li><input disabled="" type="checkbox"> 是否有架構債務產生？</li>
</ul>
<p><strong>測試通過率</strong>（4 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 相關單元測試是否 100% 通過？</li>
<li><input disabled="" type="checkbox"> 相關整合測試是否 100% 通過？</li>
<li><input disabled="" type="checkbox"> 測試覆蓋率是否達標（&gt; 80%）？</li>
<li><input disabled="" type="checkbox"> 是否有測試被 skip？</li>
</ul>
<p><strong>文檔同步性</strong>（4 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> Ticket 工作日誌是否更新？</li>
<li><input disabled="" type="checkbox"> 設計決策是否記錄？</li>
<li><input disabled="" type="checkbox"> API 文檔是否同步？</li>
<li><input disabled="" type="checkbox"> README 是否需要更新？</li>
</ul>
<h4 id="ticket-關閉階段檢查清單8-項">Ticket 關閉階段檢查清單（8 項）</h4>
<p><strong>強制條件</strong>（5 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 所有驗收條件打勾完成</li>
<li><input disabled="" type="checkbox"> Review 通過</li>
<li><input disabled="" type="checkbox"> 相關測試 100% 通過</li>
<li><input disabled="" type="checkbox"> dart analyze 0 錯誤</li>
<li><input disabled="" type="checkbox"> 工作日誌已更新</li>
</ul>
<p><strong>建議條件</strong>（3 項）：</p>
<ul>
<li><input disabled="" type="checkbox"> 程式碼符合專案規範</li>
<li><input disabled="" type="checkbox"> 無技術債務產生</li>
<li><input disabled="" type="checkbox"> 文檔同步更新</li>
</ul>
<hr>
<p><strong>文件版本</strong>: v1.0.0（完整版）
<strong>最後更新</strong>: 2025-10-10</p>
]]></content:encoded></item><item><title>如何建立AI輔助系統開發</title><link>https://tarrragon.github.io/blog/record/%E5%A6%82%E4%BD%95%E5%BB%BA%E7%AB%8Bai%E8%BC%94%E5%8A%A9%E7%B3%BB%E7%B5%B1%E9%96%8B%E7%99%BC/</link><pubDate>Sat, 11 Oct 2025 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/%E5%A6%82%E4%BD%95%E5%BB%BA%E7%AB%8Bai%E8%BC%94%E5%8A%A9%E7%B3%BB%E7%B5%B1%E9%96%8B%E7%99%BC/</guid><description>&lt;h2 id="說明">說明&lt;/h2>
&lt;p>一開始我是希望 AI 可以主動閱讀方法論然後依照我們的方法論執行，這是有效的，但是 AI 並不會總是都會記得方法論的內容，也不會完全依照方法論執行，所以在設計方法論之後，加入了代理人跟 hook 去輔助（強制）讓開發流程跟開發方式都依照我們的方法論執行&lt;/p>
&lt;h3 id="理論文件定義應該怎麼做規範">理論文件定義「應該怎麼做」（規範）&lt;/h3>
&lt;p>我們跟 AI 說明我們要的開發流程，觀念，原則，讓AI幫我們產生方法論的文章，然後再細修方法論的內容，也可以請AI來撰寫範例的部分&lt;/p>
&lt;h3 id="代理人和-hook-負責確保做到執行和監控">代理人和 Hook 負責「確保做到」（執行和監控）&lt;/h3>
&lt;p>因為我們設計了敏捷開發方法論，所以針對不同的工作我們會要求主線程派工給代理人執行，而我們要確保每個代理人都有完整強調部分他需要知道的方法論內容，而不只是需要的時候去參考那個方法論。&lt;/p>
&lt;h3 id="必須確保自動化機制能夠執行和監控新方法論">必須確保自動化機制能夠執行和監控新方法論&lt;/h3>
&lt;p>然而以生成式 AI 來說，所有的產出終究是不可靠的，方法論只是儘量讓程式產出的的內容趨近於我們的要求，所以必須用hook監控，在指派工作以及驗收工作的時候 HOOK 就可以介入檢查工作內容是否滿足我們設定的方法論條件，來確保工作的品質。&lt;/p>
&lt;p>而這部份的 HOOK 不需要自己撰寫，其實交給 AI 閱讀方法論之後讓他自由發揮就好了，我們可以後續依照實際觸發的狀況再做調整，但是關注點還是在我們的方法論上，是不是方法論的內容有疏漏需要補充，是不是有條件寫得太模糊導致判斷失敗，而不是去檢討這些 hook 腳本的品質。&lt;/p>
&lt;h3 id="建立從發現問題到強制修正的完整閉環">建立從「發現問題」到「強制修正」的完整閉環&lt;/h3>
&lt;p>所以方法論的一部分核心論述就必須包含如何發現問題，以及怎麼設計這個強制修正的流程，當然我們對於方法論的內容理解深度，會影響這部份設計的結果。&lt;/p></description><content:encoded><![CDATA[<h2 id="說明">說明</h2>
<p>一開始我是希望 AI 可以主動閱讀方法論然後依照我們的方法論執行，這是有效的，但是 AI 並不會總是都會記得方法論的內容，也不會完全依照方法論執行，所以在設計方法論之後，加入了代理人跟 hook 去輔助（強制）讓開發流程跟開發方式都依照我們的方法論執行</p>
<h3 id="理論文件定義應該怎麼做規範">理論文件定義「應該怎麼做」（規範）</h3>
<p>我們跟 AI 說明我們要的開發流程，觀念，原則，讓AI幫我們產生方法論的文章，然後再細修方法論的內容，也可以請AI來撰寫範例的部分</p>
<h3 id="代理人和-hook-負責確保做到執行和監控">代理人和 Hook 負責「確保做到」（執行和監控）</h3>
<p>因為我們設計了敏捷開發方法論，所以針對不同的工作我們會要求主線程派工給代理人執行，而我們要確保每個代理人都有完整強調部分他需要知道的方法論內容，而不只是需要的時候去參考那個方法論。</p>
<h3 id="必須確保自動化機制能夠執行和監控新方法論">必須確保自動化機制能夠執行和監控新方法論</h3>
<p>然而以生成式 AI 來說，所有的產出終究是不可靠的，方法論只是儘量讓程式產出的的內容趨近於我們的要求，所以必須用hook監控，在指派工作以及驗收工作的時候 HOOK 就可以介入檢查工作內容是否滿足我們設定的方法論條件，來確保工作的品質。</p>
<p>而這部份的 HOOK 不需要自己撰寫，其實交給 AI 閱讀方法論之後讓他自由發揮就好了，我們可以後續依照實際觸發的狀況再做調整，但是關注點還是在我們的方法論上，是不是方法論的內容有疏漏需要補充，是不是有條件寫得太模糊導致判斷失敗，而不是去檢討這些 hook 腳本的品質。</p>
<h3 id="建立從發現問題到強制修正的完整閉環">建立從「發現問題」到「強制修正」的完整閉環</h3>
<p>所以方法論的一部分核心論述就必須包含如何發現問題，以及怎麼設計這個強制修正的流程，當然我們對於方法論的內容理解深度，會影響這部份設計的結果。</p>
]]></content:encoded></item><item><title>敏捷開發方法論</title><link>https://tarrragon.github.io/blog/record/%E6%95%8F%E6%8D%B7%E9%96%8B%E7%99%BC%E6%96%B9%E6%B3%95%E8%AB%96/</link><pubDate>Wed, 08 Oct 2025 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/%E6%95%8F%E6%8D%B7%E9%96%8B%E7%99%BC%E6%96%B9%E6%B3%95%E8%AB%96/</guid><description>&lt;h2 id="方法論概述">方法論概述&lt;/h2>
&lt;p>本方法論定義敏捷開發流程，採用主線程統籌分派、子代理人專責執行的協作模式，確保重構品質和進度控制。
但是在AI開發過程中，不會百分之百按照文件要求執行，所以需要配合hook機制在轉階段的時候檢查是否符合TDD流程，每一層的文件是否有確實填寫，指派任務給代理人之前，文件是否有提供完整的規格資料以及功能描述，不然執行上還是會有偏差，但是依照這套機制可以及早發現出錯的狀況及早糾正&lt;/p>
&lt;p>這篇方法論是延續之前敏捷重構方法論補充內容，但是因為內容更大更完整，所以獨立一篇文章而不是更新舊的敏捷重構方法論(實際專案中是取代舊的方法論)。&lt;/p>
&lt;h2 id="核心原則">核心原則&lt;/h2>
&lt;h3 id="1-主線程職責專一化">1. 主線程職責專一化&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>只負責任務分派和統籌管理&lt;/strong>，絕不親自執行具體重構工作&lt;/li>
&lt;li>依照工作日誌規劃分派任務給相應的子代理人&lt;/li>
&lt;li>維持敏捷開發的節奏和品質標準&lt;/li>
&lt;/ul>
&lt;h3 id="2-任務原子化拆分">2. 任務原子化拆分&lt;/h3>
&lt;ul>
&lt;li>每個小版本任務必須在 1-2 小時內完成&lt;/li>
&lt;li>任務影響檔案數量控制在 5 個以下&lt;/li>
&lt;li>每個任務都有明確的完成標準和驗收條件&lt;/li>
&lt;/ul>
&lt;h3 id="3-品質門檻強制執行">3. 品質門檻強制執行&lt;/h3>
&lt;ul>
&lt;li>每個任務完成後必須通過測試檢查&lt;/li>
&lt;li>重構代理人強制驗證程式碼品質&lt;/li>
&lt;li>100% 測試通過率是最低要求&lt;/li>
&lt;/ul>
&lt;h2 id="agent-角色定義">Agent 角色定義&lt;/h2>
&lt;h3 id="主線程-main-thread">主線程 (Main Thread)&lt;/h3>
&lt;p>&lt;strong>職責&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>依照主版本工作日誌分派任務&lt;/li>
&lt;li>維持敏捷開發節奏&lt;/li>
&lt;li>監控整體進度和品質&lt;/li>
&lt;li>處理升級請求和任務重新分派&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>禁止行為&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>親自閱讀或修改程式碼&lt;/li>
&lt;li>執行具體的重構工作&lt;/li>
&lt;li>繞過子代理人直接操作&lt;/li>
&lt;/ul>
&lt;h3 id="子代理人-sub-agents">子代理人 (Sub-Agents)&lt;/h3>
&lt;p>&lt;strong>職責&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>執行指派的具體重構任務&lt;/li>
&lt;li>回報任務完成狀態和結果&lt;/li>
&lt;li>發現任務規模過大時向上回報&lt;/li>
&lt;li>確保任務符合完成標準&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>專業分工&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>lavender-interface-designer&lt;/strong>: TDD Phase 1 功能設計&lt;/li>
&lt;li>&lt;strong>sage-test-architect&lt;/strong>: TDD Phase 2 測試設計&lt;/li>
&lt;li>&lt;strong>pepper-test-implementer&lt;/strong>: TDD Phase 3 實作規劃&lt;/li>
&lt;li>&lt;strong>cinnamon-refactor-owl&lt;/strong>: TDD Phase 4 重構執行&lt;/li>
&lt;li>&lt;strong>mint-format-specialist&lt;/strong>: 程式碼格式化和品質修正&lt;/li>
&lt;/ul>
&lt;h3 id="重構代理人-refactor-validator">重構代理人 (Refactor Validator)&lt;/h3>
&lt;p>&lt;strong>職責&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>檢查子代理人完成的重構工作&lt;/li>
&lt;li>驗證程式碼品質和功能正確性&lt;/li>
&lt;li>確認測試通過率和效能標準&lt;/li>
&lt;li>批准任務完成或要求修正&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>代理人&lt;/strong>: &lt;code>cinnamon-refactor-owl&lt;/code>&lt;/p>
&lt;h3 id="pm-代理人-project-manager">PM 代理人 (Project Manager)&lt;/h3>
&lt;p>&lt;strong>職責&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>任務規模過大時進行二次拆分&lt;/li>
&lt;li>更新小版本工作日誌&lt;/li>
&lt;li>重新規劃任務執行順序&lt;/li>
&lt;li>管理任務依賴關係&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>代理人&lt;/strong>: &lt;code>rosemary-project-manager&lt;/code>&lt;/p>
&lt;h3 id="文件代理人-documentation-agent">文件代理人 (Documentation Agent)&lt;/h3>
&lt;p>&lt;strong>職責&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>更新小版本工作日誌&lt;/li>
&lt;li>標記小版本完成狀態&lt;/li>
&lt;li>記錄任務執行結果&lt;/li>
&lt;li>同步文件狀態&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>代理人&lt;/strong>: &lt;code>memory-network-builder&lt;/code>&lt;/p>
&lt;h2 id="任務分派前強制檢查清單">任務分派前強制檢查清單&lt;/h2>
&lt;p>&lt;strong>重要&lt;/strong>: 在分派任何重構任務前，主線程必須先通過以下檢查清單。如果無法回答這些問題，必須優先建立準備文件。&lt;/p>
&lt;h3 id="準備度檢查問題">準備度檢查問題&lt;/h3>
&lt;h4 id="1-明確的文件規劃">1. 明確的文件規劃&lt;/h4>
&lt;ul>
&lt;li>&lt;input disabled="" type="checkbox"> &lt;strong>API 規格是否完整？&lt;/strong> 代理人知道要實作什麼樣的介面和行為嗎？&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> &lt;strong>設計文件是否具體？&lt;/strong> 有詳細的類別定義、方法簽名、使用範例嗎？&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> &lt;strong>架構圖是否清晰？&lt;/strong> 代理人知道新系統的整體架構和元件關係嗎？&lt;/li>
&lt;/ul>
&lt;h4 id="2-測試先行策略">2. 測試先行策略&lt;/h4>
&lt;ul>
&lt;li>&lt;input disabled="" type="checkbox"> &lt;strong>測試規格是否存在？&lt;/strong> 每個元件都有對應的測試規格和驗收標準嗎？&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> &lt;strong>TDD 流程是否明確？&lt;/strong> 代理人知道要先寫什麼測試再實作嗎？&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> &lt;strong>效能標準是否定義？&lt;/strong> 有明確的效能基準和測試方法嗎？&lt;/li>
&lt;/ul>
&lt;h4 id="3-實作目標明確性">3. 實作目標明確性&lt;/h4>
&lt;ul>
&lt;li>&lt;input disabled="" type="checkbox"> &lt;strong>完成標準是否可測量？&lt;/strong> 代理人知道什麼程度才算任務完成嗎？&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> &lt;strong>使用範例是否充足？&lt;/strong> 有具體的程式碼範例展示最終產品嗎？&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> &lt;strong>移轉策略是否清楚？&lt;/strong> 有舊系統到新系統的具體移轉指引嗎？&lt;/li>
&lt;/ul>
&lt;h4 id="4-風險評估與應對">4. 風險評估與應對&lt;/h4>
&lt;ul>
&lt;li>&lt;input disabled="" type="checkbox"> &lt;strong>潛在問題是否識別？&lt;/strong> 有預見可能的技術難點和解決方案嗎？&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> &lt;strong>回滾計畫是否準備？&lt;/strong> 出問題時有明確的回滾和修復策略嗎？&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> &lt;strong>依賴關係是否梳理？&lt;/strong> 知道任務間的依賴順序和影響範圍嗎？&lt;/li>
&lt;/ul>
&lt;h4 id="5-參考文件和影響範圍完整性強制">5. 參考文件和影響範圍完整性（強制）&lt;/h4>
&lt;ul>
&lt;li>&lt;input disabled="" type="checkbox"> &lt;strong>UseCase 參考是否明確？&lt;/strong> 工作日誌是否列出對應的 UseCase 和業務需求？&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> &lt;strong>流程圖 Event 是否具體？&lt;/strong> 是否指定要實作流程圖的哪些 Event 編號和對應類別/方法？&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> &lt;strong>架構規範是否引用？&lt;/strong> 是否明確列出適用的架構規範文件（Clean Architecture、DDD、錯誤處理等）？&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> &lt;strong>依賴類別是否列舉？&lt;/strong> 是否列出前置任務產出的依賴類別和版本號？&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> &lt;strong>測試設計是否參考？&lt;/strong> 是否明確測試檔案、測試數量和效能基準？&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> &lt;strong>影響檔案是否清單化？&lt;/strong> 是否完整列出需建立、修改、影響的檔案清單？&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> &lt;strong>影響範圍是否評估？&lt;/strong> 是否評估對上層模組和下層模組的影響？&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>違規處理&lt;/strong>: 缺少任何一項參考文件或影響範圍資訊，視為&lt;strong>任務規劃不合格&lt;/strong>，必須立即補充後才能分派給代理人執行。&lt;/p>
&lt;h4 id="6-設計面效能考量前後端通用">6. 設計面效能考量（前後端通用）&lt;/h4>
&lt;ul>
&lt;li>&lt;input disabled="" type="checkbox"> &lt;strong>效能瓶頸是否識別？&lt;/strong> 是否分析潛在的效能瓶頸（大量資料處理、複雜運算、API 等待、資料庫查詢）？&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> &lt;strong>優化策略是否規劃？&lt;/strong> 是否規劃對應的優化策略（異步處理、Isolate、快取機制、分頁載入、索引優化）？&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> &lt;strong>資源使用是否評估？&lt;/strong> 是否評估記憶體使用、網路請求數量、CPU 負載、儲存空間需求？&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> &lt;strong>UI 阻塞是否避免？&lt;/strong> (前端) 是否確保 UI 主執行緒不被阻塞（異步操作、Loading State、compute/Isolate）？&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> &lt;strong>回應時間是否設計？&lt;/strong> (後端) 是否設計 API 回應時間目標和逾時處理機制？&lt;/li>
&lt;li>&lt;input disabled="" type="checkbox"> &lt;strong>效能基準是否設定？&lt;/strong> 是否定義具體的效能目標和測量方式（載入時間、渲染幀率、API 回應時間）？&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>適用範圍&lt;/strong>:&lt;/p></description><content:encoded><![CDATA[<h2 id="方法論概述">方法論概述</h2>
<p>本方法論定義敏捷開發流程，採用主線程統籌分派、子代理人專責執行的協作模式，確保重構品質和進度控制。
但是在AI開發過程中，不會百分之百按照文件要求執行，所以需要配合hook機制在轉階段的時候檢查是否符合TDD流程，每一層的文件是否有確實填寫，指派任務給代理人之前，文件是否有提供完整的規格資料以及功能描述，不然執行上還是會有偏差，但是依照這套機制可以及早發現出錯的狀況及早糾正</p>
<p>這篇方法論是延續之前敏捷重構方法論補充內容，但是因為內容更大更完整，所以獨立一篇文章而不是更新舊的敏捷重構方法論(實際專案中是取代舊的方法論)。</p>
<h2 id="核心原則">核心原則</h2>
<h3 id="1-主線程職責專一化">1. 主線程職責專一化</h3>
<ul>
<li><strong>只負責任務分派和統籌管理</strong>，絕不親自執行具體重構工作</li>
<li>依照工作日誌規劃分派任務給相應的子代理人</li>
<li>維持敏捷開發的節奏和品質標準</li>
</ul>
<h3 id="2-任務原子化拆分">2. 任務原子化拆分</h3>
<ul>
<li>每個小版本任務必須在 1-2 小時內完成</li>
<li>任務影響檔案數量控制在 5 個以下</li>
<li>每個任務都有明確的完成標準和驗收條件</li>
</ul>
<h3 id="3-品質門檻強制執行">3. 品質門檻強制執行</h3>
<ul>
<li>每個任務完成後必須通過測試檢查</li>
<li>重構代理人強制驗證程式碼品質</li>
<li>100% 測試通過率是最低要求</li>
</ul>
<h2 id="agent-角色定義">Agent 角色定義</h2>
<h3 id="主線程-main-thread">主線程 (Main Thread)</h3>
<p><strong>職責</strong>：</p>
<ul>
<li>依照主版本工作日誌分派任務</li>
<li>維持敏捷開發節奏</li>
<li>監控整體進度和品質</li>
<li>處理升級請求和任務重新分派</li>
</ul>
<p><strong>禁止行為</strong>：</p>
<ul>
<li>親自閱讀或修改程式碼</li>
<li>執行具體的重構工作</li>
<li>繞過子代理人直接操作</li>
</ul>
<h3 id="子代理人-sub-agents">子代理人 (Sub-Agents)</h3>
<p><strong>職責</strong>：</p>
<ul>
<li>執行指派的具體重構任務</li>
<li>回報任務完成狀態和結果</li>
<li>發現任務規模過大時向上回報</li>
<li>確保任務符合完成標準</li>
</ul>
<p><strong>專業分工</strong>：</p>
<ul>
<li><strong>lavender-interface-designer</strong>: TDD Phase 1 功能設計</li>
<li><strong>sage-test-architect</strong>: TDD Phase 2 測試設計</li>
<li><strong>pepper-test-implementer</strong>: TDD Phase 3 實作規劃</li>
<li><strong>cinnamon-refactor-owl</strong>: TDD Phase 4 重構執行</li>
<li><strong>mint-format-specialist</strong>: 程式碼格式化和品質修正</li>
</ul>
<h3 id="重構代理人-refactor-validator">重構代理人 (Refactor Validator)</h3>
<p><strong>職責</strong>：</p>
<ul>
<li>檢查子代理人完成的重構工作</li>
<li>驗證程式碼品質和功能正確性</li>
<li>確認測試通過率和效能標準</li>
<li>批准任務完成或要求修正</li>
</ul>
<p><strong>代理人</strong>: <code>cinnamon-refactor-owl</code></p>
<h3 id="pm-代理人-project-manager">PM 代理人 (Project Manager)</h3>
<p><strong>職責</strong>：</p>
<ul>
<li>任務規模過大時進行二次拆分</li>
<li>更新小版本工作日誌</li>
<li>重新規劃任務執行順序</li>
<li>管理任務依賴關係</li>
</ul>
<p><strong>代理人</strong>: <code>rosemary-project-manager</code></p>
<h3 id="文件代理人-documentation-agent">文件代理人 (Documentation Agent)</h3>
<p><strong>職責</strong>：</p>
<ul>
<li>更新小版本工作日誌</li>
<li>標記小版本完成狀態</li>
<li>記錄任務執行結果</li>
<li>同步文件狀態</li>
</ul>
<p><strong>代理人</strong>: <code>memory-network-builder</code></p>
<h2 id="任務分派前強制檢查清單">任務分派前強制檢查清單</h2>
<p><strong>重要</strong>: 在分派任何重構任務前，主線程必須先通過以下檢查清單。如果無法回答這些問題，必須優先建立準備文件。</p>
<h3 id="準備度檢查問題">準備度檢查問題</h3>
<h4 id="1-明確的文件規劃">1. 明確的文件規劃</h4>
<ul>
<li><input disabled="" type="checkbox"> <strong>API 規格是否完整？</strong> 代理人知道要實作什麼樣的介面和行為嗎？</li>
<li><input disabled="" type="checkbox"> <strong>設計文件是否具體？</strong> 有詳細的類別定義、方法簽名、使用範例嗎？</li>
<li><input disabled="" type="checkbox"> <strong>架構圖是否清晰？</strong> 代理人知道新系統的整體架構和元件關係嗎？</li>
</ul>
<h4 id="2-測試先行策略">2. 測試先行策略</h4>
<ul>
<li><input disabled="" type="checkbox"> <strong>測試規格是否存在？</strong> 每個元件都有對應的測試規格和驗收標準嗎？</li>
<li><input disabled="" type="checkbox"> <strong>TDD 流程是否明確？</strong> 代理人知道要先寫什麼測試再實作嗎？</li>
<li><input disabled="" type="checkbox"> <strong>效能標準是否定義？</strong> 有明確的效能基準和測試方法嗎？</li>
</ul>
<h4 id="3-實作目標明確性">3. 實作目標明確性</h4>
<ul>
<li><input disabled="" type="checkbox"> <strong>完成標準是否可測量？</strong> 代理人知道什麼程度才算任務完成嗎？</li>
<li><input disabled="" type="checkbox"> <strong>使用範例是否充足？</strong> 有具體的程式碼範例展示最終產品嗎？</li>
<li><input disabled="" type="checkbox"> <strong>移轉策略是否清楚？</strong> 有舊系統到新系統的具體移轉指引嗎？</li>
</ul>
<h4 id="4-風險評估與應對">4. 風險評估與應對</h4>
<ul>
<li><input disabled="" type="checkbox"> <strong>潛在問題是否識別？</strong> 有預見可能的技術難點和解決方案嗎？</li>
<li><input disabled="" type="checkbox"> <strong>回滾計畫是否準備？</strong> 出問題時有明確的回滾和修復策略嗎？</li>
<li><input disabled="" type="checkbox"> <strong>依賴關係是否梳理？</strong> 知道任務間的依賴順序和影響範圍嗎？</li>
</ul>
<h4 id="5-參考文件和影響範圍完整性強制">5. 參考文件和影響範圍完整性（強制）</h4>
<ul>
<li><input disabled="" type="checkbox"> <strong>UseCase 參考是否明確？</strong> 工作日誌是否列出對應的 UseCase 和業務需求？</li>
<li><input disabled="" type="checkbox"> <strong>流程圖 Event 是否具體？</strong> 是否指定要實作流程圖的哪些 Event 編號和對應類別/方法？</li>
<li><input disabled="" type="checkbox"> <strong>架構規範是否引用？</strong> 是否明確列出適用的架構規範文件（Clean Architecture、DDD、錯誤處理等）？</li>
<li><input disabled="" type="checkbox"> <strong>依賴類別是否列舉？</strong> 是否列出前置任務產出的依賴類別和版本號？</li>
<li><input disabled="" type="checkbox"> <strong>測試設計是否參考？</strong> 是否明確測試檔案、測試數量和效能基準？</li>
<li><input disabled="" type="checkbox"> <strong>影響檔案是否清單化？</strong> 是否完整列出需建立、修改、影響的檔案清單？</li>
<li><input disabled="" type="checkbox"> <strong>影響範圍是否評估？</strong> 是否評估對上層模組和下層模組的影響？</li>
</ul>
<p><strong>違規處理</strong>: 缺少任何一項參考文件或影響範圍資訊，視為<strong>任務規劃不合格</strong>，必須立即補充後才能分派給代理人執行。</p>
<h4 id="6-設計面效能考量前後端通用">6. 設計面效能考量（前後端通用）</h4>
<ul>
<li><input disabled="" type="checkbox"> <strong>效能瓶頸是否識別？</strong> 是否分析潛在的效能瓶頸（大量資料處理、複雜運算、API 等待、資料庫查詢）？</li>
<li><input disabled="" type="checkbox"> <strong>優化策略是否規劃？</strong> 是否規劃對應的優化策略（異步處理、Isolate、快取機制、分頁載入、索引優化）？</li>
<li><input disabled="" type="checkbox"> <strong>資源使用是否評估？</strong> 是否評估記憶體使用、網路請求數量、CPU 負載、儲存空間需求？</li>
<li><input disabled="" type="checkbox"> <strong>UI 阻塞是否避免？</strong> (前端) 是否確保 UI 主執行緒不被阻塞（異步操作、Loading State、compute/Isolate）？</li>
<li><input disabled="" type="checkbox"> <strong>回應時間是否設計？</strong> (後端) 是否設計 API 回應時間目標和逾時處理機制？</li>
<li><input disabled="" type="checkbox"> <strong>效能基準是否設定？</strong> 是否定義具體的效能目標和測量方式（載入時間、渲染幀率、API 回應時間）？</li>
</ul>
<p><strong>適用範圍</strong>:</p>
<ul>
<li>前端開發（UI 渲染、資料處理、狀態管理）</li>
<li>Backend API 開發（查詢優化、快取策略、並發處理）</li>
<li>資料處理邏輯（JSON 解析、大量計算、批次處理）</li>
<li>所有可能影響使用者體驗的功能</li>
</ul>
<p><strong>強制要求</strong>:</p>
<ul>
<li>Phase 1 設計規格必須包含「效能考量」章節</li>
<li>所有任務分派都需要評估效能影響</li>
<li>無論前後端開發，都要從設計面預防效能問題</li>
<li>效能問題在設計階段解決，而非依賴測試發現</li>
</ul>
<p><strong>參考文件</strong>:</p>
<ul>
<li>TDD 協作流程 - Phase 4-1 UI 效能檢查</li>
<li>測試金字塔設計 - 效能測試策略</li>
<li>Widget 測試指導原則 - 禁止效能測試</li>
</ul>
<p><strong>違規處理</strong>: 缺少效能考量分析，視為<strong>設計規劃不完整</strong>，必須補充效能分析後才能分派任務。</p>
<h3 id="準備度檢查失敗處理">準備度檢查失敗處理</h3>
<p><strong>如果任何檢查項目回答為「否」，必須執行以下優先動作</strong>：</p>
<h4 id="優先動作1-建立-api-規格文件">優先動作1: 建立 API 規格文件</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">檔案: docs/[feature-name]-api-specification.md
</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 class="k">-</span> 完整的介面定義
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="k">-</span> 詳細的方法簽名
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="k">-</span> 具體的使用範例
</span></span><span class="line"><span class="ln">6</span><span class="cl">- 效能要求規格</span></span></code></pre></div><h4 id="優先動作2-建立-tdd-測試規格">優先動作2: 建立 TDD 測試規格</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">檔案: docs/[feature-name]-test-specification.md
</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 class="k">-</span> 每個元件的測試案例
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="k">-</span> 驗收測試標準
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="k">-</span> 效能基準測試
</span></span><span class="line"><span class="ln">6</span><span class="cl">- 整合測試場景</span></span></code></pre></div><h4 id="優先動作3-建立移轉指引">優先動作3: 建立移轉指引</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">檔案: docs/[feature-name]-migration-guide.md
</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 class="k">-</span> 舊系統→新系統對照表
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="k">-</span> 常見使用場景範例
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="k">-</span> 最佳實踐指引
</span></span><span class="line"><span class="ln">6</span><span class="cl">- 問題排除手冊</span></span></code></pre></div><h4 id="優先動作4-建立實作範例">優先動作4: 建立實作範例</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">檔案: docs/[feature-name]-implementation-examples.md
</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 class="k">-</span> 完整的程式碼範例
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="k">-</span> 使用情境示範
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="k">-</span> 常見模式展示
</span></span><span class="line"><span class="ln">6</span><span class="cl">- 最佳實踐指引</span></span></code></pre></div><h3 id="準備文件建立流程">準備文件建立流程</h3>
<h4 id="文件建立責任分工">文件建立責任分工</h4>
<ul>
<li><strong>API 規格</strong>: lavender-interface-designer (功能設計專家)</li>
<li><strong>測試規格</strong>: sage-test-architect (測試設計專家)</li>
<li><strong>移轉指引</strong>: memory-network-builder (文件專家)</li>
<li><strong>實作範例</strong>: pepper-test-implementer (實作規劃專家)</li>
</ul>
<h4 id="文件品質標準">文件品質標準</h4>
<ul>
<li><strong>完整性</strong>: 涵蓋所有必要資訊，無遺漏</li>
<li><strong>具體性</strong>: 提供可執行的詳細指引，非概念性描述</li>
<li><strong>一致性</strong>: 與整體架構和設計原則保持一致</li>
<li><strong>可測試性</strong>: 所有規格都可以通過測試驗證</li>
</ul>
<h2 id="三重文件原則-triple-document-principle">三重文件原則 (Triple Document Principle)</h2>
<p>本方法論採用三重文件機制確保不同層級的資訊流動和代理人協調。</p>
<h3 id="文件層級定義">文件層級定義</h3>
<h4 id="1-changelogmd---版本功能變動記錄">1. CHANGELOG.md - 版本功能變動記錄</h4>
<p><strong>職責</strong>：</p>
<ul>
<li>面向用戶的版本功能描述</li>
<li>只記錄「做了什麼」，不記錄「怎麼做」</li>
<li>版本發布時的正式變更說明</li>
<li>符合 Keep a Changelog 規範</li>
</ul>
<p><strong>格式範例</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## v0.11.0 (2025-09-30)
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 新增功能
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span><span class="k">-</span> 統一書籍資訊查詢服務
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">-</span> 支援 Google Books API 整合
</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 class="gu">### 改善
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="gu"></span><span class="k">-</span> 優化錯誤處理機制
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">-</span> 提升查詢效能
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu">### 修復
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu"></span>- 修正 ISBN 驗證問題</span></span></code></pre></div><p><strong>更新時機</strong>：</p>
<ul>
<li>版本發布時由文件代理人更新</li>
<li>從 work-log 提取用戶可感知的功能變動</li>
<li>不記錄內部重構或技術細節</li>
</ul>
<h4 id="2-todolistmd---開發任務全景圖">2. todolist.md - 開發任務全景圖</h4>
<p><strong>職責</strong>：</p>
<ul>
<li>記錄整個開發過程所有待處理任務</li>
<li>任務狀態追蹤（待執行/進行中/已完成）</li>
<li>優先級排序和依賴關係管理</li>
<li>跨版本的任務規劃視圖</li>
</ul>
<p><strong>代理人使用場景</strong>：</p>
<ul>
<li><strong>PM 代理人</strong>：規劃任務優先序和依賴關係</li>
<li><strong>主線程</strong>：查看全局並分派下一個任務</li>
<li><strong>執行代理人</strong>：了解任務背景和依賴</li>
<li><strong>所有代理人</strong>：查看整體進度和下一步方向</li>
</ul>
<p><strong>內容結構</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gh"># TodoList
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gh"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">## v0.11.x 系列任務
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span><span class="k">- [x]</span> v0.11.0 統一書籍資訊查詢服務
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="k">- [x]</span> Phase 1: 流程設計
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  <span class="k">- [x]</span> Phase 2: 測試設計
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="k">- [ ]</span> Phase 3: 實作執行
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="k">- [x]</span> v0.11.3 Domain 輸入驗證
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="k">- [x]</span> v0.11.4 核心查詢服務
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="k">- [ ]</span> v0.11.5 資料處理和快取
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">- [ ]</span> v0.11.15 測試修復完成
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu">## v0.12.x 系列任務（規劃中）
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu"></span>- [ ] v0.12.0 外部 API 整合</span></span></code></pre></div><p><strong>更新責任</strong>：</p>
<ul>
<li>所有代理人在任務狀態變更時即時更新</li>
<li>PM 代理人負責新增和重組任務</li>
<li>主線程負責維護任務優先序</li>
</ul>
<h4 id="3-work-log---詳細實作記錄">3. work-log/ - 詳細實作記錄</h4>
<p><strong>職責</strong>：</p>
<ul>
<li>完整的技術實作細節和決策過程</li>
<li>TDD 四階段進度追蹤</li>
<li>代理人交接的完整上下文</li>
<li>問題發現和解決方案記錄</li>
</ul>
<p><strong>文件結構</strong>：</p>
<ul>
<li><strong>主版本日誌</strong> (<code>vX.Y.0-main.md</code>)：中版本總覽、任務分派清單</li>
<li><strong>小版本日誌</strong> (<code>vX.Y.Z-task.md</code>)：具體任務的詳細執行記錄</li>
</ul>
<p><strong>內容標準</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gh"># vX.Y.Z 任務名稱
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gh"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">## 任務資訊
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span><span class="k">-</span> 版本號、負責代理人、執行日期
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">-</span> 前置任務、後續任務
</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 class="gu">## 任務目標
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="gu"></span><span class="k">-</span> 明確的技術目標
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu">## 參考文件
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu"></span><span class="k">-</span> UseCase、流程圖、架構規範
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">-</span> 依賴類別、測試設計
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu">## 執行過程
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="gu"></span><span class="k">-</span> Phase 1: 設計
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">-</span> Phase 2: 測試
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">-</span> Phase 3: 實作
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">-</span> Phase 4: 重構
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="gu">## 完成標準
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="gu"></span><span class="k">-</span> 測試通過率 100%
</span></span><span class="line"><span class="ln">22</span><span class="cl">- 程式碼品質符合標準</span></span></code></pre></div><p><strong>更新責任</strong>：</p>
<ul>
<li>執行代理人負責記錄詳細過程</li>
<li>文件代理人負責標記完成狀態</li>
<li>PM 代理人負責維護主版本日誌</li>
</ul>
<h3 id="三重文件協調機制">三重文件協調機制</h3>
<h4 id="資訊流動方向">資訊流動方向</h4>





<pre tabindex="0"><code class="language-mermaid" data-lang="mermaid">graph TD
    A[work-log&lt;br/&gt;實作細節] --&gt;|提取功能變動| B[CHANGELOG&lt;br/&gt;版本說明]
    A --&gt;|完成任務| C[todolist&lt;br/&gt;狀態更新]
    C --&gt;|規劃下一步| D[主線程分派]
    D --&gt;|查看詳細| A
    B --&gt;|版本發布| E[用戶和團隊]

    style A fill:#e1f5ff
    style B fill:#fff4e1
    style C fill:#e8f5e9</code></pre><p><strong>流程說明</strong>：</p>
<ol>
<li>代理人在 <strong>work-log</strong> 記錄詳細實作過程</li>
<li>任務完成時更新 <strong>todolist</strong> 狀態</li>
<li>版本發布時從 <strong>work-log</strong> 提取變動到 <strong>CHANGELOG</strong></li>
<li>主線程基於 <strong>todolist</strong> 和 <strong>work-log</strong> 規劃下一步</li>
</ol>
<h4 id="代理人交接層級">代理人交接層級</h4>
<h5 id="主線程--pm-代理人">主線程 ↔ PM 代理人</h5>
<p><strong>主要參考</strong>：</p>
<ul>
<li><code>todolist.md</code> - 任務全局和優先序</li>
<li><code>vX.Y.0-main.md</code> - 版本進度和任務拆分</li>
</ul>
<p><strong>交接場景</strong>：</p>
<ul>
<li>任務規模過大需要拆分</li>
<li>規劃下一個版本系列</li>
<li>任務依賴關係調整</li>
</ul>
<h5 id="主線程--執行代理人">主線程 ↔ 執行代理人</h5>
<p><strong>主要參考</strong>：</p>
<ul>
<li><code>vX.Y.Z-task.md</code> - 具體任務詳細說明</li>
<li><code>todolist.md</code> - 任務依賴和背景</li>
</ul>
<p><strong>交接場景</strong>：</p>
<ul>
<li>分派新任務</li>
<li>接收完成回報</li>
<li>處理問題升級</li>
</ul>
<h5 id="執行代理人--執行代理人">執行代理人 ↔ 執行代理人</h5>
<p><strong>主要參考</strong>：</p>
<ul>
<li><code>vX.Y.Z-task.md</code> - 前置任務的產出和發現</li>
<li><code>vX.Y.0-main.md</code> - 重要決策和架構調整</li>
</ul>
<p><strong>交接場景</strong>：</p>
<ul>
<li>Phase 1 → Phase 2：設計交接測試</li>
<li>Phase 2 → Phase 3：測試交接實作</li>
<li>Phase 3 → Phase 4：實作交接重構</li>
<li>依賴任務間的產出交接</li>
</ul>
<h5 id="版本發布時">版本發布時</h5>
<p><strong>主要參考</strong>：</p>
<ul>
<li><code>work-log/</code> - 提取所有功能變動</li>
<li><code>todolist.md</code> - 確認完成項目</li>
</ul>
<p><strong>執行流程</strong>：</p>
<ol>
<li>文件代理人檢視所有已完成的 work-log</li>
<li>提取用戶可感知的功能變動</li>
<li>更新 CHANGELOG.md</li>
<li>確認 todolist.md 狀態一致</li>
</ol>
<h3 id="三重文件更新責任">三重文件更新責任</h3>
<table>
  <thead>
      <tr>
          <th>文件</th>
          <th>更新時機</th>
          <th>責任代理人</th>
          <th>更新內容</th>
          <th>更新頻率</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>work-log</strong></td>
          <td>任務執行中/完成</td>
          <td>執行代理人</td>
          <td>詳細實作過程、決策記錄</td>
          <td>即時</td>
      </tr>
      <tr>
          <td><strong>todolist</strong></td>
          <td>任務狀態變更</td>
          <td>所有代理人</td>
          <td>任務狀態、優先序</td>
          <td>即時</td>
      </tr>
      <tr>
          <td><strong>CHANGELOG</strong></td>
          <td>版本發布</td>
          <td>文件代理人</td>
          <td>功能變動摘要</td>
          <td>版本發布時</td>
      </tr>
  </tbody>
</table>
<h3 id="三重文件一致性檢查">三重文件一致性檢查</h3>
<h4 id="強制檢查項目">強制檢查項目</h4>
<p><strong>版本號一致性</strong>：</p>
<ul>
<li>CHANGELOG 版本號 = work-log 主版本號</li>
<li>todolist 版本系列 = work-log 版本系列</li>
</ul>
<p><strong>任務狀態一致性</strong>：</p>
<ul>
<li>todolist 標記完成 ⇒ work-log 必須有對應完成記錄</li>
<li>work-log 標記完成 ⇒ todolist 必須同步更新</li>
</ul>
<p><strong>功能描述一致性</strong>：</p>
<ul>
<li>CHANGELOG 功能描述 ⇒ 必須對應 work-log 實作內容</li>
<li>不可在 CHANGELOG 記錄未實作的功能</li>
</ul>
<h4 id="hook-系統整合">Hook 系統整合</h4>
<p><strong>Version Check Hook</strong>：</p>
<ul>
<li>檢查三重文件版本號一致性</li>
<li>建議版本推進策略</li>
</ul>
<p><strong>Document Sync Hook</strong>：</p>
<ul>
<li>提醒 work-log 完成時更新 todolist</li>
<li>提醒版本發布時更新 CHANGELOG</li>
</ul>
<p><strong>Work Log Check Hook</strong>：</p>
<ul>
<li>識別工作狀態（更新/新建/完成）</li>
<li>確保 work-log 記錄完整性</li>
</ul>
<h3 id="強制要求">強制要求</h3>
<h4 id="文件完整性要求">文件完整性要求</h4>
<ol>
<li>
<p><strong>work-log 必須完整</strong>：</p>
<ul>
<li>所有技術細節必須記錄</li>
<li>所有決策必須說明理由</li>
<li>所有問題發現必須記錄</li>
</ul>
</li>
<li>
<p><strong>todolist 必須即時</strong>：</p>
<ul>
<li>任務狀態即時更新，不得延遲</li>
<li>新任務必須立即加入</li>
<li>完成任務必須立即標記</li>
</ul>
</li>
<li>
<p><strong>CHANGELOG 必須準確</strong>：</p>
<ul>
<li>只記錄用戶可感知的變動</li>
<li>不記錄內部重構或技術債務</li>
<li>必須基於實際完成的 work-log</li>
</ul>
</li>
</ol>
<h4 id="代理人責任要求">代理人責任要求</h4>
<ol>
<li>
<p><strong>執行代理人</strong>：</p>
<ul>
<li>任務執行過程必須記錄到 work-log</li>
<li>任務完成時必須更新 todolist 狀態</li>
<li>發現問題必須記錄到 work-log</li>
</ul>
</li>
<li>
<p><strong>文件代理人</strong>：</p>
<ul>
<li>版本發布時必須更新 CHANGELOG</li>
<li>確保三重文件一致性</li>
<li>標記 work-log 完成狀態</li>
</ul>
</li>
<li>
<p><strong>PM 代理人</strong>：</p>
<ul>
<li>維護 todolist 任務結構</li>
<li>維護 vX.Y.0-main.md 任務分派</li>
<li>確保任務依賴關係正確</li>
</ul>
</li>
<li>
<p><strong>主線程</strong>：</p>
<ul>
<li>不得親自修改程式碼</li>
<li>基於 todolist 和 work-log 分派任務</li>
<li>監控三重文件一致性</li>
</ul>
</li>
</ol>
<h3 id="v0110-實戰範例">v0.11.0 實戰範例</h3>
<h4 id="三重文件協調實例">三重文件協調實例</h4>
<p><strong>work-log 記錄</strong>（v0.11.3-domain-input-validation.md）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gu">## Phase 3 實作完成
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gu"></span><span class="k">-</span> BookQueryInput Value Object 實作
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="k">-</span> InputValidator 驗證邏輯
</span></span><span class="line"><span class="ln">4</span><span class="cl">- 71 個測試 100% 通過</span></span></code></pre></div><p><strong>todolist 更新</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl">- [x] v0.11.3 Domain 輸入驗證</span></span></code></pre></div><p><strong>CHANGELOG 提取</strong>（版本發布時）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gu">## v0.11.0 (2025-09-30)
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gu">### 新增功能
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="gu"></span>- 統一書籍資訊查詢服務（支援 ISBN、標題、作者查詢）</span></span></code></pre></div><p><strong>協調流程</strong>：</p>
<ol>
<li>pepper-test-implementer 完成 v0.11.3 實作</li>
<li>記錄詳細過程到 work-log</li>
<li>更新 todolist 標記完成</li>
<li>主線程分派下一個任務 v0.11.4</li>
<li>版本發布時，文件代理人從所有 v0.11.x work-log 提取功能到 CHANGELOG</li>
</ol>
<h4 id="代理人交接實例">代理人交接實例</h4>
<p><strong>Phase 2 → Phase 3 交接</strong>：</p>
<p>sage-test-architect 完成測試設計後：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gh"># v0.11.2-tdd-phase2-test-design.md
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gh"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">## Phase 2 產出
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span><span class="k">-</span> 335 個測試用例設計
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">-</span> 33 個測試檔案規劃
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">-</span> 架構修正決策：BookEnrichmentData 遷移到 Domain 層
</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"><span class="gu">## 傳遞給 Phase 3
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu"></span><span class="k">-</span> 依賴類別：BookEnrichmentData (已遷移)
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">-</span> 測試優先序：v0.11.3 → v0.11.4 → v0.11.5
</span></span><span class="line"><span class="ln">11</span><span class="cl">- 重要發現：Domain 服務層需從零建立</span></span></code></pre></div><p>pepper-test-implementer 接收後：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gh"># v0.11.3-domain-input-validation.md
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gh"></span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="gu">## 參考文件
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="gu"></span><span class="k">-</span> 測試設計：v0.11.2-tdd-phase2-test-design.md<span class="ni">#task</span>-20
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="k">-</span> 前置任務產出：無（首個任務）
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="k">-</span> 架構調整：BookEnrichmentData 已遷移到 Domain 層
</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"><span class="gu">## Phase 3 執行
</span></span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="gu"></span>基於 Phase 2 測試設計，實作 BookQueryInput...</span></span></code></pre></div><p><strong>交接重點</strong>：</p>
<ul>
<li>Phase 2 的架構發現立即傳遞</li>
<li>Phase 3 明確參考 Phase 2 測試設計</li>
<li>work-log 完整記錄交接脈絡</li>
</ul>
<h2 id="敏捷重構流程">敏捷重構流程</h2>
<h3 id="增強版執行循環">增強版執行循環</h3>





<pre tabindex="0"><code class="language-mermaid" data-lang="mermaid">graph TD
    A[主線程開始任務分派] --&gt; B[執行準備度檢查清單]
    B --&gt; C{檢查是否全部通過?}
    C --&gt;|否| D[呼叫相應代理人建立準備文件]
    D --&gt; E[文件建立完成]
    E --&gt; B
    C --&gt;|是| F[檢查主版本工作日誌]
    F --&gt; G[分派下一個任務]
    G --&gt; H[子代理人執行任務]
    H --&gt; I{任務規模是否適當?}
    I --&gt;|過大| J[向上回報主線程]
    J --&gt; K[PM代理人拆分任務]
    K --&gt; L[建立小版本工作日誌]
    L --&gt; M[依小版本日誌分派]
    M --&gt; H
    I --&gt;|適當| N[完成任務回報]
    N --&gt; O[重構代理人檢查]
    O --&gt; P{品質是否通過?}
    P --&gt;|不通過| Q[要求修正]
    Q --&gt; H
    P --&gt;|通過| R[文件代理人更新日誌]
    R --&gt; S{小版本是否完成?}
    S --&gt;|是| T[回到主版本檢查下一任務]
    S --&gt;|否| U[檢查小版本下一任務]
    U --&gt; M
    T --&gt; F</code></pre><h3 id="標準執行循環">標準執行循環</h3>





<pre tabindex="0"><code class="language-mermaid" data-lang="mermaid">graph TD
    A[主線程檢查主版本工作日誌] --&gt; B[分派下一個任務]
    B --&gt; C[子代理人執行任務]
    C --&gt; D{任務規模是否適當?}
    D --&gt;|過大| E[向上回報主線程]
    E --&gt; F[PM代理人拆分任務]
    F --&gt; G[建立小版本工作日誌]
    G --&gt; H[依小版本日誌分派]
    H --&gt; C
    D --&gt;|適當| I[完成任務回報]
    I --&gt; J[重構代理人檢查]
    J --&gt; K{品質是否通過?}
    K --&gt;|不通過| L[要求修正]
    L --&gt; C
    K --&gt;|通過| M[文件代理人更新日誌]
    M --&gt; N{小版本是否完成?}
    N --&gt;|是| O[回到主版本檢查下一任務]
    N --&gt;|否| P[檢查小版本下一任務]
    P --&gt; H
    O --&gt; A</code></pre><h2 id="任務拆分實戰範例">任務拆分實戰範例</h2>
<h3 id="v0115-任務拆分情境">v0.11.5 任務拆分情境</h3>
<p><strong>原始任務</strong>：v0.11.5 - 批次 1.3 資料處理和快取</p>
<p><strong>任務描述</strong>（來自 v0.11.0-main.md）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gu">#### v0.11.5 - 批次 1.3 資料處理和快取
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gu"></span>**狀態**: 待執行
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="gs">**負責**</span>: pepper-test-implementer
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="gs">**實作內容**</span>:
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="k">-</span> BookEnrichmentProcessor
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="k">-</span> BookMetadata 實體
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="k">-</span> ApiCacheManager
</span></span><span class="line"><span class="ln">8</span><span class="cl">
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="gs">**驗收**</span>: 資料處理鏈測試通過</span></span></code></pre></div><h3 id="代理人回報情境假設">代理人回報（情境假設）</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 代理人回報 - v0.11.5
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gs">**回報代理人**</span>: pepper-test-implementer
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gs">**任務**</span>: v0.11.5 - 批次 1.3 資料處理和快取
</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="gu">### 問題描述
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span>經過 Phase 2 測試設計分析，發現此任務包含三個獨立且複雜的元件：
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">1.</span> BookEnrichmentProcessor - 資料處理邏輯（20+ 個測試用例）
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">2.</span> BookMetadata 實體 - Domain 層核心實體（15+ 個測試用例）
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">3.</span> ApiCacheManager - 快取管理機制（18+ 個測試用例）
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">預估總工作量：4-5 小時，超過單一任務標準（1-2 小時）
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu">### 建議拆分方案
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="gu"></span>**方案 A**：依元件職責拆分為三個子任務
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">-</span> v0.11.5-a: BookEnrichmentProcessor（資料處理核心）
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">-</span> v0.11.5-b: BookMetadata（Domain 實體）
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">-</span> v0.11.5-c: ApiCacheManager（快取機制）
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="gs">**方案 B**</span>：依依賴關係拆分
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">-</span> v0.11.5-a: BookMetadata + BookEnrichmentProcessor
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">-</span> v0.11.5-b: ApiCacheManager
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="gu">### 推薦方案
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="gu"></span>推薦方案 A，原因：
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="k">-</span> 每個元件職責單一，測試獨立
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="k">-</span> 符合 Clean Architecture 分層（Domain / Application）
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="k">-</span> 可並行開發（如有多代理人）
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="k">-</span> 風險隔離，單一元件問題不影響其他
</span></span><span class="line"><span class="ln">30</span><span class="cl">
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="gu">### 等待主線程決策
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="gu"></span>請確認拆分方案，並指示是否需要 PM 代理人建立子版本工作日誌。</span></span></code></pre></div><h3 id="主線程決策處理">主線程決策處理</h3>
<p><strong>決策過程</strong>：</p>
<ol>
<li>
<p><strong>檢視相關文件</strong>：</p>
<ul>
<li>Phase 2 測試設計：確認測試用例數量和複雜度</li>
<li>流程圖 Event 6-7：確認元件間依賴關係</li>
<li>架構規範：確認分層正確性</li>
</ul>
</li>
<li>
<p><strong>評估拆分方案</strong>：</p>
<ul>
<li>方案 A 職責更清晰，符合單一職責原則</li>
<li>每個子任務 1.5-2 小時，符合標準</li>
<li>BookMetadata 是 Domain 層核心，應獨立處理</li>
</ul>
</li>
<li>
<p><strong>做出決策</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 決策記錄 - v0.11.5 拆分
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gs">**決策**</span>: 採用方案 A，拆分為三個子任務
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gs">**理由**</span>:
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">-</span> 符合單一職責原則
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">-</span> BookMetadata 是 Domain 層核心實體，重要性高
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">-</span> 測試覆蓋率可逐步建立
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">-</span> 風險管理更精確
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gs">**執行**</span>:
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">1.</span> 呼叫 rosemary-project-manager 建立子版本工作日誌
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">2.</span> 更新 v0.11.0-main.md 任務清單
</span></span><span class="line"><span class="ln">13</span><span class="cl">3. 依序分派 v0.11.5-a → v0.11.5-b → v0.11.5-c</span></span></code></pre></div></li>
</ol>
<h3 id="pm-代理人建立子版本日誌">PM 代理人建立子版本日誌</h3>
<p><strong>rosemary-project-manager 執行</strong>：</p>
<h4 id="1-建立-v0115-a-工作日誌">1. 建立 v0.11.5-a 工作日誌</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gh"># v0.11.5-a 資料處理核心 - BookEnrichmentProcessor
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gh"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">## 任務資訊
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span><span class="k">-</span> **版本號**: v0.11.5-a
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">-</span> **父任務**: v0.11.5 批次 1.3 資料處理和快取
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">-</span> **負責代理人**: pepper-test-implementer
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">-</span> **執行日期**: 2025-09-30
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">-</span> **前置任務**: v0.11.4 (核心查詢服務)
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">-</span> **後續任務**: v0.11.5-b (BookMetadata)
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu">## 任務目標
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu"></span>實作 BookEnrichmentProcessor 資料處理核心邏輯。
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu">## 參考文件（強制完整）
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="gu">### UseCase 參考
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="gu"></span><span class="k">-</span> [<span class="nt">UC-01 ISBN 精確查詢</span>](<span class="na">/app-use-cases.md#uc-01</span>)
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="gu">### 流程圖參考（具體到 Event）
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="gu"></span><span class="k">-</span> [<span class="nt">主流程 Event 6</span>](<span class="na">/use-cases/unified-api/book-info-query-flow.md</span>)
</span></span><span class="line"><span class="ln">21</span><span class="cl">  <span class="k">-</span> Event 6: BookEnrichmentProcessor.process(rawData)
</span></span><span class="line"><span class="ln">22</span><span class="cl">  <span class="k">-</span> 輸入: GoogleBooksDto
</span></span><span class="line"><span class="ln">23</span><span class="cl">  <span class="k">-</span> 輸出: BookEnrichmentData
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="gu">### 架構規範
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="gu"></span><span class="k">-</span> [<span class="nt">Application 層設計</span>](<span class="na">/app-requirements-spec.md#application-layer</span>)
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="k">-</span> [<span class="nt">資料轉換規範</span>](<span class="na">/domain-transformation-layer-design.md</span>)
</span></span><span class="line"><span class="ln">28</span><span class="cl">
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="gu">### 依賴類別（前置任務產出）
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="gu"></span><span class="k">-</span> <span class="sb">`BookQueryInput`</span> (v0.11.3)
</span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="k">-</span> <span class="sb">`QueryType`</span> (v0.11.4)
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="k">-</span> <span class="sb">`GoogleBooksDto`</span> (Infrastructure 既有)
</span></span><span class="line"><span class="ln">33</span><span class="cl">
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="gu">### 測試設計參考
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="gu"></span><span class="k">-</span> [<span class="nt">主流程測試設計</span>](<span class="na">/record/agile-programing-methodology/v0.11.2-tdd-phase2-test-design.md#task-22</span>)
</span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="k">-</span> 測試檔案: <span class="sb">`book_enrichment_processor_test.dart`</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="k">-</span> 測試用例: 20 個
</span></span><span class="line"><span class="ln">38</span><span class="cl">
</span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="gu">## 影響範圍
</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="gu"></span><span class="k">-</span> 新建: <span class="sb">`lib/application/book_info/processors/book_enrichment_processor.dart`</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="k">-</span> 新建: <span class="sb">`test/unit/application/book_info/processors/book_enrichment_processor_test.dart`</span>
</span></span><span class="line"><span class="ln">42</span><span class="cl">
</span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="gu">## 完成標準
</span></span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> BookEnrichmentProcessor 實作完成
</span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="k">- [ ]</span> 20 個測試用例 100% 通過
</span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="k">- [ ]</span> 符合 Application 層職責
</span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="k">- [ ]</span> 錯誤處理遵循規範
</span></span><span class="line"><span class="ln">48</span><span class="cl">
</span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="gu">## 回到父任務
</span></span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="gu"></span>完成後更新 v0.11.0-main.md 標記 v0.11.5-a 完成。</span></span></code></pre></div><h4 id="2-建立-v0115-bv0115-c-工作日誌">2. 建立 v0.11.5-b、v0.11.5-c 工作日誌</h4>
<p>（類似格式，省略詳細內容）</p>
<h3 id="更新主版本工作日誌">更新主版本工作日誌</h3>
<p><strong>更新 v0.11.0-main.md</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">#### v0.11.5 - 批次 1.3 資料處理和快取（待執行）
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>**狀態**: 執行中（已拆分為子任務）
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gs">**負責**</span>: pepper-test-implementer
</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"><span class="gs">**子任務**</span>:
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">- [ ]</span> v0.11.5-a: BookEnrichmentProcessor ([<span class="nt">工作日誌</span>](<span class="na">/record/agile-programing-methodology/v0.11.5-a-enrichment-processor.md</span>))
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">- [ ]</span> v0.11.5-b: BookMetadata ([<span class="nt">工作日誌</span>](<span class="na">/record/agile-programing-methodology/v0.11.5-b-book-metadata.md</span>))
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">- [ ]</span> v0.11.5-c: ApiCacheManager ([<span class="nt">工作日誌</span>](<span class="na">/record/agile-programing-methodology/v0.11.5-c-cache-manager.md</span>))
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gs">**驗收**</span>: 三個子任務全部完成，資料處理鏈測試通過
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gs">**拆分原因**</span>: 任務複雜度超出標準，依元件職責拆分為三個獨立任務
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gs">**拆分決策**</span>: [決策記錄連結]</span></span></code></pre></div><h3 id="任務拆分關鍵原則">任務拆分關鍵原則</h3>
<h4 id="何時拆分">何時拆分</h4>
<ul>
<li>預估工作時間 &gt; 3 小時</li>
<li>影響檔案數量 &gt; 5 個</li>
<li>測試用例數量 &gt; 30 個</li>
<li>包含多個獨立職責的元件</li>
<li>依賴關係複雜需要分階段驗證</li>
</ul>
<h4 id="拆分命名規則">拆分命名規則</h4>
<ul>
<li><strong>單層拆分</strong>: vX.Y.Z-a, vX.Y.Z-b, vX.Y.Z-c</li>
<li><strong>多層拆分</strong>: vX.Y.Z-a1, vX.Y.Z-a2（極少使用）</li>
<li><strong>命名語意</strong>: 清楚表達子任務核心職責</li>
</ul>
<h4 id="拆分後的依賴管理">拆分後的依賴管理</h4>
<ul>
<li><strong>串行依賴</strong>: 明確標註前置任務和後續任務</li>
<li><strong>並行任務</strong>: 標註可同時進行的子任務</li>
<li><strong>共享依賴</strong>: 在父任務層級說明共同依賴</li>
</ul>
<h2 id="動態文件更新機制">動態文件更新機制</h2>
<h3 id="核心原則-1">核心原則</h3>
<p><strong>敏捷開發的資訊流動本質</strong>：前置階段的發現必須立即傳遞並更新後續任務描述，確保所有代理人基於最新、最完整的資訊執行任務。</p>
<h3 id="phase-發現傳遞流程">Phase 發現傳遞流程</h3>
<h4 id="phase-1--phase-2--phase-3-資訊流">Phase 1 → Phase 2 → Phase 3 資訊流</h4>





<pre tabindex="0"><code class="language-mermaid" data-lang="mermaid">graph LR
    A[Phase 1 設計] --&gt;|架構決策| B[Phase 2 測試設計]
    B --&gt;|實作指引| C[Phase 3 實作]
    B --&gt;|問題識別| D[架構調整]
    D --&gt;|更新設計| A
    D --&gt;|更新任務| C</code></pre><h4 id="v011-實際案例phase-2-發現如何更新-phase-3-任務">v0.11 實際案例：Phase 2 發現如何更新 Phase 3 任務</h4>
<p><strong>Phase 2 發現</strong>（v0.11.2）：</p>
<ol>
<li><strong>架構問題</strong>：BookEnrichmentData 位於 Infrastructure 層（應在 Domain 層）</li>
<li><strong>命名不一致</strong>：4 個流程圖元件命名與程式碼不符</li>
<li><strong>缺失元件</strong>：Domain 服務層完全缺失，需要 20 個新類別</li>
<li><strong>時程調整</strong>：從 4-6 天調整為 6-7 天</li>
</ol>
<p><strong>立即執行的更新動作</strong>：</p>
<h5 id="1-架構修正阻塞性問題">1. 架構修正（阻塞性問題）</h5>
<p><strong>決策</strong>：立即停止 Phase 3 任務，優先修正架構問題</p>
<p><strong>執行</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 緊急任務插入 - v0.11.2-fix
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gs">**任務**</span>: 遷移 BookEnrichmentData 到 Domain 層
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gs">**優先級**</span>: 最高（阻塞後續開發）
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gs">**執行**</span>: 主線程直接處理或指派給 mint-format-specialist
</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 class="gs">**完成後動作**</span>:
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">-</span> 更新所有相關任務的依賴類別參考
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">-</span> 更新流程圖中的層級標註
</span></span><span class="line"><span class="ln">10</span><span class="cl">- 通知所有相關代理人架構變更</span></span></code></pre></div><h5 id="2-更新待執行任務的參考文件">2. 更新待執行任務的參考文件</h5>
<p><strong>原始 v0.11.5 任務描述</strong>（Phase 2 前）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gu">#### v0.11.5 - 批次 1.3 資料處理和快取
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gu"></span>**實作內容**:
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="k">-</span> BookEnrichmentProcessor
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="k">-</span> BookMetadata 實體
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="k">-</span> ApiCacheManager
</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 class="gs">**參考文件**</span>: （缺失）</span></span></code></pre></div><p><strong>更新後 v0.11.5 任務描述</strong>（Phase 2 後）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">#### v0.11.5 - 批次 1.3 資料處理和快取
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>**實作內容**:
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="k">-</span> BookEnrichmentProcessor
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="k">-</span> BookMetadata 實體
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">-</span> ApiCacheManager
</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 class="gs">**參考文件**</span>（Phase 2 補充）:
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">-</span> [<span class="nt">主流程 Event 6-7</span>](<span class="na">/record/use-cases/unified-api/book-info-query-flow.md</span>)
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">-</span> [<span class="nt">測試設計</span>](<span class="na">/record/agile-programing-methodology/v0.11.2-tdd-phase2-test-design.md#task-22</span>)
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">-</span> 依賴類別: <span class="sb">`BookEnrichmentData`</span> (**已遷移到 Domain 層**)
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">-</span> 依賴類別: <span class="sb">`BookQueryInput`</span> (v0.11.3)
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">-</span> 依賴類別: <span class="sb">`QueryType`</span> (v0.11.4)
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">-</span> [<span class="nt">錯誤處理規範</span>](<span class="na">/record/app-error-handling-design.md</span>)
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="gs">**Phase 2 發現**</span>:
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">-</span> BookEnrichmentData 已從 Infrastructure 遷移到 Domain
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">-</span> 需要處理資料轉換和驗證邏輯
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">-</span> 快取策略需要配合速率控制（批次 2）
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="gs">**驗收**</span>: 資料處理鏈測試通過，符合 Domain 層職責</span></span></code></pre></div><h5 id="3-更新主版本工作日誌持續性文件">3. 更新主版本工作日誌（持續性文件）</h5>
<p><strong>v0.11.0-main.md 動態更新區域</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## Phase 2 重要發現（持續更新）
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 架構調整
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span><span class="k">-</span> **BookEnrichmentData 層級修正**（2025-09-30）
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="k">-</span> 原位置: <span class="sb">`lib/infrastructure/`</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  <span class="k">-</span> 新位置: <span class="sb">`lib/domains/book_info/entities/`</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="k">-</span> 影響任務: v0.11.5, v0.11.6
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="k">-</span> Git commit: 58b1603
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu">### 命名統一
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu"></span><span class="k">-</span> **流程圖命名修正**（2025-09-30）
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="k">-</span> 修正 4 個元件命名不一致
</span></span><span class="line"><span class="ln">13</span><span class="cl">  <span class="k">-</span> 所有後續任務參考已更新
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="gu">### 實作發現
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="gu"></span><span class="k">-</span> **Domain 服務層新建**：v0.11.3-4 建立 BookQueryInput, QueryTypeResolver
</span></span><span class="line"><span class="ln">17</span><span class="cl">  <span class="k">-</span> 影響: 所有後續任務依賴這些基礎類別
</span></span><span class="line"><span class="ln">18</span><span class="cl">  <span class="k">-</span> 參考: 所有任務需 import package:book_overview_app/domains/book_info/
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="gu">### 時程調整
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="gu"></span><span class="k">-</span> **原估時程**: 4-6 天
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">-</span> **調整時程**: 6-7 天（+1-2 天）
</span></span><span class="line"><span class="ln">23</span><span class="cl">- <span class="gs">**原因**</span>: 28 個 TDD 新建測試，Domain 層從零建立</span></span></code></pre></div><h3 id="動態更新觸發時機">動態更新觸發時機</h3>
<h4 id="1-架構變更時">1. 架構變更時</h4>
<p><strong>觸發條件</strong>：</p>
<ul>
<li>類別位置調整（跨層級遷移）</li>
<li>介面簽名變更</li>
<li>依賴關係變化</li>
</ul>
<p><strong>更新範圍</strong>：</p>
<ul>
<li>所有引用該類別的任務</li>
<li>相關的流程圖和設計文件</li>
<li>測試設計中的 Mock 設定</li>
</ul>
<h4 id="2-測試設計發現時">2. 測試設計發現時</h4>
<p><strong>觸發條件</strong>：</p>
<ul>
<li>識別出新的測試用例</li>
<li>發現缺失的邊界條件</li>
<li>複雜度評估變化</li>
</ul>
<p><strong>更新範圍</strong>：</p>
<ul>
<li>對應實作任務的驗收標準</li>
<li>相關任務的前置條件</li>
<li>時程估算調整</li>
</ul>
<h4 id="3-實作階段發現時">3. 實作階段發現時</h4>
<p><strong>觸發條件</strong>：</p>
<ul>
<li>技術方案調整</li>
<li>新增輔助類別</li>
<li>效能優化需求</li>
</ul>
<p><strong>更新範圍</strong>：</p>
<ul>
<li>後續相關任務的參考實作</li>
<li>測試設計補充</li>
<li>文件範例更新</li>
</ul>
<h3 id="動態更新執行標準">動態更新執行標準</h3>
<h4 id="主線程職責">主線程職責</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gu">## 主線程動態更新檢查清單
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gu"></span>
</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 class="k">- [ ]</span> 檢查是否有架構變更
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="k">- [ ]</span> 檢查是否有新的依賴類別
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="k">- [ ]</span> 檢查是否影響後續任務
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="k">- [ ]</span> 更新主版本工作日誌「重要發現」區域
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="k">- [ ]</span> 更新所有受影響任務的參考文件
</span></span><span class="line"><span class="ln">9</span><span class="cl">- [ ] 通知相關代理人（如已分派）</span></span></code></pre></div><h4 id="代理人回報職責">代理人回報職責</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 代理人發現回報格式
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 發現類型
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 架構問題
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">- [ ]</span> 依賴變更
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">- [ ]</span> 新增元件
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">- [ ]</span> 技術債務
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">### 影響評估
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span><span class="k">-</span> 影響範圍: [列出受影響的任務版本號]
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">-</span> 嚴重程度: 阻塞 / 重要 / 資訊性
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">-</span> 建議動作: [具體建議]
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu">### 需要更新的文件
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 主版本工作日誌
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">- [ ]</span> 相關任務參考文件
</span></span><span class="line"><span class="ln">17</span><span class="cl">- [ ] 流程圖或設計文件</span></span></code></pre></div><h2 id="代理人回報與討論流程">代理人回報與討論流程</h2>
<h3 id="回報時機與類型">回報時機與類型</h3>
<h4 id="1-架構衝突檢測">1. 架構衝突檢測</h4>
<p><strong>觸發情境</strong>：</p>
<ul>
<li>流程圖定義與實作需求衝突</li>
<li>類別職責與層級定位不符</li>
<li>依賴方向違反 Clean Architecture</li>
</ul>
<p><strong>v0.11 實際案例</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 架構衝突回報 - v0.11.2
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gs">**回報代理人**</span>: sage-test-architect
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gs">**檢測階段**</span>: Phase 2 測試設計
</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="gu">### 衝突描述
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span>發現 <span class="sb">`BookEnrichmentData`</span> 位於 <span class="sb">`lib/infrastructure/`</span> 目錄，
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">但其職責為 Domain 層實體（純資料結構，無外部依賴）。
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu">### 架構分析
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu"></span><span class="k">-</span> **當前位置**: <span class="sb">`lib/infrastructure/api/google_books/book_enrichment_data.dart`</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">-</span> **應該位置**: <span class="sb">`lib/domains/book_info/entities/book_enrichment_data.dart`</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">-</span> **衝突原因**: 違反 Clean Architecture 分層原則
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">-</span> **影響範圍**:
</span></span><span class="line"><span class="ln">15</span><span class="cl">  <span class="k">-</span> v0.11.5 (資料處理) 需要引用此類別
</span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="k">-</span> v0.11.6 (API 客戶端) 需要轉換到此類別
</span></span><span class="line"><span class="ln">17</span><span class="cl">  <span class="k">-</span> 所有測試需要更新 import 路徑
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="gu">### 建議方案
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="gu"></span>**推薦**: 立即遷移到 Domain 層
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="gs">**理由**</span>:
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">1.</span> Domain 層應該定義核心資料模型
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="k">2.</span> Infrastructure 層應該依賴 Domain，而非反向
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="k">3.</span> 後續所有 Domain 服務都需要使用此類別
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="gu">### 需要決策
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 是否同意遷移到 Domain 層
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="k">- [ ]</span> 是否需要重新命名（EnrichmentData → BookEnrichmentData）
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="k">- [ ]</span> 是否需要更新所有流程圖標註
</span></span><span class="line"><span class="ln">30</span><span class="cl">
</span></span><span class="line"><span class="ln">31</span><span class="cl">#### 等待主線程確認後執行</span></span></code></pre></div><p><strong>主線程處理流程</strong>：</p>
<ol>
<li>
<p><strong>檢視相關規範</strong>：</p>
<ul>
<li>閱讀 Clean Architecture 設計文件</li>
<li>確認 Domain 層職責定義</li>
<li>檢查是否有其他類似問題</li>
</ul>
</li>
<li>
<p><strong>做出決策</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 決策 - BookEnrichmentData 遷移
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gs">**決定**</span>: 同意遷移到 Domain 層
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gs">**執行**</span>:
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">1.</span> 立即處理（阻塞後續任務）
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">2.</span> 遷移到 <span class="sb">`lib/domains/book_info/entities/`</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">3.</span> 更新所有 import 引用
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">4.</span> 更新流程圖層級標註
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gs">**指派**</span>: mint-format-specialist 執行檔案遷移和路徑更新
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gs">**時程**</span>: 0.5 小時，完成後繼續 v0.11.3</span></span></code></pre></div></li>
<li>
<p><strong>更新受影響任務</strong>：</p>
<ul>
<li>更新 v0.11.5, v0.11.6 參考文件</li>
<li>標註 BookEnrichmentData 新位置</li>
<li>更新所有測試的 import 路徑範例</li>
</ul>
</li>
</ol>
<h4 id="2-任務複雜度超標">2. 任務複雜度超標</h4>
<p><strong>觸發情境</strong>：</p>
<ul>
<li>預估工作時間 &gt; 3 小時</li>
<li>測試用例數量 &gt; 30 個</li>
<li>影響檔案數量 &gt; 5 個</li>
</ul>
<p>（參考「任務拆分實戰範例」章節）</p>
<h4 id="3-流程圖與需求不一致">3. 流程圖與需求不一致</h4>
<p><strong>觸發情境</strong>：</p>
<ul>
<li>Event 描述過於抽象</li>
<li>方法簽名未定義清楚</li>
<li>錯誤處理策略模糊</li>
</ul>
<p><strong>回報格式</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 流程圖釐清需求 - vX.Y.Z
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gs">**回報代理人**</span>: [agent-name]
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gs">**流程圖**</span>: [flow-diagram-name.md]
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gs">**Event**</span>: Event X
</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 class="gu">### 需要釐清的問題
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="gu"></span><span class="k">1.</span> <span class="gs">**Event X 描述**</span>: [原始描述]
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">   <span class="k">-</span> 疑問: [具體不清楚的地方]
</span></span><span class="line"><span class="ln">10</span><span class="cl">   <span class="k">-</span> 需要確認: [需要確認的技術細節]
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">2.</span> <span class="gs">**方法簽名缺失**</span>:
</span></span><span class="line"><span class="ln">13</span><span class="cl">   <span class="k">-</span> 當前: Event X 只說「處理資料」
</span></span><span class="line"><span class="ln">14</span><span class="cl">   <span class="k">-</span> 需要: 輸入參數類型、輸出返回類型、異常處理
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="gu">### 建議補充
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="gu"></span>```dart
</span></span><span class="line"><span class="ln">18</span><span class="cl">// 建議的方法簽名
</span></span><span class="line"><span class="ln">19</span><span class="cl">Future<span class="p">&lt;</span><span class="nt">BookEnrichmentData</span><span class="p">&gt;</span> processRawData(
</span></span><span class="line"><span class="ln">20</span><span class="cl">  GoogleBooksDto rawData,
</span></span><span class="line"><span class="ln">21</span><span class="cl">  QueryType queryType,
</span></span><span class="line"><span class="ln">22</span><span class="cl">) async {
</span></span><span class="line"><span class="ln">23</span><span class="cl">  // 實作邏輯
</span></span><span class="line"><span class="ln">24</span><span class="cl">}</span></span></code></pre></div><h3 id="需要主線程確認">需要主線程確認</h3>
<ul>
<li><input disabled="" type="checkbox"> 方法簽名是否正確</li>
<li><input disabled="" type="checkbox"> 是否需要更新流程圖</li>
<li><input disabled="" type="checkbox"> 錯誤處理策略（拋出異常 vs OperationResult）</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">
</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">1. 檢視 UseCase 定義
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">2. 確認技術方案
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">3. 更新流程圖或任務描述
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">4. 回覆代理人並記錄決策
</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">#### 4. 技術方案需要確認
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">**觸發情境**：
</span></span><span class="line"><span class="ln">10</span><span class="cl">- 多種實作方案可行
</span></span><span class="line"><span class="ln">11</span><span class="cl">- 效能與可讀性取捨
</span></span><span class="line"><span class="ln">12</span><span class="cl">- 第三方套件選擇
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl">**回報格式**：
</span></span><span class="line"><span class="ln">15</span><span class="cl">```markdown
</span></span><span class="line"><span class="ln">16</span><span class="cl">## 技術方案確認 - vX.Y.Z
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl">**方案 A**: [描述]
</span></span><span class="line"><span class="ln">19</span><span class="cl">- 優點: [列舉]
</span></span><span class="line"><span class="ln">20</span><span class="cl">- 缺點: [列舉]
</span></span><span class="line"><span class="ln">21</span><span class="cl">- 適用場景: [說明]
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl">**方案 B**: [描述]
</span></span><span class="line"><span class="ln">24</span><span class="cl">- 優點: [列舉]
</span></span><span class="line"><span class="ln">25</span><span class="cl">- 缺點: [列舉]
</span></span><span class="line"><span class="ln">26</span><span class="cl">- 適用場景: [說明]
</span></span><span class="line"><span class="ln">27</span><span class="cl">
</span></span><span class="line"><span class="ln">28</span><span class="cl">### 推薦方案及理由
</span></span><span class="line"><span class="ln">29</span><span class="cl">[代理人的推薦與分析]
</span></span><span class="line"><span class="ln">30</span><span class="cl">
</span></span><span class="line"><span class="ln">31</span><span class="cl">### 等待主線程決策
</span></span><span class="line"><span class="ln">32</span><span class="cl">[需要確認的具體問題]</span></span></code></pre></div><h3 id="回報標準格式">回報標準格式</h3>
<h4 id="完整回報模板">完整回報模板</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 代理人回報 - vX.Y.Z
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gs">**回報代理人**</span>: [agent-name]
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gs">**任務**</span>: [task-name]
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gs">**回報類型**</span>: 架構衝突 / 複雜度 / 流程圖釐清 / 技術方案
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="gs">**嚴重程度**</span>: 阻塞 / 重要 / 資訊性
</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">---
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu">### 問題描述
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu"></span>[清楚描述發現的問題或疑問]
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu">### 背景資訊
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu"></span><span class="k">-</span> **相關文件**: [列出相關的 UseCase、流程圖、架構文件]
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">-</span> **當前狀態**: [任務執行到什麼階段]
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">-</span> **發現時機**: [什麼時候發現這個問題]
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="gu">### 影響評估
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="gu"></span><span class="k">-</span> **影響任務**: [列出受影響的任務版本號]
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">-</span> **影響範圍**: [程式碼、測試、文件]
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">-</span> **風險等級**: [高/中/低]
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="gu">### 分析與建議
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="gu">#### 原因分析
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="gu"></span>[為什麼會發生這個問題]
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="gu">#### 建議方案
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="gu"></span>**方案 A**: [描述]
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="gs">**方案 B**</span>: [描述]
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="gs">**推薦**</span>: [哪個方案及理由]
</span></span><span class="line"><span class="ln">31</span><span class="cl">
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="gu">### 等待主線程決策
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> [具體需要決策的問題 1]
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="k">- [ ]</span> [具體需要決策的問題 2]
</span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="k">- [ ]</span> [需要更新的文件清單]
</span></span><span class="line"><span class="ln">36</span><span class="cl">
</span></span><span class="line"><span class="ln">37</span><span class="cl">---
</span></span><span class="line"><span class="ln">38</span><span class="cl">
</span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="gs">**暫停執行**</span>: 是 / 可繼續其他任務 / 不影響當前工作</span></span></code></pre></div><h3 id="主線程討論與決策流程">主線程討論與決策流程</h3>
<h4 id="決策流程圖">決策流程圖</h4>





<pre tabindex="0"><code class="language-mermaid" data-lang="mermaid">graph TD
    A[代理人回報] --&gt; B[主線程接收]
    B --&gt; C{嚴重程度?}
    C --&gt;|阻塞| D[立即處理]
    C --&gt;|當日| E[當日處理]
    C --&gt;|資訊性| F[記錄並規劃]

    D --&gt; G[檢視相關文件]
    E --&gt; G
    F --&gt; G

    G --&gt; H[分析影響範圍]
    H --&gt; I{需要專家?}
    I --&gt;|是| J[呼叫 PM 或相關代理人]
    I --&gt;|否| K[主線程決策]

    J --&gt; K
    K --&gt; L[記錄決策理由]
    L --&gt; M[更新受影響任務]
    M --&gt; N[通知相關代理人]
    N --&gt; O[繼續執行]</code></pre><h4 id="決策記錄格式">決策記錄格式</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 決策記錄 - [問題簡述]
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gs">**決策日期**</span>: YYYY-MM-DD
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gs">**相關回報**</span>: [代理人回報連結]
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gs">**決策者**</span>: 主線程 / PM代理人
</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">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">### 問題摘要
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span>[簡要描述問題]
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu">### 決策內容
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu"></span>**決定**: [具體決策]
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gs">**理由**</span>:
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">1.</span> [理由 1]
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">2.</span> [理由 2]
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">3.</span> [理由 3]
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="gu">### 執行計畫
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> [動作 1]
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">- [ ]</span> [動作 2]
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">- [ ]</span> [動作 3]
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="gu">### 影響範圍
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="gu"></span><span class="k">-</span> **更新任務**: [列出需要更新的任務]
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="k">-</span> **更新文件**: [列出需要更新的文件]
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="k">-</span> **通知對象**: [列出需要通知的代理人]
</span></span><span class="line"><span class="ln">28</span><span class="cl">
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="gu">### 追蹤記錄
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="gu"></span><span class="k">-</span> YYYY-MM-DD: [執行狀態更新]
</span></span><span class="line"><span class="ln">31</span><span class="cl">- YYYY-MM-DD: [完成確認]</span></span></code></pre></div><h3 id="決策傳遞機制">決策傳遞機制</h3>
<h4 id="1-更新主版本工作日誌">1. 更新主版本工作日誌</h4>
<p>在 <code>vX.Y.0-main.md</code> 新增「決策記錄」區域：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln">1</span><span class="cl"><span class="gu">## 重要決策記錄
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="gu">### [決策 1 標題]
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="gu"></span><span class="k">-</span> **日期**: YYYY-MM-DD
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="k">-</span> **問題**: [簡述]
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="k">-</span> **決策**: [簡述]
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="k">-</span> **影響**: [列出受影響任務]
</span></span><span class="line"><span class="ln">8</span><span class="cl">- <span class="gs">**詳細**</span>: [連結到完整決策記錄]</span></span></code></pre></div><h4 id="2-更新受影響任務描述">2. 更新受影響任務描述</h4>
<p>所有受影響的任務都需要補充「決策影響」區域。</p>
<h4 id="3-通知相關代理人">3. 通知相關代理人</h4>
<p>如果任務已分派，需要明確通知代理人決策內容。</p>
<h3 id="溝通協作原則">溝通協作原則</h3>
<h4 id="代理人責任">代理人責任</h4>
<ul>
<li><strong>主動回報</strong>：發現問題立即回報，不自行假設</li>
<li><strong>完整資訊</strong>：提供足夠的背景資訊供決策</li>
<li><strong>建議方案</strong>：分析並提供具體建議</li>
<li><strong>等待確認</strong>：阻塞性問題必須等待主線程決策</li>
</ul>
<h4 id="主線程責任">主線程責任</h4>
<ul>
<li><strong>快速響應</strong>：阻塞問題當日處理完畢</li>
<li><strong>充分調研</strong>：檢視相關文件和規範</li>
<li><strong>清楚決策</strong>：明確說明決策理由</li>
<li><strong>完整傳遞</strong>：確保所有受影響方收到資訊</li>
</ul>
<h4 id="協作品質標準">協作品質標準</h4>
<ul>
<li><strong>響應時間</strong>: 阻塞問題 &lt; 2 小時，重要問題 &lt; 1 天</li>
<li><strong>決策品質</strong>: 必須基於專案規範和架構原則</li>
<li><strong>資訊完整</strong>: 所有決策必須文件化並傳遞</li>
<li><strong>追蹤閉環</strong>: 決策執行完成必須確認和記錄</li>
</ul>
<h3 id="任務分派規則">任務分派規則</h3>
<h4 id="1-任務優先序">1. 任務優先序</h4>
<ol>
<li><strong>架構核心任務</strong> - 影響多個模組的基礎架構變更</li>
<li><strong>高風險任務</strong> - 影響關鍵業務邏輯的重構</li>
<li><strong>中風險任務</strong> - 次要功能模組的重構</li>
<li><strong>低風險任務</strong> - 測試檔案和工具檔案的更新</li>
</ol>
<h4 id="2-任務大小控制">2. 任務大小控制</h4>
<p><strong>理想任務大小</strong>：</p>
<ul>
<li>執行時間：1-2 小時</li>
<li>影響檔案：1-5 個檔案</li>
<li>測試影響：單一測試套件</li>
<li>複雜度：單一職責變更</li>
</ul>
<p><strong>任務過大指標</strong>：</p>
<ul>
<li>影響檔案 &gt; 5 個</li>
<li>預估時間 &gt; 3 小時</li>
<li>涉及多個模組</li>
<li>需要連環修改</li>
</ul>
<h4 id="3-升級處理機制">3. 升級處理機制</h4>
<p><strong>何時觸發升級</strong>：</p>
<ul>
<li>子代理人回報任務規模過大</li>
<li>發現任務間複雜依賴關係</li>
<li>需要重新評估技術方案</li>
<li>遇到無法解決的技術問題</li>
</ul>
<p><strong>升級處理流程</strong>：</p>
<ol>
<li>子代理人向主線程回報問題</li>
<li>主線程呼叫 PM 代理人進行分析</li>
<li>PM 代理人拆分任務並建立小版本日誌</li>
<li>依新的小版本日誌重新分派任務</li>
</ol>
<h2 id="任務執行標準">任務執行標準</h2>
<h3 id="任務分派標準格式">任務分派標準格式</h3>
<p>每個任務分派必須包含：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 任務 vX.Y.Z - [任務名稱]
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 任務目標
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span><span class="k">-</span> 明確的完成目標描述
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">-</span> 具體的技術實現要求
</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 class="gu">### 參考文件（強制完整填寫）
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu">#### UseCase 參考（必須）
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu"></span><span class="k">-</span> [<span class="nt">UC-XX</span>](<span class="na">/record/app-use-cases.md#uc-xx</span>) - [Use Case 名稱]
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">-</span> [<span class="nt">UC-YY</span>](<span class="na">/record/app-use-cases.md#uc-yy</span>) - [相關 Use Case]
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gs">**說明**</span>: 明確指出此任務對應哪些 Use Case，確保實作符合業務需求。
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="gu">#### 流程圖參考（必須，具體到 Event）
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="gu"></span><span class="k">-</span> [<span class="nt">流程圖名稱 Event X-Y</span>](<span class="na">/record/use-cases/[feature]/[flow-name].md</span>)
</span></span><span class="line"><span class="ln">17</span><span class="cl">  <span class="k">-</span> Event X: [Event 描述] - [對應的類別/方法]
</span></span><span class="line"><span class="ln">18</span><span class="cl">  <span class="k">-</span> Event Y: [Event 描述] - [對應的類別/方法]
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="gs">**範例**</span>（來自 v0.11.5）:
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">-</span> [<span class="nt">主流程 Event 6-7</span>](<span class="na">/record/use-cases/unified-api/book-info-query-flow.md</span>)
</span></span><span class="line"><span class="ln">22</span><span class="cl">  <span class="k">-</span> Event 6: BookEnrichmentProcessor.process(rawData) - 處理原始資料
</span></span><span class="line"><span class="ln">23</span><span class="cl">  <span class="k">-</span> Event 7: BookMetadata.create() - 建立 Domain 實體
</span></span><span class="line"><span class="ln">24</span><span class="cl">
</span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="gs">**說明**</span>: 具體到 Event 編號，讓代理人知道要實作流程圖的哪個部分。
</span></span><span class="line"><span class="ln">26</span><span class="cl">
</span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="gu">#### 架構規範（必須）
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="gu"></span><span class="k">-</span> [<span class="nt">Domain 層設計</span>](<span class="na">/record/app-requirements-spec.md#domain-layer</span>) - 層級職責定義
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="k">-</span> [<span class="nt">錯誤處理規範</span>](<span class="na">/record/app-error-handling-design.md</span>) - 異常處理標準
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="k">-</span> [<span class="nt">資料轉換規範</span>](<span class="na">/record/domain-transformation-layer-design.md</span>) - DTO 轉換規則
</span></span><span class="line"><span class="ln">31</span><span class="cl">
</span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="gs">**說明**</span>: 確保實作符合專案架構原則。
</span></span><span class="line"><span class="ln">33</span><span class="cl">
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="gu">#### 依賴類別（前置任務產出）
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="gu"></span><span class="k">-</span> <span class="sb">`ClassName1`</span> (vX.Y.Z) - [簡短說明]
</span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="k">-</span> <span class="sb">`ClassName2`</span> (vX.Y.Z) - [簡短說明]
</span></span><span class="line"><span class="ln">37</span><span class="cl">
</span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="gs">**範例**</span>（來自 v0.11.5）:
</span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="k">-</span> <span class="sb">`BookQueryInput`</span> (v0.11.3) - 查詢輸入參數 Value Object
</span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="k">-</span> <span class="sb">`QueryType`</span> (v0.11.4) - 查詢類型枚舉
</span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="k">-</span> <span class="sb">`BookEnrichmentData`</span> (Domain 層) - <span class="gs">**已從 Infrastructure 遷移**</span>
</span></span><span class="line"><span class="ln">42</span><span class="cl">
</span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="gs">**說明**</span>: 列出此任務依賴的前置任務產出，包含版本號方便追溯。
</span></span><span class="line"><span class="ln">44</span><span class="cl">
</span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="gu">#### 測試設計參考（TDD 必須）
</span></span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="gu"></span><span class="k">-</span> [<span class="nt">測試設計文件</span>](<span class="na">/record/agile-programing-methodology/vX.Y.Z-test-design.md#section</span>)
</span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="k">-</span> 測試檔案: <span class="sb">`test_file_name_test.dart`</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="k">-</span> 測試用例數: N 個
</span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="k">-</span> 效能基準: [如有特殊要求]
</span></span><span class="line"><span class="ln">50</span><span class="cl">
</span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="gs">**說明**</span>: TDD 開發必須先參考測試設計，瞭解驗收標準。
</span></span><span class="line"><span class="ln">52</span><span class="cl">
</span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="gu">#### 實作範例（如有）
</span></span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="gu"></span><span class="k">-</span> [<span class="nt">類似實作參考</span>](<span class="na">/record/agile-programing-methodology/vX.Y.Z-work-log.md#implementation</span>)
</span></span><span class="line"><span class="ln">55</span><span class="cl"><span class="k">-</span> [<span class="nt">程式碼範例</span>](<span class="na">/record/docs/implementation-examples.md</span>)
</span></span><span class="line"><span class="ln">56</span><span class="cl">
</span></span><span class="line"><span class="ln">57</span><span class="cl"><span class="gs">**說明**</span>: 提供參考範例幫助代理人理解預期實作方式。
</span></span><span class="line"><span class="ln">58</span><span class="cl">
</span></span><span class="line"><span class="ln">59</span><span class="cl"><span class="gu">### 影響範圍
</span></span></span><span class="line"><span class="ln">60</span><span class="cl"><span class="gu"></span><span class="k">-</span> 需要修改的檔案清單
</span></span><span class="line"><span class="ln">61</span><span class="cl"><span class="k">-</span> 預估影響的測試檔案
</span></span><span class="line"><span class="ln">62</span><span class="cl"><span class="k">-</span> 影響的依賴關係
</span></span><span class="line"><span class="ln">63</span><span class="cl">
</span></span><span class="line"><span class="ln">64</span><span class="cl"><span class="gu">### 依賴關係
</span></span></span><span class="line"><span class="ln">65</span><span class="cl"><span class="gu"></span><span class="k">-</span> 前置任務：[必須完成的前置任務清單]
</span></span><span class="line"><span class="ln">66</span><span class="cl"><span class="k">-</span> 後續任務：[依賴此任務的後續任務]
</span></span><span class="line"><span class="ln">67</span><span class="cl"><span class="k">-</span> 並行任務：[可同時進行的相關任務]
</span></span><span class="line"><span class="ln">68</span><span class="cl">
</span></span><span class="line"><span class="ln">69</span><span class="cl"><span class="gu">### 完成標準
</span></span></span><span class="line"><span class="ln">70</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 功能實現完成
</span></span><span class="line"><span class="ln">71</span><span class="cl"><span class="k">- [ ]</span> 測試通過率 100%
</span></span><span class="line"><span class="ln">72</span><span class="cl"><span class="k">- [ ]</span> 程式碼品質檢查通過
</span></span><span class="line"><span class="ln">73</span><span class="cl"><span class="k">- [ ]</span> 無新增警告或錯誤
</span></span><span class="line"><span class="ln">74</span><span class="cl"><span class="k">- [ ]</span> 參考文件更新 (如有需要)
</span></span><span class="line"><span class="ln">75</span><span class="cl">
</span></span><span class="line"><span class="ln">76</span><span class="cl"><span class="gu">### 風險評估
</span></span></span><span class="line"><span class="ln">77</span><span class="cl"><span class="gu"></span><span class="k">-</span> 技術風險等級：高/中/低
</span></span><span class="line"><span class="ln">78</span><span class="cl"><span class="k">-</span> 潛在影響評估
</span></span><span class="line"><span class="ln">79</span><span class="cl"><span class="k">-</span> 回滾策略
</span></span><span class="line"><span class="ln">80</span><span class="cl">
</span></span><span class="line"><span class="ln">81</span><span class="cl"><span class="gu">### 指派代理人
</span></span></span><span class="line"><span class="ln">82</span><span class="cl"><span class="gu"></span><span class="k">-</span> 主要執行代理人：[Agent Name]
</span></span><span class="line"><span class="ln">83</span><span class="cl"><span class="k">-</span> 檢查代理人：cinnamon-refactor-owl
</span></span><span class="line"><span class="ln">84</span><span class="cl">
</span></span><span class="line"><span class="ln">85</span><span class="cl"><span class="gu">### 後續更新責任 (準備階段特有)
</span></span></span><span class="line"><span class="ln">86</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 更新相關任務的參考文件欄位
</span></span><span class="line"><span class="ln">87</span><span class="cl"><span class="k">- [ ]</span> 補充依賴任務的詳細資訊
</span></span><span class="line"><span class="ln">88</span><span class="cl">- [ ] 建立文件間的關聯性</span></span></code></pre></div><h3 id="任務完成回報格式">任務完成回報格式</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 任務完成回報 - vX.Y.Z
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 完成項目
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span><span class="k">- [x]</span> 目標1 完成
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">- [x]</span> 目標2 完成
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">- [x]</span> 測試驗證通過
</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"><span class="gu">### 品質指標
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu"></span><span class="k">-</span> 測試通過率：100%
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">-</span> 程式碼覆蓋率：維持或提升
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">-</span> 效能影響：無負面影響
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu">### 技術變更摘要
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu"></span><span class="k">-</span> 修改檔案數量：N 個
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">-</span> 新增程式碼行數：+N 行
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">-</span> 移除程式碼行數：-N 行
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="gu">### 發現問題
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="gu"></span><span class="k">-</span> 無問題 / 發現問題清單
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="gu">### 後續建議
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="gu"></span><span class="k">-</span> 下一個任務的建議
</span></span><span class="line"><span class="ln">23</span><span class="cl">- 潛在改善機會</span></span></code></pre></div><h2 id="品質控制機制">品質控制機制</h2>
<h3 id="強制檢查點">強制檢查點</h3>
<h4 id="任務執行前檢查">任務執行前檢查</h4>
<ul>
<li><input disabled="" type="checkbox"> 任務目標明確定義</li>
<li><input disabled="" type="checkbox"> 完成標準可測量</li>
<li><input disabled="" type="checkbox"> 指派代理人明確</li>
<li><input disabled="" type="checkbox"> 風險評估完成</li>
<li><input disabled="" type="checkbox"> <strong>參考文件完整性（強制）</strong> - UseCase、流程圖 Event、架構規範、依賴類別、測試設計全部填寫</li>
<li><input disabled="" type="checkbox"> <strong>影響範圍明確性（強制）</strong> - 需建立/修改/影響的檔案清單完整列出</li>
</ul>
<h4 id="任務執行中檢查">任務執行中檢查</h4>
<ul>
<li><input disabled="" type="checkbox"> 定期進度回報</li>
<li><input disabled="" type="checkbox"> 問題及時升級</li>
<li><input disabled="" type="checkbox"> 品質標準持續監控</li>
<li><input disabled="" type="checkbox"> 測試覆蓋率維持</li>
</ul>
<h4 id="任務完成後檢查">任務完成後檢查</h4>
<ul>
<li><input disabled="" type="checkbox"> 重構代理人驗證通過</li>
<li><input disabled="" type="checkbox"> 測試通過率 100%</li>
<li><input disabled="" type="checkbox"> 程式碼品質符合標準</li>
<li><input disabled="" type="checkbox"> 文件同步更新</li>
</ul>
<h3 id="階段完成驗證機制">階段完成驗證機制</h3>
<p><strong>重要</strong>: 基於實戰經驗學習，每個開發階段必須通過完整驗證才能標記為完成。</p>
<h4 id="階段完成檢查清單-stage-completion-checklist">階段完成檢查清單 (Stage Completion Checklist)</h4>
<h5 id="1-編譯完整性檢查-compilation-integrity">1. <strong>編譯完整性檢查 (Compilation Integrity)</strong></h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><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">flutter analyze lib/ --no-fatal-warnings
</span></span><span class="line"><span class="ln">3</span><span class="cl">dart analyze lib/ --no-fatal-warnings
</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"><span class="c1"># 檢查是否有 error 級別問題</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">flutter analyze lib/ 2&gt;<span class="p">&amp;</span><span class="m">1</span> <span class="p">|</span> grep -E <span class="s2">&#34;error&#34;</span> <span class="o">||</span> <span class="nb">echo</span> <span class="s2">&#34;編譯檢查通過&#34;</span></span></span></code></pre></div><p><strong>通過標準</strong>:</p>
<ul>
<li><input disabled="" type="checkbox"> 0 個 error 級別問題</li>
<li><input disabled="" type="checkbox"> warning 和 info 級別問題可接受</li>
<li><input disabled="" type="checkbox"> 所有 import 語句正確解析</li>
</ul>
<h5 id="2-依賴路徑一致性檢查-dependency-path-consistency">2. <strong>依賴路徑一致性檢查 (Dependency Path Consistency)</strong></h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><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">flutter analyze lib/ 2&gt;<span class="p">&amp;</span><span class="m">1</span> <span class="p">|</span> grep <span class="s2">&#34;Target of URI doesn&#39;t exist&#34;</span> <span class="o">||</span> <span class="nb">echo</span> <span class="s2">&#34;路徑檢查通過&#34;</span>
</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 class="c1"># 檢查是否符合 package 導入規範</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">grep -r <span class="s2">&#34;import &#39;\.\.&#34;</span> lib/ <span class="o">||</span> <span class="nb">echo</span> <span class="s2">&#34;無相對路徑導入&#34;</span>
</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 class="c1"># 檢查是否有重複或錯誤的路徑引用</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl">find lib/ -name <span class="s2">&#34;*.dart&#34;</span> -exec grep -l <span class="s2">&#34;package:book_overview_app&#34;</span> <span class="o">{}</span> <span class="se">\;</span> <span class="p">|</span> sort</span></span></code></pre></div><p><strong>通過標準</strong>:</p>
<ul>
<li><input disabled="" type="checkbox"> 無「Target of URI doesn&rsquo;t exist」錯誤</li>
<li><input disabled="" type="checkbox"> 100% 使用 package 導入格式，0% 相對路徑</li>
<li><input disabled="" type="checkbox"> 所有路徑指向正確的檔案位置</li>
</ul>
<h5 id="3-測試通過率檢查-test-pass-rate">3. <strong>測試通過率檢查 (Test Pass Rate)</strong></h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 100% 測試通過率要求</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">dart <span class="nb">test</span> --coverage
</span></span><span class="line"><span class="ln">3</span><span class="cl">flutter <span class="nb">test</span> --coverage
</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"><span class="c1"># 確保所有測試套件都能執行</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">dart <span class="nb">test</span> test/unit/ --reporter<span class="o">=</span>expanded
</span></span><span class="line"><span class="ln">7</span><span class="cl">flutter <span class="nb">test</span> test/widget/ --reporter<span class="o">=</span>expanded
</span></span><span class="line"><span class="ln">8</span><span class="cl">flutter <span class="nb">test</span> test/integration/ --reporter<span class="o">=</span>expanded</span></span></code></pre></div><p><strong>通過標準</strong>:</p>
<ul>
<li><input disabled="" type="checkbox"> 100% 測試通過率，無例外</li>
<li><input disabled="" type="checkbox"> 所有測試套件正常執行</li>
<li><input disabled="" type="checkbox"> 無測試環境錯誤</li>
<li><input disabled="" type="checkbox"> 測試覆蓋率不下降</li>
</ul>
<h5 id="4-重複實作檢查-duplicate-implementation-check">4. <strong>重複實作檢查 (Duplicate Implementation Check)</strong></h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><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">find lib/ -name <span class="s2">&#34;*service*.dart&#34;</span> <span class="p">|</span> sort
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">find lib/ -name <span class="s2">&#34;*provider*.dart&#34;</span> <span class="p">|</span> sort
</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"><span class="c1"># 檢查類似命名的類別</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">grep -r <span class="s2">&#34;class.*Service&#34;</span> lib/ --include<span class="o">=</span><span class="s2">&#34;*.dart&#34;</span> <span class="p">|</span> sort
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">grep -r <span class="s2">&#34;class.*Provider&#34;</span> lib/ --include<span class="o">=</span><span class="s2">&#34;*.dart&#34;</span> <span class="p">|</span> sort
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"># 檢查是否有功能重疊的實作</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;手動檢查是否有功能重複的實作&#34;</span></span></span></code></pre></div><p><strong>通過標準</strong>:</p>
<ul>
<li><input disabled="" type="checkbox"> 無功能重複的服務實作</li>
<li><input disabled="" type="checkbox"> 類別命名符合單一職責原則</li>
<li><input disabled="" type="checkbox"> 無廢棄或未使用的實作</li>
<li><input disabled="" type="checkbox"> 依賴關係清晰無衝突</li>
</ul>
<h5 id="5-架構一致性檢查-architecture-consistency">5. <strong>架構一致性檢查 (Architecture Consistency)</strong></h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><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">ls -la lib/core/services/
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">ls -la lib/domains/*/services/
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">ls -la lib/presentation/*/services/
</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="c1"># 驗證 Clean Architecture 分層</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;確認檔案位置符合 Clean Architecture 原則&#34;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"># 檢查導入路徑是否符合架構設計</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">grep -r <span class="s2">&#34;import.*lib/core&#34;</span> lib/presentation/ <span class="o">||</span> <span class="nb">echo</span> <span class="s2">&#34;無違反架構的導入&#34;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">grep -r <span class="s2">&#34;import.*lib/presentation&#34;</span> lib/core/ <span class="o">||</span> <span class="nb">echo</span> <span class="s2">&#34;無反向依賴&#34;</span></span></span></code></pre></div><p><strong>通過標準</strong>:</p>
<ul>
<li><input disabled="" type="checkbox"> 檔案位置符合 Clean Architecture 分層</li>
<li><input disabled="" type="checkbox"> 無跨層直接依賴違規</li>
<li><input disabled="" type="checkbox"> 依賴方向正確 (外層依賴內層)</li>
<li><input disabled="" type="checkbox"> 服務檔案位於正確的目錄結構</li>
</ul>
<h4 id="階段完成強制流程"><strong>階段完成強制流程</strong></h4>
<p>每個開發階段必須按以下順序執行：</p>
<ol>
<li><strong>完成階段開發工作</strong></li>
<li><strong>執行編譯檢查</strong> - 必須 0 error</li>
<li><strong>執行測試檢查</strong> - 必須 100% 通過</li>
<li><strong>檢查路徑一致性</strong> - 修正所有 import 問題</li>
<li><strong>檢查重複實作</strong> - 確保無功能重複</li>
<li><strong>驗證架構一致性</strong> - 檔案位置符合設計</li>
<li><strong>更新工作日誌</strong> - 記錄檢查結果</li>
<li><strong>標記階段完成</strong> - 更新 todolist 狀態</li>
</ol>
<h4 id="階段失敗處理原則"><strong>階段失敗處理原則</strong></h4>
<ul>
<li><strong>任何檢查項目失敗 = 階段未完成</strong></li>
<li><strong>必須修正所有問題後重新檢查</strong></li>
<li><strong>不允許「暫時跳過」或「之後處理」</strong></li>
<li><strong>問題修正優先於新功能開發</strong></li>
<li><strong>重複檢查直到所有項目通過</strong></li>
</ul>
<h4 id="檢查清單整合到工作日誌"><strong>檢查清單整合到工作日誌</strong></h4>
<h5 id="每個開發階段的工作日誌必須包含">每個開發階段的工作日誌必須包含</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 階段完成檢查
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 編譯檢查
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> flutter analyze 無 error
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">- [ ]</span> dart analyze 無 error
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">- [ ]</span> 無「Target of URI doesn&#39;t exist」錯誤
</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"><span class="gu">### 測試檢查
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> dart test 100% 通過
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">- [ ]</span> flutter test 100% 通過
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">- [ ]</span> 測試覆蓋率檢查完成
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu">### 一致性檢查
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 無相對路徑 import
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">- [ ]</span> 無重複服務實作
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">- [ ]</span> 檔案位置符合架構設計
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="gu">### 最終確認
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 所有檢查項目通過
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">- [ ]</span> 工作日誌更新完成
</span></span><span class="line"><span class="ln">21</span><span class="cl">- [ ] 準備進入下一階段</span></span></code></pre></div><h4 id="hook-系統整合-1"><strong>Hook 系統整合</strong></h4>
<p>此階段完成驗證機制與現有 Hook 系統整合：</p>
<ul>
<li><strong>PostEdit Hook</strong> - 檔案修改後自動檢查編譯狀態</li>
<li><strong>Version Check Hook</strong> - 階段推進前強制執行完整檢查</li>
<li><strong>Code Smell Detection Hook</strong> - 即時偵測重複實作問題</li>
<li><strong>PM Trigger Hook</strong> - 檢查失敗時觸發專案管理介入</li>
</ul>
<h3 id="失敗處理機制">失敗處理機制</h3>
<h4 id="任務失敗類型">任務失敗類型</h4>
<ol>
<li><strong>技術失敗</strong> - 無法達成技術目標</li>
<li><strong>品質失敗</strong> - 無法通過品質檢查</li>
<li><strong>時程失敗</strong> - 超出預估完成時間</li>
<li><strong>範圍失敗</strong> - 任務範圍超出預期</li>
</ol>
<h4 id="失敗處理策略">失敗處理策略</h4>
<ol>
<li><strong>技術失敗</strong> → PM 代理人重新設計技術方案</li>
<li><strong>品質失敗</strong> → 重構代理人指導修正</li>
<li><strong>時程失敗</strong> → PM 代理人拆分任務</li>
<li><strong>範圍失敗</strong> → 主線程重新分派任務</li>
</ol>
<h2 id="進度追蹤機制">進度追蹤機制</h2>
<h3 id="三重文件原則整合">三重文件原則整合</h3>
<p>本方法論的進度追蹤完全基於「三重文件原則」（詳見前述章節）：</p>
<ol>
<li><strong>CHANGELOG.md</strong> - 版本功能變動（面向用戶）</li>
<li><strong>todolist.md</strong> - 任務全景圖（面向開發）</li>
<li><strong>work-log/</strong> - 實作細節（面向交接）</li>
</ol>
<h3 id="三層進度管理對應-work-log">三層進度管理（對應 work-log）</h3>
<p><strong>重要說明</strong>：三層進度管理主要對應到 work-log 文件系統，與 CHANGELOG 和 todolist 協同運作。</p>
<h4 id="1-大版本層級-vxyx---對應主版本工作日誌">1. 大版本層級 (vX.Y.x) - 對應主版本工作日誌</h4>
<ul>
<li><strong>追蹤文件</strong>: <code>docs/work-logs/vX.Y.0-main.md</code> (主版本總覽)</li>
<li><strong>對應三重文件</strong>:
<ul>
<li><strong>work-log</strong>: 詳細任務分派和執行記錄</li>
<li><strong>todolist</strong>: vX.Y.x 系列所有任務清單</li>
<li><strong>CHANGELOG</strong>: 版本發布時提取功能變動</li>
</ul>
</li>
<li><strong>更新頻率</strong>: 任務分派時、階段完成時</li>
<li><strong>責任代理</strong>: 主線程、PM 代理人</li>
</ul>
<h4 id="2-小版本層級-vxy1-vxy2----對應小版本工作日誌">2. 小版本層級 (vX.Y.1, vX.Y.2, &hellip;) - 對應小版本工作日誌</h4>
<ul>
<li><strong>追蹤文件</strong>: <code>docs/work-logs/vX.Y.Z-[specific-task].md</code> (具體任務)</li>
<li><strong>對應三重文件</strong>:
<ul>
<li><strong>work-log</strong>: 完整的實作細節和 TDD 階段記錄</li>
<li><strong>todolist</strong>: 單一任務狀態更新</li>
<li><strong>CHANGELOG</strong>: 任務完成後潛在的功能變動點</li>
</ul>
</li>
<li><strong>更新頻率</strong>: 任務執行中、任務完成時</li>
<li><strong>責任代理</strong>: 執行代理人、文件代理人</li>
</ul>
<h4 id="3-任務層級-todolist---對應-todolistmd">3. 任務層級 (TodoList) - 對應 todolist.md</h4>
<ul>
<li><strong>追蹤工具</strong>: TodoWrite 工具 + <code>docs/todolist.md</code></li>
<li><strong>對應三重文件</strong>:
<ul>
<li><strong>todolist</strong>: 任務狀態即時追蹤</li>
<li><strong>work-log</strong>: 任務詳細資訊來源</li>
<li><strong>CHANGELOG</strong>: 完成任務累積的功能變動</li>
</ul>
</li>
<li><strong>更新頻率</strong>: 即時更新</li>
<li><strong>責任代理</strong>: 所有代理人</li>
</ul>
<h3 id="三重文件同步機制">三重文件同步機制</h3>
<h4 id="任務執行流程中的文件更新">任務執行流程中的文件更新</h4>





<pre tabindex="0"><code class="language-mermaid" data-lang="mermaid">sequenceDiagram
    participant 主線程
    participant 執行代理人
    participant todolist
    participant work-log
    participant CHANGELOG

    主線程-&gt;&gt;todolist: 檢視下一個待執行任務
    主線程-&gt;&gt;work-log: 查看 vX.Y.0-main.md 分派清單
    主線程-&gt;&gt;執行代理人: 分派任務 vX.Y.Z

    執行代理人-&gt;&gt;work-log: 記錄執行過程到 vX.Y.Z.md
    執行代理人-&gt;&gt;todolist: 更新任務狀態為「進行中」

    執行代理人-&gt;&gt;work-log: 標記任務完成
    執行代理人-&gt;&gt;todolist: 更新任務狀態為「完成」

    Note over 主線程,CHANGELOG: 版本發布時
    主線程-&gt;&gt;work-log: 檢視所有已完成任務
    主線程-&gt;&gt;CHANGELOG: 提取功能變動
    主線程-&gt;&gt;todolist: 確認版本完成</code></pre><h4 id="版本發布時的三重文件協調">版本發布時的三重文件協調</h4>
<ol>
<li>
<p><strong>文件代理人檢查 work-log</strong>：</p>
<ul>
<li>讀取 <code>vX.Y.0-main.md</code> 所有已完成任務</li>
<li>讀取每個 <code>vX.Y.Z-task.md</code> 實作細節</li>
</ul>
</li>
<li>
<p><strong>提取功能變動到 CHANGELOG</strong>：</p>
<ul>
<li>只記錄用戶可感知的功能變動</li>
<li>忽略內部重構和技術細節</li>
<li>依照 Keep a Changelog 格式撰寫</li>
</ul>
</li>
<li>
<p><strong>驗證 todolist 一致性</strong>：</p>
<ul>
<li>確認 todolist 標記的完成任務與 work-log 一致</li>
<li>確認版本號對應正確</li>
</ul>
</li>
</ol>
<h3 id="關鍵指標監控">關鍵指標監控</h3>
<h4 id="效率指標">效率指標</h4>
<ul>
<li>任務完成速度 (任務數/天)</li>
<li>平均任務執行時間</li>
<li>升級請求頻率</li>
<li>重做次數</li>
</ul>
<h4 id="品質指標">品質指標</h4>
<ul>
<li>測試通過率趨勢</li>
<li>程式碼覆蓋率變化</li>
<li>新增錯誤/警告數量</li>
<li>效能回歸情況</li>
</ul>
<h4 id="協作指標">協作指標</h4>
<ul>
<li>代理人回應時間</li>
<li>任務交接效率</li>
<li>溝通品質評分</li>
<li>問題解決時間</li>
</ul>
<h2 id="成功標準">成功標準</h2>
<h3 id="方法論成功指標">方法論成功指標</h3>
<h4 id="效率成功標準">效率成功標準</h4>
<ul>
<li><input disabled="" type="checkbox"> 30-40 個小版本任務如期完成</li>
<li><input disabled="" type="checkbox"> 每個任務平均完成時間 &lt; 2 小時</li>
<li><input disabled="" type="checkbox"> 升級請求 &lt; 10% 總任務數</li>
<li><input disabled="" type="checkbox"> 重做次數 &lt; 5% 總任務數</li>
</ul>
<h4 id="品質成功標準">品質成功標準</h4>
<ul>
<li><input disabled="" type="checkbox"> 100% 測試通過率維持</li>
<li><input disabled="" type="checkbox"> 程式碼覆蓋率不下降</li>
<li><input disabled="" type="checkbox"> 0 個新增的錯誤或警告</li>
<li><input disabled="" type="checkbox"> 效能目標全部達成</li>
</ul>
<h4 id="協作成功標準">協作成功標準</h4>
<ul>
<li><input disabled="" type="checkbox"> 代理人角色職責清晰</li>
<li><input disabled="" type="checkbox"> 任務交接無遺漏</li>
<li><input disabled="" type="checkbox"> 問題升級機制有效</li>
<li><input disabled="" type="checkbox"> 文件同步及時準確</li>
</ul>
<h4 id="階段驗證成功標準">階段驗證成功標準</h4>
<ul>
<li><input disabled="" type="checkbox"> 每個階段 100% 通過編譯檢查</li>
<li><input disabled="" type="checkbox"> 每個階段 100% 通過測試檢查</li>
<li><input disabled="" type="checkbox"> 每個階段 100% 通過路徑一致性檢查</li>
<li><input disabled="" type="checkbox"> 每個階段 100% 通過重複實作檢查</li>
<li><input disabled="" type="checkbox"> 每個階段 100% 通過架構一致性檢查</li>
<li><input disabled="" type="checkbox"> 階段失敗率 &lt; 5%</li>
<li><input disabled="" type="checkbox"> 平均階段驗證時間 &lt; 30 分鐘</li>
</ul>
<h3 id="重構成功標準">重構成功標準</h3>
<h4 id="架構成功標準">架構成功標準</h4>
<ul>
<li><input disabled="" type="checkbox"> 完全符合設計文件 v2.0 規範</li>
<li><input disabled="" type="checkbox"> 原生 Exception 系統實作完成</li>
<li><input disabled="" type="checkbox"> 舊錯誤處理系統完全移除</li>
<li><input disabled="" type="checkbox"> 統一錯誤處理入口建立</li>
</ul>
<h4 id="效能成功標準">效能成功標準</h4>
<ul>
<li><input disabled="" type="checkbox"> 錯誤建立時間 &lt; 0.1ms</li>
<li><input disabled="" type="checkbox"> 記憶體占用 &lt; 200 bytes</li>
<li><input disabled="" type="checkbox"> 程式碼量減少 ~80%</li>
<li><input disabled="" type="checkbox"> 編譯時間改善</li>
</ul>
<h2 id="敏捷開發本質">敏捷開發本質</h2>
<h3 id="核心理念">核心理念</h3>
<h4 id="敏捷開發是持續發現快速調整資訊流動的動態過程">敏捷開發是持續發現、快速調整、資訊流動的動態過程</h4>
<h3 id="資訊流動機制">資訊流動機制</h3>
<h4 id="phase-間的資訊傳遞">Phase 間的資訊傳遞</h4>





<pre tabindex="0"><code class="language-mermaid" data-lang="mermaid">graph TD
    A[Phase 1 設計] --&gt;|架構決策| B[Phase 2 測試設計]
    B --&gt;|實作指引| C[Phase 3 實作]
    C --&gt;|重構發現| D[Phase 4 重構]

    B --&gt;|問題識別| E[架構調整]
    E --&gt;|更新設計| A
    E --&gt;|更新測試| B
    E --&gt;|更新實作| C

    C --&gt;|技術債務| F[文件補充]
    F --&gt;|參考資料| C
    F --&gt;|設計優化| A</code></pre><h4 id="v011-實際資訊流範例">v0.11 實際資訊流範例</h4>
<p><strong>Phase 1 → Phase 2 傳遞</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">Phase 1 (v0.11.1) 產出:
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="k">-</span> 44 個 Event 流程設計
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="k">-</span> 3 個核心流程圖
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="k">-</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">Phase 2 (v0.11.2) 接收並發現:
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">-</span> 流程圖定義清楚
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">-</span> 注意：BookEnrichmentData 位置錯誤
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">-</span> 4 個命名不一致
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">-</span> Domain 服務層缺失
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">Phase 2 回饋 Phase 1:
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">-</span> 修正架構問題 → 立即執行
</span></span><span class="line"><span class="ln">14</span><span class="cl">- 更新流程圖命名 → 完成修正</span></span></code></pre></div><p><strong>Phase 2 → Phase 3 傳遞</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">Phase 2 (v0.11.2) 產出:
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="k">-</span> 335 個測試用例設計
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="k">-</span> 33 個測試檔案規劃
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="k">-</span> 架構修正決策
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">-</span> 時程調整 (Plan B)
</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">Phase 3 (v0.11.3-14) 接收並執行:
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">-</span> 測試先行 TDD 開發
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">-</span> 依賴 Phase 2 架構修正
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">-</span> 參考測試設計驗收標準
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">-</span> 遵循調整後時程
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl">Phase 3 動態更新 Phase 2 發現:
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">-</span> 補充任務參考文件
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">-</span> 更新依賴類別資訊
</span></span><span class="line"><span class="ln">16</span><span class="cl">- 調整實作優先序</span></span></code></pre></div><h3 id="靈活任務交接">靈活任務交接</h3>
<h4 id="交接不是單向傳遞">交接不是單向傳遞</h4>
<p><strong>傳統錯誤模式</strong>：</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">Phase 1 完成 → 凍結
</span></span><span class="line"><span class="ln">2</span><span class="cl">Phase 2 開始 → 發現問題但不敢改 Phase 1
</span></span><span class="line"><span class="ln">3</span><span class="cl">Phase 3 繼續 → 基於錯誤的設計實作</span></span></code></pre></div><p><strong>敏捷正確模式</strong>：</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">Phase 1 完成 → 基礎版本
</span></span><span class="line"><span class="ln">2</span><span class="cl">Phase 2 發現 → 立即回報並調整 Phase 1
</span></span><span class="line"><span class="ln">3</span><span class="cl">Phase 1 更新 → 同步更新 Phase 2 和 Phase 3 參考
</span></span><span class="line"><span class="ln">4</span><span class="cl">Phase 3 執行 → 基於最新、最正確的設計</span></span></code></pre></div><h4 id="任務交接檢查清單">任務交接檢查清單</h4>
<p><strong>前置任務完成時</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 任務交接檢查 - vX.Y.Z 完成
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 產出清單
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 程式碼檔案: [列出新建/修改檔案]
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">- [ ]</span> 測試檔案: [列出測試覆蓋]
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">- [ ]</span> 文件更新: [列出相關文件]
</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"><span class="gu">### 後續任務更新
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 檢查哪些任務依賴此產出
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">- [ ]</span> 更新後續任務的「依賴類別」參考
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">- [ ]</span> 補充實作範例連結
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">- [ ]</span> 通知相關代理人（如已分派）
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu">### 發現問題記錄
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 是否有架構問題影響後續任務
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">- [ ]</span> 是否有命名需要統一
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">- [ ]</span> 是否有技術債務需要標註
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">- [ ]</span> 是否需要調整後續任務範圍
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="gu">### 主版本日誌更新
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="gu"></span><span class="k">- [ ]</span> 標記任務完成
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">- [ ]</span> 補充「重要發現」區域
</span></span><span class="line"><span class="ln">23</span><span class="cl">- [ ] 更新進度統計</span></span></code></pre></div><h3 id="持續文件精煉">持續文件精煉</h3>
<h4 id="文件不是一次性產物">文件不是一次性產物</h4>
<p><strong>文件生命週期</strong>：</p>





<pre tabindex="0"><code class="language-mermaid" data-lang="mermaid">graph LR
    A[初始版本] --&gt; B[Phase 1 補充]
    B --&gt; C[Phase 2 修正]
    C --&gt; D[Phase 3 完善]
    D --&gt; E[Phase 4 定版]

    C --&gt;|發現問題| F[緊急更新]
    F --&gt; C

    D --&gt;|實作發現| G[補充範例]
    G --&gt; D</code></pre><h4 id="文件更新觸發點">文件更新觸發點</h4>
<h5 id="1-強制更新時機">1. 強制更新時機</h5>
<ul>
<li>架構變更 → 立即更新所有相關文件</li>
<li>介面簽名變更 → 更新流程圖和參考</li>
<li>依賴關係變化 → 更新任務依賴清單</li>
<li>命名修正 → 全專案統一更新</li>
</ul>
<h5 id="2-建議更新時機">2. 建議更新時機</h5>
<ul>
<li>發現更好的實作方式 → 補充範例</li>
<li>常見問題解決 → 補充 FAQ</li>
<li>效能優化技巧 → 補充最佳實踐</li>
<li>測試技巧發現 → 補充測試指引</li>
</ul>
<h5 id="3-v011-實際更新案例">3. v0.11 實際更新案例</h5>
<h6 id="案例-1架構修正觸發連鎖更新">案例 1：架構修正觸發連鎖更新</h6>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">觸發: BookEnrichmentData 遷移到 Domain 層
</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 class="k">1.</span> 流程圖 book-info-query-flow.md
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">   <span class="k">-</span> Event 6-7 層級標註
</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 class="k">2.</span> 主版本日誌 v0.11.0-main.md
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">   <span class="k">-</span> 架構調整記錄
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">   <span class="k">-</span> v0.11.5, v0.11.6 依賴類別更新
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">3.</span> 測試設計 v0.11.2-tdd-phase2-test-design.md
</span></span><span class="line"><span class="ln">12</span><span class="cl">   <span class="k">-</span> Mock 設定路徑更新
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">4.</span> 所有後續任務描述
</span></span><span class="line"><span class="ln">15</span><span class="cl">   <span class="k">-</span> 參考文件補充
</span></span><span class="line"><span class="ln">16</span><span class="cl">   - import 路徑範例</span></span></code></pre></div><h6 id="案例-2phase-2-發現補充任務參考">案例 2：Phase 2 發現補充任務參考</h6>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl">觸發: Phase 2 完成測試設計
</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 class="k">1.</span> v0.11.5-14 所有任務
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">   <span class="k">-</span> 新增「測試設計參考」欄位
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">   <span class="k">-</span> 具體到測試檔案名稱和用例數
</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"><span class="k">2.</span> v0.11.0-main.md
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">   <span class="k">-</span> 補充「Phase 2 重要發現」區域
</span></span><span class="line"><span class="ln">10</span><span class="cl">   <span class="k">-</span> 記錄時程調整和原因
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">3.</span> 依賴類別參考
</span></span><span class="line"><span class="ln">13</span><span class="cl">   <span class="k">-</span> v0.11.3, v0.11.4 產出類別
</span></span><span class="line"><span class="ln">14</span><span class="cl">   - 列入所有相關任務參考</span></span></code></pre></div><h3 id="響應式規劃">響應式規劃</h3>
<h4 id="規劃不是固定藍圖">規劃不是固定藍圖</h4>
<p><strong>初始規劃基於</strong>：</p>
<ul>
<li>現有知識和經驗</li>
<li>需求分析結果</li>
<li>架構設計假設</li>
<li>風險初步評估</li>
</ul>
<p><strong>執行中調整基於</strong>：</p>
<ul>
<li>實際發現的問題</li>
<li>技術方案驗證結果</li>
<li>測試設計識別的複雜度</li>
<li>代理人的回報和建議</li>
</ul>
<h4 id="調整決策標準">調整決策標準</h4>
<h5 id="何時調整規劃">何時調整規劃？</h5>
<p><strong>立即調整（阻塞性）</strong>：</p>
<ul>
<li>架構設計根本性錯誤</li>
<li>依賴關係衝突無法解決</li>
<li>技術方案不可行</li>
<li>測試通過率無法達成</li>
</ul>
<p><strong>範例</strong>：BookEnrichmentData 位置錯誤 → 立即停止並修正</p>
<p><strong>計畫調整（重要性）</strong>：</p>
<ul>
<li>任務複雜度超出預期</li>
<li>需要額外的前置任務</li>
<li>測試用例數量大幅增加</li>
<li>發現缺失的功能模組</li>
</ul>
<p><strong>範例</strong>：Domain 服務層缺失 → 調整時程從 4-6 天到 6-7 天</p>
<p><strong>優化調整（改善性）</strong>：</p>
<ul>
<li>發現更好的實作方式</li>
<li>可以提前完成的任務</li>
<li>可以合併的重複工作</li>
<li>效能優化機會</li>
</ul>
<h5 id="調整必須文件化">調整必須文件化</h5>
<p><strong>調整決策記錄格式</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 規劃調整決策 - [調整標題]
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gs">**調整日期**</span>: YYYY-MM-DD
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gs">**觸發原因**</span>: [為什麼需要調整]
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gs">**影響範圍**</span>: [哪些任務受影響]
</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 class="gu">### 原始規劃
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="gu"></span><span class="k">-</span> 任務數量: N 個
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">-</span> 預估時程: X 天
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">-</span> 假設條件: [列出]
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu">### 調整後規劃
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu"></span><span class="k">-</span> 任務數量: M 個
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">-</span> 預估時程: Y 天
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">-</span> 調整原因: [詳細說明]
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="gu">### 具體變更
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="gu"></span><span class="k">1.</span> [變更 1]
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">2.</span> [變更 2]
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">3.</span> [變更 3]
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="gu">### 風險評估
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="gu"></span><span class="k">-</span> 新風險: [識別出的新風險]
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="k">-</span> 緩解措施: [對應的處理方式]
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="gu">### 溝通記錄
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="gu"></span><span class="k">-</span> YYYY-MM-DD: [通知相關代理人]
</span></span><span class="line"><span class="ln">28</span><span class="cl">- YYYY-MM-DD: [更新主版本日誌]</span></span></code></pre></div><h4 id="v011-規劃調整實例">v0.11 規劃調整實例</h4>
<p><strong>Plan B 決策</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 規劃調整決策 - v0.11.0 採用 Plan B
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gs">**調整日期**</span>: 2025-09-30
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gs">**觸發原因**</span>: Phase 2 識別出 28 個 TDD 新建測試和 Domain 層缺失
</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="gu">### 原始規劃
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span><span class="k">-</span> 任務數量: 10-12 個小版本
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">-</span> 預估時程: 4-6 天
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">-</span> 假設: 60% 實作已完成，40% 補充測試
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu">### 調整後規劃
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu"></span><span class="k">-</span> 任務數量: 14 個小版本
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">-</span> 預估時程: 6-7 天 (+1-2 天)
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">-</span> 原因: Domain 層需從零建立，測試覆蓋率需 100%
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="gu">### 具體變更
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 新增 v0.11.3-4：Domain 輸入驗證和查詢服務
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">2.</span> 延後外部 API 整合到 v0.12.0
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">3.</span> 簡化速率控制，移除批次處理和斷路器
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">4.</span> 專注核心功能，確保品質優先
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="gu">### 風險評估
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="gu"></span><span class="k">-</span> 新風險: 時程延長 1-2 天
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="k">-</span> 緩解: 簡化外部 API 整合，移到下個版本
</span></span><span class="line"><span class="ln">25</span><span class="cl">- 好處: Domain 層基礎扎實，後續開發更順暢</span></span></code></pre></div><h3 id="敏捷成功關鍵">敏捷成功關鍵</h3>
<h4 id="1-資訊透明化">1. 資訊透明化</h4>
<ul>
<li>所有發現立即記錄</li>
<li>所有決策公開透明</li>
<li>所有變更同步通知</li>
</ul>
<h4 id="2-快速響應能力">2. 快速響應能力</h4>
<ul>
<li>問題當日解決</li>
<li>調整迅速執行</li>
<li>文件即時更新</li>
</ul>
<h4 id="3-持續改進循環">3. 持續改進循環</h4>
<ul>
<li>每個 Phase 總結學習</li>
<li>問題根因分析</li>
<li>流程持續優化</li>
</ul>
<h4 id="4-團隊協作文化">4. 團隊協作文化</h4>
<ul>
<li>鼓勵主動回報</li>
<li>容許計畫調整</li>
<li>品質高於速度</li>
</ul>
<h2 id="使用指引">使用指引</h2>
<h3 id="主線程使用方式">主線程使用方式</h3>
<ol>
<li><strong>檢查主版本工作日誌</strong>，找到下一個待執行任務</li>
<li><strong>分派任務</strong>給適當的子代理人，使用標準任務格式</li>
<li><strong>等待回報</strong>，不親自執行任何程式碼修改</li>
<li><strong>處理升級</strong>，必要時呼叫 PM 代理人</li>
<li><strong>確認完成</strong>，由重構代理人和文件代理人完成驗證</li>
</ol>
<h3 id="子代理人使用方式">子代理人使用方式</h3>
<ol>
<li><strong>接收任務</strong>，確認任務目標和完成標準</li>
<li><strong>評估規模</strong>，如發現過大立即向上回報</li>
<li><strong>執行任務</strong>，專注於指派的具體工作</li>
<li><strong>回報結果</strong>，使用標準回報格式</li>
<li><strong>配合檢查</strong>，與重構代理人協作完成驗證</li>
</ol>
<h3 id="緊急情況處理">緊急情況處理</h3>
<h4 id="代理人無法執行任務">代理人無法執行任務</h4>
<ol>
<li>立即向主線程回報</li>
<li>主線程評估替代方案</li>
<li>必要時調整任務或更換代理人</li>
</ol>
<h4 id="技術方案遇到阻礙">技術方案遇到阻礙</h4>
<ol>
<li>子代理人立即停止執行</li>
<li>向主線程詳細回報問題</li>
<li>主線程呼叫 PM 代理人重新設計</li>
</ol>
<h4 id="品質標準無法達成">品質標準無法達成</h4>
<ol>
<li>重構代理人拒絕通過</li>
<li>明確指出品質問題</li>
<li>要求修正或重新設計</li>
</ol>
<hr>
<h2 id="v0110-實戰案例集">v0.11.0 實戰案例集</h2>
<h3 id="案例總覽">案例總覽</h3>
<p>v0.11.0 統一書籍資訊查詢服務開發過程，完整展現了敏捷重構方法論的實際應用，包含任務拆分、動態調整、代理人協作等核心流程。</p>
<h3 id="案例-1phase-2-發現觸發-plan-b-調整">案例 1：Phase 2 發現觸發 Plan B 調整</h3>
<h4 id="背景">背景</h4>
<p><strong>初始規劃</strong>：</p>
<ul>
<li>預估時程：4-6 天</li>
<li>假設：60% 程式碼已完成，補充測試即可</li>
<li>任務數：10-12 個小版本</li>
</ul>
<h4 id="觸發事件">觸發事件</h4>
<p>sage-test-architect 完成 Phase 2 測試設計後回報：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 重大發現 - Phase 2 測試設計
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 實作狀態檢查結果
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span><span class="k">-</span> 已完成：GoogleBooksApiImplementation 已完整實作
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">-</span> 已完成：GoogleBooksDto 和轉換已實作
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">-</span> 缺失：Domain 服務層完全缺失（0% 完成度）
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">-</span> 待辦：需要 28 個 TDD 新建測試
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">-</span> 注意：BookEnrichmentData 位置錯誤
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu">### 影響評估
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu"></span><span class="k">-</span> 預估工作量：原估 2-3 天 → 實際需 4-5 天
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">-</span> 複雜度：低 → 高（需從零建立 Domain 層）
</span></span><span class="line"><span class="ln">13</span><span class="cl">- 風險：中 → 高（架構債務累積風險）</span></span></code></pre></div><h4 id="主線程決策過程">主線程決策過程</h4>
<ol>
<li>
<p><strong>檢視相關文件</strong>：</p>
<ul>
<li>閱讀 Clean Architecture 規範</li>
<li>確認 Domain 層職責定義</li>
<li>評估架構債務風險</li>
</ul>
</li>
<li>
<p><strong>召開決策會議</strong>（主線程 + PM代理人）：</p>
<ul>
<li>分析時程影響</li>
<li>評估功能範圍調整可能性</li>
<li>制定 Plan A、Plan B 方案</li>
</ul>
</li>
<li>
<p><strong>決策記錄</strong>：</p>
</li>
</ol>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 決策 - v0.11.0 採用 Plan B
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gs">**決策日期**</span>: 2025-09-30
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gs">**決策者**</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="gu">### 方案比較
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span>**Plan A**: 維持原時程，降低測試覆蓋率
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">-</span> 優點：時程不變 (4-6 天)
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">-</span> 缺點：測試覆蓋率 <span class="p">&lt;</span> <span class="nt">80</span><span class="err">%</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">-</span> <span class="na">缺點</span><span class="err">：</span><span class="na">架構債務累積</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">-</span> <span class="na">缺點</span><span class="err">：</span><span class="na">違反品質標準</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gs">**Plan B**</span><span class="na">:</span> <span class="na">延長時程</span><span class="err">，</span><span class="na">確保品質</span><span class="err">（</span><span class="na">採用</span><span class="err">）</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">-</span> <span class="na">優點</span><span class="err">：</span><span class="na">測試覆蓋率</span> <span class="na">100</span><span class="err">%</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">-</span> <span class="na">優點</span><span class="err">：</span><span class="na">Domain</span> <span class="na">層基礎扎實</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">-</span> <span class="na">優點</span><span class="err">：</span><span class="na">架構符合規範</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">-</span> <span class="na">注意</span><span class="err">：</span><span class="na">時程延長</span> <span class="err">+</span><span class="na">1-2</span> <span class="na">天</span> <span class="err">(→</span> <span class="na">6-7</span> <span class="na">天</span><span class="err">)</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="gu">### 決定
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="gu"></span><span class="na">採用</span> <span class="na">Plan</span> <span class="na">B</span><span class="err">，</span><span class="na">理由</span><span class="err">：</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">1.</span> <span class="na">品質優於速度</span><span class="err">（</span><span class="na">專案鐵律</span><span class="err">）</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="k">2.</span> <span class="na">Domain</span> <span class="na">層是核心基礎</span><span class="err">，</span><span class="na">不能妥協</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="k">3.</span> <span class="na">延長</span> <span class="na">1-2</span> <span class="na">天可接受</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="k">4.</span> <span class="na">簡化外部</span> <span class="na">API</span> <span class="na">整合補償時程</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="gu">### 執行計畫
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="gu"></span><span class="k">1.</span> <span class="na">新增</span> <span class="na">v0</span><span class="err">.</span><span class="na">11</span><span class="err">.</span><span class="na">3-4</span><span class="err">：</span><span class="na">Domain</span> <span class="na">輸入驗證和查詢服務</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="k">2.</span> <span class="na">延後外部</span> <span class="na">API</span> <span class="na">整合</span><span class="err">（</span><span class="na">Amazon</span><span class="err">、</span><span class="na">台灣出版品</span><span class="err">）</span><span class="na">到</span> <span class="na">v0</span><span class="err">.</span><span class="na">12</span><span class="err">.</span><span class="na">0</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="k">3.</span> <span class="na">簡化速率控制</span><span class="err">，</span><span class="na">移除批次處理和斷路器</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="na">4</span><span class="err">.</span> <span class="na">更新所有後續任務參考文件</span></span></span></code></pre></div><h4 id="執行結果">執行結果</h4>
<ul>
<li>v0.11.3-4 成功完成（71 個測試 100% 通過）</li>
<li>優點：Domain 層基礎扎實</li>
<li>時程控制良好（實際 +1 天）</li>
<li>品質標準維持 100%</li>
</ul>
<h4 id="學習要點">學習要點</h4>
<ol>
<li><strong>測試設計階段至關重要</strong>：能提前識別實作狀態問題</li>
<li><strong>品質不妥協</strong>：短期時程延長換長期穩定發展</li>
<li><strong>靈活調整功能範圍</strong>：延後次要功能，專注核心價值</li>
<li><strong>決策必須文件化</strong>：清楚記錄原因和權衡</li>
</ol>
<h3 id="案例-2架構衝突檢測與立即修正">案例 2：架構衝突檢測與立即修正</h3>
<h4 id="背景-1">背景</h4>
<p>Phase 2 測試設計過程中，sage-test-architect 發現 <code>BookEnrichmentData</code> 位於錯誤層級。</p>
<h4 id="問題描述">問題描述</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 架構衝突 - BookEnrichmentData 位置錯誤
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 當前狀態
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span><span class="k">-</span> 位置: <span class="sb">`lib/infrastructure/api/google_books/book_enrichment_data.dart`</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">-</span> 職責: 純資料結構，封裝書籍豐富化資料
</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 class="gu">### 問題分析
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="gu"></span><span class="k">-</span> BookEnrichmentData 無外部依賴，應屬於 Domain 層
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">-</span> Infrastructure 層不應定義 Domain 實體
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">-</span> 違反 Clean Architecture 依賴方向
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="gu">### 影響範圍
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu"></span><span class="k">-</span> v0.11.5 (資料處理) 需要引用
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">-</span> v0.11.6 (API 客戶端) 需要轉換
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">-</span> 所有測試需要更新 import 路徑
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="gu">### 建議
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="gu"></span>立即遷移到 Domain 層，阻塞性問題。</span></span></code></pre></div><h4 id="主線程處理">主線程處理</h4>
<ol>
<li><strong>確認問題嚴重性</strong>：阻塞（影響後續所有任務）</li>
<li><strong>立即停止 Phase 3 啟動</strong>：等待架構修正完成</li>
<li><strong>指派緊急任務</strong>：</li>
</ol>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 緊急任務 - v0.11.2-fix BookEnrichmentData 遷移
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gs">**優先級**</span>: 最高
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gs">**執行人**</span>: mint-format-specialist
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="gs">**預估時間**</span>: 0.5 小時
</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 class="gu">### 執行步驟
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 遷移檔案到 <span class="sb">`lib/domains/book_info/entities/`</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">2.</span> 更新所有 import 引用
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">3.</span> 執行測試確認無錯誤
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">4.</span> 提交變更
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="gu">### 完成後動作
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 更新流程圖層級標註
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">2.</span> 更新 v0.11.5-6 任務參考
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">3.</span> 通知所有相關代理人
</span></span><span class="line"><span class="ln">17</span><span class="cl">4. 繼續 v0.11.3 執行</span></span></code></pre></div><ol start="4">
<li><strong>執行和追蹤</strong>：
<ul>
<li>0.4 小時完成遷移</li>
<li>Git commit: 58b1603</li>
<li>更新相關文件</li>
<li>解除 Phase 3 阻塞</li>
</ul>
</li>
</ol>
<h4 id="執行結果-1">執行結果</h4>
<ul>
<li>架構問題立即解決</li>
<li>延遲影響最小化（&lt; 1 小時）</li>
<li>後續任務基於正確架構</li>
<li>無技術債務累積</li>
</ul>
<h4 id="學習要點-1">學習要點</h4>
<ol>
<li><strong>架構問題零容忍</strong>：立即停止並修正</li>
<li><strong>緊急任務機制</strong>：快速插入高優先級任務</li>
<li><strong>影響範圍評估</strong>：精確識別受影響任務並更新</li>
<li><strong>追蹤閉環</strong>：確認完成並解除阻塞</li>
</ol>
<h3 id="案例-3命名不一致的系統化修正">案例 3：命名不一致的系統化修正</h3>
<h4 id="背景-2">背景</h4>
<p>Phase 2 測試設計發現 4 個流程圖元件命名與程式碼不符。</p>
<h4 id="問題清單">問題清單</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 命名不一致清單
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="k">1.</span> 流程圖: QueryValidator
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">   程式碼: BookInputValidator
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">   影響: Event 1-2
</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 class="k">2.</span> 流程圖: BookInfoService
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">   程式碼: UnifiedBookInfoService
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">   影響: Event 3-5
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">3.</span> 流程圖: DataProcessor
</span></span><span class="line"><span class="ln">12</span><span class="cl">   程式碼: BookEnrichmentProcessor
</span></span><span class="line"><span class="ln">13</span><span class="cl">   影響: Event 6-7
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">4.</span> 流程圖: CacheService
</span></span><span class="line"><span class="ln">16</span><span class="cl">   程式碼: ApiCacheManager
</span></span><span class="line"><span class="ln">17</span><span class="cl">   影響: Event 8-9</span></span></code></pre></div><h4 id="決策過程">決策過程</h4>
<p><strong>選項 A</strong>：更新流程圖命名</p>
<ul>
<li>流程圖是設計文件，應該貼近業務語意</li>
<li>程式碼已實作，更新成本高</li>
<li>捨棄：流程圖命名過於簡化</li>
</ul>
<p><strong>選項 B</strong>：更新程式碼命名</p>
<ul>
<li>程式碼命名更具體清楚</li>
<li>符合 Domain 層命名原則</li>
<li>採用：流程圖更新成本低</li>
</ul>
<h4 id="執行計畫">執行計畫</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 命名統一任務
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gs">**執行**</span>: memory-network-builder
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gs">**時程**</span>: 0.5 小時
</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="gu">### 更新範圍
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 更新 3 個流程圖文件
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">2.</span> 更新主版本工作日誌
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">3.</span> 更新所有任務參考
</span></span><span class="line"><span class="ln">10</span><span class="cl">4. Git commit 記錄變更</span></span></code></pre></div><h4 id="執行結果-2">執行結果</h4>
<ul>
<li>流程圖命名統一</li>
<li>Git commit: 79992cb</li>
<li>所有任務參考同步更新</li>
<li>代理人無歧義參考</li>
</ul>
<h4 id="學習要點-2">學習要點</h4>
<ol>
<li><strong>命名一致性至關重要</strong>：避免代理人困惑</li>
<li><strong>及早發現及早處理</strong>：Phase 2 發現是最佳時機</li>
<li><strong>全專案同步更新</strong>：確保所有引用一致</li>
<li><strong>決策權衡明確</strong>：記錄為什麼選擇特定方案</li>
</ol>
<h3 id="案例-4工作日誌架構重組">案例 4：工作日誌架構重組</h3>
<h4 id="背景-3">背景</h4>
<p>v0.11.0 初始建立了 6 個工作日誌文件，違反「一個主版本一個日誌」原則。</p>
<h4 id="問題描述-1">問題描述</h4>





<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><span class="line"><span class="ln">2</span><span class="cl">- v0.11.0-unified-api-implementation.md (主日誌)
</span></span><span class="line"><span class="ln">3</span><span class="cl">- v0.11.0-unified-api-flow-design.md (Phase 1)
</span></span><span class="line"><span class="ln">4</span><span class="cl">- v0.11.0-implementation-status-report.md (Phase 2.1)
</span></span><span class="line"><span class="ln">5</span><span class="cl">- v0.11.0-test-design-main-flow.md (Phase 2.2)
</span></span><span class="line"><span class="ln">6</span><span class="cl">- v0.11.0-test-design-api-fallback.md (Phase 2.3)
</span></span><span class="line"><span class="ln">7</span><span class="cl">- v0.11.0-test-design-rate-limiting.md (Phase 2.4)
</span></span><span class="line"><span class="ln">8</span><span class="cl">
</span></span><span class="line"><span class="ln">9</span><span class="cl">問題: 6 個檔案都以 v0.11.0 開頭，不符合方法論規範</span></span></code></pre></div><h4 id="重組策略">重組策略</h4>
<p>參考 v0.9.0 成功模式，制定重組計畫：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="gu">## 重組計畫
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="gu"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="gu">### 新架構
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="gu"></span><span class="k">-</span> v0.11.0-main.md (主版本總覽)
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">-</span> v0.11.1-tdd-phase1-design.md (Phase 1)
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">-</span> v0.11.2-tdd-phase2-test-design.md (Phase 2 合併)
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">-</span> v0.11.3-domain-input-validation.md (批次 1.1)
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">-</span> v0.11.4-core-query-service.md (批次 1.2)
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="gu">### 執行步驟
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="gu"></span><span class="k">1.</span> 重命名主日誌
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="k">2.</span> Phase 1 重命名為 v0.11.1
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="k">3.</span> Phase 2 四個文件合併為 v0.11.2
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="k">4.</span> 批次實作提取為 v0.11.3-4
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="k">5.</span> 清理舊檔案
</span></span><span class="line"><span class="ln">16</span><span class="cl">6. Git 提交重組結果</span></span></code></pre></div><h4 id="執行結果-3">執行結果</h4>
<ul>
<li>10 個檔案變更（929 行新增，5910 行刪除）</li>
<li>Git commit: d86940a</li>
<li>符合方法論規範</li>
<li>清晰的版本架構</li>
</ul>
<h4 id="學習要點-3">學習要點</h4>
<ol>
<li><strong>方法論合規性檢查</strong>：定期審查是否符合標準</li>
<li><strong>參考成功案例</strong>：v0.9.0 模式可複用</li>
<li><strong>大膽重組</strong>：發現問題立即修正，不將就</li>
<li><strong>文件瘦身</strong>：主版本日誌簡潔，詳細內容在子版本</li>
</ol>
<h3 id="方法論改進建議基於實戰">方法論改進建議（基於實戰）</h3>
<h4 id="1-強化-phase-2-重要性">1. 強化 Phase 2 重要性</h4>
<p><strong>發現</strong>：Phase 2 測試設計階段能提前識別 80% 的實作問題
<strong>建議</strong>：增加 Phase 2 的檢查清單和回報機制</p>
<h4 id="2-任務參考文件標準化已強制執行">2. 任務參考文件標準化（已強制執行）</h4>
<p><strong>發現</strong>：初始任務描述參考文件不足，導致代理人需要詢問
<strong>原建議</strong>：強制要求 UseCase、流程圖 Event、依賴類別完整填寫
<strong>已實施</strong>：</p>
<ul>
<li>更新「準備度檢查問題」加入第 5 項強制檢查</li>
<li>更新「任務執行前檢查」加入參考文件和影響範圍強制項目</li>
<li>違規處理機制：缺少任何一項視為任務規劃不合格</li>
<li>實戰驗證：v0.12.1 已補充完整參考文件和影響範圍章節</li>
</ul>
<h4 id="3-動態更新機制制度化">3. 動態更新機制制度化</h4>
<p><strong>發現</strong>：Phase 發現需要立即傳遞給後續任務
<strong>建議</strong>：建立「重要發現」區域，強制每個 Phase 完成後更新</p>
<h4 id="4-緊急任務插入流程">4. 緊急任務插入流程</h4>
<p><strong>發現</strong>：架構問題需要立即處理
<strong>建議</strong>：定義緊急任務命名規則（vX.Y.Z-fix）和執行流程</p>
<h4 id="5-工作日誌結構模板">5. 工作日誌結構模板</h4>
<p><strong>發現</strong>：初期結構混亂，後期重組成本高
<strong>建議</strong>：提供標準模板，從一開始就符合規範</p>
<h2 id="方法論版本歷史">方法論版本歷史</h2>
<h3 id="v13-2025-10-07">v1.3 (2025-10-07)</h3>
<ul>
<li><strong>新增</strong>: 三重文件協調原則</li>
</ul>
<h3 id="v12-2025-09-30">v1.2 (2025-09-30)</h3>
<ul>
<li><strong>新增</strong>: 任務拆分實戰範例（基於 v0.11.5 情境）</li>
<li><strong>新增</strong>: 動態文件更新機制（Phase 發現傳遞流程）</li>
<li><strong>新增</strong>: 代理人回報與討論流程（4 種回報類型）</li>
<li><strong>新增</strong>: 敏捷開發本質說明（資訊流動、靈活交接、響應式規劃）</li>
<li><strong>新增</strong>: v0.11.0 實戰案例集（4 個完整案例）</li>
<li><strong>更新</strong>: 任務參考文件格式（強制完整填寫要求）</li>
<li><strong>更新</strong>: 基於 v0.11 實戰經驗完善所有流程</li>
</ul>
<h3 id="v11-2025-09-29">v1.1 (2025-09-29)</h3>
<ul>
<li><strong>新增</strong>: 階段完成驗證機制 (基於實戰經驗學習)</li>
<li><strong>新增</strong>: 5 項強制檢查項目 (編譯、測試、路徑、重複、架構)</li>
<li><strong>新增</strong>: 階段失敗處理原則</li>
<li><strong>新增</strong>: Hook 系統整合指引</li>
<li><strong>更新</strong>: 成功標準，加入階段驗證指標</li>
<li><strong>修正</strong>: 移除版本特定內容，改為通用流程</li>
</ul>
<h3 id="v10-2025-09-27">v1.0 (2025-09-27)</h3>
<ul>
<li><strong>建立</strong>: 基礎敏捷重構方法論</li>
<li><strong>定義</strong>: Agent 分工協作模式</li>
<li><strong>建立</strong>: 任務分派和品質控制機制</li>
</ul>
<hr>
<p><strong>當前版本</strong>: v1.2
<strong>適用範圍</strong>: 通用敏捷重構開發流程
<strong>最後更新</strong>: 2025-09-30
<strong>責任人</strong>: Claude Code AI Assistant
<strong>重大更新</strong>: 基於 v0.11.0 實戰經驗，新增任務拆分範例、動態更新機制、代理人回報流程、敏捷開發本質說明</p>
]]></content:encoded></item><item><title>v0.9.0 錯誤處理系統重構工作日誌</title><link>https://tarrragon.github.io/blog/record/v0.9.0-%E9%8C%AF%E8%AA%A4%E8%99%95%E7%90%86%E7%B3%BB%E7%B5%B1%E9%87%8D%E6%A7%8B%E5%B7%A5%E4%BD%9C%E6%97%A5%E8%AA%8C/</link><pubDate>Sat, 27 Sep 2025 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/v0.9.0-%E9%8C%AF%E8%AA%A4%E8%99%95%E7%90%86%E7%B3%BB%E7%B5%B1%E9%87%8D%E6%A7%8B%E5%B7%A5%E4%BD%9C%E6%97%A5%E8%AA%8C/</guid><description>&lt;h2 id="v09x-版本敏捷重構執行模式">v0.9.x 版本敏捷重構執行模式&lt;/h2>
&lt;p>&lt;strong>重要&lt;/strong>: 整個 v0.9.x 版本系列遵循敏捷重構方法論，詳見：&lt;a href="https://tarrragon.github.io/blog/record/%E6%95%8F%E6%8D%B7%E9%87%8D%E6%A7%8B%E6%96%B9%E6%B3%95%E8%AB%96-agent-%E5%88%86%E5%B7%A5%E5%8D%94%E4%BD%9C%E6%A8%A1%E5%BC%8F/" data-link-title="敏捷重構方法論 - Agent 分工協作模式" data-link-desc="描述如何讓AI使用敏捷開發的流程方法論，以主線程統籌、子代理人專責執行的協作模式，確保重構品質和進度控制">敏捷重構方法論&lt;/a>&lt;/p>
&lt;h3 id="執行原則">執行原則&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>主線程只負責任務分派&lt;/strong>，絕不親自執行程式碼修改&lt;/li>
&lt;li>&lt;strong>每個任務交給子代理人處理&lt;/strong>，完成後由重構代理人檢查&lt;/li>
&lt;li>&lt;strong>任務規模過大時向上回報&lt;/strong>，PM 代理人負責二次拆分&lt;/li>
&lt;li>&lt;strong>完成後由文件代理人更新工作日誌&lt;/strong>，標記小版本完成&lt;/li>
&lt;/ul>
&lt;h3 id="agent-分工">Agent 分工&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>執行&lt;/strong>: lavender, sage, pepper, cinnamon, mint 等專業代理人&lt;/li>
&lt;li>&lt;strong>檢查&lt;/strong>: cinnamon-refactor-owl (重構代理人)&lt;/li>
&lt;li>&lt;strong>拆分&lt;/strong>: rosemary-project-manager (PM 代理人)&lt;/li>
&lt;li>&lt;strong>文件&lt;/strong>: memory-network-builder (文件代理人)&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="版本資訊">版本資訊&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>版本號&lt;/strong>: v0.9.0+1&lt;/li>
&lt;li>&lt;strong>開始時間&lt;/strong>: 2025-09-27&lt;/li>
&lt;li>&lt;strong>工作類型&lt;/strong>: 重大架構重構&lt;/li>
&lt;li>&lt;strong>預估週期&lt;/strong>: 完整中版本週期&lt;/li>
&lt;li>&lt;strong>優先級&lt;/strong>: 關鍵架構債務&lt;/li>
&lt;/ul>
&lt;h2 id="重構目標">重構目標&lt;/h2>
&lt;h3 id="核心目標">核心目標&lt;/h3>
&lt;p>基於設計文件 v2.0 規範，將過度設計的錯誤處理系統重構為原生 Exception + ErrorCodes 簡化架構，提升效能並降低維護成本。&lt;/p>
&lt;h3 id="具體成果指標">具體成果指標&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>效能提升&lt;/strong>: 錯誤建立時間從 ~1ms 降至 &amp;lt; 0.1ms&lt;/li>
&lt;li>&lt;strong>記憶體優化&lt;/strong>: 錯誤物件記憶體占用從 ~1KB 降至 &amp;lt; 200 bytes&lt;/li>
&lt;li>&lt;strong>程式碼簡化&lt;/strong>: 移除 ~80% 的錯誤處理程式碼&lt;/li>
&lt;li>&lt;strong>維護性&lt;/strong>: 零學習成本，使用標準 Dart Exception&lt;/li>
&lt;/ul>
&lt;h2 id="現況分析與問題評估">現況分析與問題評估&lt;/h2>
&lt;h3 id="發現的架構問題">發現的架構問題&lt;/h3>
&lt;h4 id="1-雙重錯誤系統並存">1. 雙重錯誤系統並存&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">lib/core/error_handling/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">├── app_error.dart # AppError 抽象基類定義
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">└── unified_error_handler.dart # 統一錯誤處理器
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">lib/core/errors/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">├── app_error.dart # AppError 具體實作
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">├── standard_error.dart # StandardError 包裝器
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">├── error_adapters.dart # 錯誤轉換器
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">9&lt;/span>&lt;span class="cl">└── error_codes.dart # 錯誤代碼&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>問題分析&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>兩個目錄都包含 &lt;code>app_error.dart&lt;/code>，責任不明確&lt;/li>
&lt;li>StandardError 作為 BusinessLogicError 的包裝器，增加不必要複雜度&lt;/li>
&lt;li>導入路徑混亂，&lt;code>error_handling/app_error.dart&lt;/code> 和 &lt;code>errors/app_error.dart&lt;/code> 互相依賴&lt;/li>
&lt;/ul>
&lt;h4 id="2-過度設計問題">2. 過度設計問題&lt;/h4>
&lt;p>&lt;strong>基於實際程式碼分析&lt;/strong>:&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-dart" data-lang="dart">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1">// StandardError.dart - 過度包裝
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">class&lt;/span> &lt;span class="nc">StandardError&lt;/span> &lt;span class="kd">extends&lt;/span> &lt;span class="n">BusinessLogicError&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl"> &lt;span class="n">StandardError&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">String&lt;/span> &lt;span class="n">code&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">String&lt;/span> &lt;span class="n">message&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">Map&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">String&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">dynamic&lt;/span>&lt;span class="o">&amp;gt;?&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="p">,])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl"> &lt;span class="o">:&lt;/span> &lt;span class="k">super&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl"> &lt;span class="nl">code:&lt;/span> &lt;span class="n">code&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl"> &lt;span class="nl">message:&lt;/span> &lt;span class="n">message&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> &lt;span class="nl">businessRule:&lt;/span> &lt;span class="s1">&amp;#39;LEGACY_STANDARD_ERROR&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// 固定值，無實際意義
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nl">context:&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>發現的過度設計&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>複雜的錯誤分類系統（NetworkError, ValidationError, BusinessLogicError 等）&lt;/li>
&lt;li>StandardError 只是 BusinessLogicError 的薄包裝&lt;/li>
&lt;li>深度複製和循環參照處理增加不必要開銷&lt;/li>
&lt;li>ErrorAdapters 提供多餘的轉換功能&lt;/li>
&lt;/ul>
&lt;h4 id="3-與設計文件-v20-不一致">3. 與設計文件 v2.0 不一致&lt;/h4>
&lt;p>&lt;strong>設計文件 v2.0 規範&lt;/strong>:&lt;/p>
&lt;blockquote>
&lt;p>注意：v2.0 重大架構更新: 基於 Linux/John Carmack 專家建議，完全放棄 StandardError 複雜設計，回歸原生錯誤處理 + ErrorCodes 簡化模式&lt;/p>&lt;/blockquote>
&lt;p>&lt;strong>實際實作狀況&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>仍使用複雜的 AppError 分層系統&lt;/li>
&lt;li>StandardError 包裝器違反簡化原則&lt;/li>
&lt;li>UnifiedErrorHandler 功能過於複雜&lt;/li>
&lt;/ul>
&lt;h2 id="新架構設計基於設計文件-v20">新架構設計（基於設計文件 v2.0）&lt;/h2>
&lt;h3 id="核心設計原則">核心設計原則&lt;/h3>
&lt;h4 id="專家驅動的簡化設計">專家驅動的簡化設計&lt;/h4>
&lt;p>&lt;strong>過度工程化問題 (當前 v0.8.x)&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>複雜的錯誤分類系統解決不存在的問題&lt;/li>
&lt;li>深度複製和循環參照處理增加不必要開銷&lt;/li>
&lt;li>ID 生成和時間戳記錄對實際業務無價值&lt;/li>
&lt;li>學習成本高，維護困難&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>簡化設計優勢 (目標 v0.9.x)&lt;/strong>:&lt;/p></description><content:encoded><![CDATA[<h2 id="v09x-版本敏捷重構執行模式">v0.9.x 版本敏捷重構執行模式</h2>
<p><strong>重要</strong>: 整個 v0.9.x 版本系列遵循敏捷重構方法論，詳見：<a href="/blog/record/%E6%95%8F%E6%8D%B7%E9%87%8D%E6%A7%8B%E6%96%B9%E6%B3%95%E8%AB%96-agent-%E5%88%86%E5%B7%A5%E5%8D%94%E4%BD%9C%E6%A8%A1%E5%BC%8F/" data-link-title="敏捷重構方法論 - Agent 分工協作模式" data-link-desc="描述如何讓AI使用敏捷開發的流程方法論，以主線程統籌、子代理人專責執行的協作模式，確保重構品質和進度控制">敏捷重構方法論</a></p>
<h3 id="執行原則">執行原則</h3>
<ul>
<li><strong>主線程只負責任務分派</strong>，絕不親自執行程式碼修改</li>
<li><strong>每個任務交給子代理人處理</strong>，完成後由重構代理人檢查</li>
<li><strong>任務規模過大時向上回報</strong>，PM 代理人負責二次拆分</li>
<li><strong>完成後由文件代理人更新工作日誌</strong>，標記小版本完成</li>
</ul>
<h3 id="agent-分工">Agent 分工</h3>
<ul>
<li><strong>執行</strong>: lavender, sage, pepper, cinnamon, mint 等專業代理人</li>
<li><strong>檢查</strong>: cinnamon-refactor-owl (重構代理人)</li>
<li><strong>拆分</strong>: rosemary-project-manager (PM 代理人)</li>
<li><strong>文件</strong>: memory-network-builder (文件代理人)</li>
</ul>
<hr>
<h2 id="版本資訊">版本資訊</h2>
<ul>
<li><strong>版本號</strong>: v0.9.0+1</li>
<li><strong>開始時間</strong>: 2025-09-27</li>
<li><strong>工作類型</strong>: 重大架構重構</li>
<li><strong>預估週期</strong>: 完整中版本週期</li>
<li><strong>優先級</strong>: 關鍵架構債務</li>
</ul>
<h2 id="重構目標">重構目標</h2>
<h3 id="核心目標">核心目標</h3>
<p>基於設計文件 v2.0 規範，將過度設計的錯誤處理系統重構為原生 Exception + ErrorCodes 簡化架構，提升效能並降低維護成本。</p>
<h3 id="具體成果指標">具體成果指標</h3>
<ul>
<li><strong>效能提升</strong>: 錯誤建立時間從 ~1ms 降至 &lt; 0.1ms</li>
<li><strong>記憶體優化</strong>: 錯誤物件記憶體占用從 ~1KB 降至 &lt; 200 bytes</li>
<li><strong>程式碼簡化</strong>: 移除 ~80% 的錯誤處理程式碼</li>
<li><strong>維護性</strong>: 零學習成本，使用標準 Dart Exception</li>
</ul>
<h2 id="現況分析與問題評估">現況分析與問題評估</h2>
<h3 id="發現的架構問題">發現的架構問題</h3>
<h4 id="1-雙重錯誤系統並存">1. 雙重錯誤系統並存</h4>





<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">lib/core/error_handling/
</span></span><span class="line"><span class="ln">2</span><span class="cl">├── app_error.dart          # AppError 抽象基類定義
</span></span><span class="line"><span class="ln">3</span><span class="cl">└── unified_error_handler.dart  # 統一錯誤處理器
</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">lib/core/errors/
</span></span><span class="line"><span class="ln">6</span><span class="cl">├── app_error.dart          # AppError 具體實作
</span></span><span class="line"><span class="ln">7</span><span class="cl">├── standard_error.dart     # StandardError 包裝器
</span></span><span class="line"><span class="ln">8</span><span class="cl">├── error_adapters.dart     # 錯誤轉換器
</span></span><span class="line"><span class="ln">9</span><span class="cl">└── error_codes.dart        # 錯誤代碼</span></span></code></pre></div><p><strong>問題分析</strong>:</p>
<ul>
<li>兩個目錄都包含 <code>app_error.dart</code>，責任不明確</li>
<li>StandardError 作為 BusinessLogicError 的包裝器，增加不必要複雜度</li>
<li>導入路徑混亂，<code>error_handling/app_error.dart</code> 和 <code>errors/app_error.dart</code> 互相依賴</li>
</ul>
<h4 id="2-過度設計問題">2. 過度設計問題</h4>
<p><strong>基於實際程式碼分析</strong>:</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">// StandardError.dart - 過度包裝
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">StandardError</span> <span class="kd">extends</span> <span class="n">BusinessLogicError</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="n">StandardError</span><span class="p">(</span><span class="kt">String</span> <span class="n">code</span><span class="p">,</span> <span class="kt">String</span> <span class="n">message</span><span class="p">,</span> <span class="p">[</span><span class="n">Map</span><span class="o">&lt;</span><span class="kt">String</span><span class="p">,</span> <span class="kt">dynamic</span><span class="o">&gt;?</span> <span class="n">context</span><span class="p">,])</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="o">:</span> <span class="k">super</span><span class="p">(</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">        <span class="nl">code:</span> <span class="n">code</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">        <span class="nl">message:</span> <span class="n">message</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">        <span class="nl">businessRule:</span> <span class="s1">&#39;LEGACY_STANDARD_ERROR&#39;</span><span class="p">,</span>  <span class="c1">// 固定值，無實際意義
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span>        <span class="nl">context:</span> <span class="n">context</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">      <span class="p">);</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p><strong>發現的過度設計</strong>:</p>
<ul>
<li>複雜的錯誤分類系統（NetworkError, ValidationError, BusinessLogicError 等）</li>
<li>StandardError 只是 BusinessLogicError 的薄包裝</li>
<li>深度複製和循環參照處理增加不必要開銷</li>
<li>ErrorAdapters 提供多餘的轉換功能</li>
</ul>
<h4 id="3-與設計文件-v20-不一致">3. 與設計文件 v2.0 不一致</h4>
<p><strong>設計文件 v2.0 規範</strong>:</p>
<blockquote>
<p>注意：v2.0 重大架構更新: 基於 Linux/John Carmack 專家建議，完全放棄 StandardError 複雜設計，回歸原生錯誤處理 + ErrorCodes 簡化模式</p></blockquote>
<p><strong>實際實作狀況</strong>:</p>
<ul>
<li>仍使用複雜的 AppError 分層系統</li>
<li>StandardError 包裝器違反簡化原則</li>
<li>UnifiedErrorHandler 功能過於複雜</li>
</ul>
<h2 id="新架構設計基於設計文件-v20">新架構設計（基於設計文件 v2.0）</h2>
<h3 id="核心設計原則">核心設計原則</h3>
<h4 id="專家驅動的簡化設計">專家驅動的簡化設計</h4>
<p><strong>過度工程化問題 (當前 v0.8.x)</strong>:</p>
<ul>
<li>複雜的錯誤分類系統解決不存在的問題</li>
<li>深度複製和循環參照處理增加不必要開銷</li>
<li>ID 生成和時間戳記錄對實際業務無價值</li>
<li>學習成本高，維護困難</li>
</ul>
<p><strong>簡化設計優勢 (目標 v0.9.x)</strong>:</p>
<ol>
<li><strong>回歸原生</strong>: 使用 Dart Exception，零學習成本</li>
<li><strong>高效能</strong>: 錯誤建立時間 &lt; 0.1ms，記憶體占用 &lt; 200 bytes</li>
<li><strong>實用主義</strong>: 只解決真正存在的問題</li>
<li><strong>零依賴</strong>: 不引入不必要的複雜度</li>
</ol>
<h3 id="技術架構設計">技術架構設計</h3>
<h4 id="1-統一錯誤代碼系統">1. 統一錯誤代碼系統</h4>





<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">// lib/core/errors/error_codes.dart
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="n">enum</span> <span class="n">ErrorCode</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="c1">// 驗證錯誤
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"></span>  <span class="n">validationFailed</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="n">invalidInput</span><span class="p">,</span>
</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 class="c1">// 網路錯誤
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span>  <span class="n">networkTimeout</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="n">noConnection</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">  <span class="c1">// 業務邏輯錯誤
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"></span>  <span class="n">bookNotFound</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">  <span class="n">duplicateBook</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">  <span class="c1">// 系統錯誤
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1"></span>  <span class="n">storageError</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">  <span class="n">permissionDenied</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h4 id="2-原生-exception-擴展">2. 原生 Exception 擴展</h4>





<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">// lib/core/errors/exceptions.dart
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1">// 業務邏輯異常
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">BookException</span> <span class="kd">implements</span> <span class="n">Exception</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="kd">final</span> <span class="n">ErrorCode</span> <span class="n">code</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">  <span class="kd">final</span> <span class="kt">String</span> <span class="n">message</span><span class="p">;</span>
</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">  <span class="kd">const</span> <span class="n">BookException</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="n">code</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="n">message</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="err">@</span><span class="n">override</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">  <span class="kt">String</span> <span class="n">toString</span><span class="p">()</span> <span class="o">=&gt;</span> <span class="s1">&#39;BookException: </span><span class="si">${</span><span class="n">code</span><span class="p">.</span><span class="n">name</span><span class="si">}</span><span class="s1"> - </span><span class="si">$</span><span class="n">message</span><span class="s1">&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="c1">// 驗證異常
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">ValidationException</span> <span class="kd">implements</span> <span class="n">Exception</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="kd">final</span> <span class="n">ErrorCode</span> <span class="n">code</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">  <span class="kd">final</span> <span class="kt">String</span> <span class="n">message</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">  <span class="kd">final</span> <span class="kt">String</span><span class="o">?</span> <span class="n">field</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl">  <span class="kd">const</span> <span class="n">ValidationException</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="n">code</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="n">message</span><span class="p">,</span> <span class="p">[</span><span class="k">this</span><span class="p">.</span><span class="n">field</span><span class="p">]);</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl">  <span class="err">@</span><span class="n">override</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">  <span class="kt">String</span> <span class="n">toString</span><span class="p">()</span> <span class="o">=&gt;</span> <span class="s1">&#39;ValidationException: </span><span class="si">${</span><span class="n">code</span><span class="p">.</span><span class="n">name</span><span class="si">}</span><span class="s1"> - </span><span class="si">$</span><span class="n">message</span><span class="si">${</span><span class="n">field</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">?</span> <span class="s1">&#39; (field: </span><span class="si">$</span><span class="n">field</span><span class="s1">)&#39;</span> <span class="o">:</span> <span class="s1">&#39;&#39;</span><span class="si">}</span><span class="s1">&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="c1">// 網路異常
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">NetworkException</span> <span class="kd">implements</span> <span class="n">Exception</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">  <span class="kd">final</span> <span class="n">ErrorCode</span> <span class="n">code</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">  <span class="kd">final</span> <span class="kt">String</span> <span class="n">message</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">
</span></span><span class="line"><span class="ln">31</span><span class="cl">  <span class="kd">const</span> <span class="n">NetworkException</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="n">code</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="n">message</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">
</span></span><span class="line"><span class="ln">33</span><span class="cl">  <span class="err">@</span><span class="n">override</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">  <span class="kt">String</span> <span class="n">toString</span><span class="p">()</span> <span class="o">=&gt;</span> <span class="s1">&#39;NetworkException: </span><span class="si">${</span><span class="n">code</span><span class="p">.</span><span class="n">name</span><span class="si">}</span><span class="s1"> - </span><span class="si">$</span><span class="n">message</span><span class="s1">&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h4 id="3-統一錯誤處理器">3. 統一錯誤處理器</h4>





<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">// lib/core/errors/error_handler.dart
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="kd">class</span> <span class="nc">ErrorHandler</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="kd">static</span> <span class="kt">void</span> <span class="n">handleError</span><span class="p">(</span><span class="n">Exception</span> <span class="n">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="k">switch</span> <span class="p">(</span><span class="n">error</span><span class="p">.</span><span class="n">runtimeType</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">      <span class="k">case</span> <span class="nl">BookException:</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">        <span class="n">_handleBookError</span><span class="p">(</span><span class="n">error</span> <span class="o">as</span> <span class="n">BookException</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">      <span class="k">case</span> <span class="nl">ValidationException:</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">        <span class="n">_handleValidationError</span><span class="p">(</span><span class="n">error</span> <span class="o">as</span> <span class="n">ValidationException</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">      <span class="k">case</span> <span class="nl">NetworkException:</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">        <span class="n">_handleNetworkError</span><span class="p">(</span><span class="n">error</span> <span class="o">as</span> <span class="n">NetworkException</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">        <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">      <span class="k">default</span><span class="o">:</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">        <span class="n">_handleGenericError</span><span class="p">(</span><span class="n">error</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl">  <span class="kd">static</span> <span class="kt">void</span> <span class="n">_handleBookError</span><span class="p">(</span><span class="n">BookException</span> <span class="n">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="c1">// 簡單的書籍相關錯誤處理
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="c1"></span>    <span class="n">print</span><span class="p">(</span><span class="s1">&#39;書籍錯誤: </span><span class="si">${</span><span class="n">error</span><span class="p">.</span><span class="n">message</span><span class="si">}</span><span class="s1">&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl">  <span class="kd">static</span> <span class="kt">void</span> <span class="n">_handleValidationError</span><span class="p">(</span><span class="n">ValidationException</span> <span class="n">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">    <span class="c1">// 簡單的驗證錯誤處理
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="c1"></span>    <span class="n">print</span><span class="p">(</span><span class="s1">&#39;驗證錯誤: </span><span class="si">${</span><span class="n">error</span><span class="p">.</span><span class="n">message</span><span class="si">}</span><span class="s1">&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">
</span></span><span class="line"><span class="ln">29</span><span class="cl">  <span class="kd">static</span> <span class="kt">void</span> <span class="n">_handleNetworkError</span><span class="p">(</span><span class="n">NetworkException</span> <span class="n">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">    <span class="c1">// 簡單的網路錯誤處理
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="c1"></span>    <span class="n">print</span><span class="p">(</span><span class="s1">&#39;網路錯誤: </span><span class="si">${</span><span class="n">error</span><span class="p">.</span><span class="n">message</span><span class="si">}</span><span class="s1">&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">
</span></span><span class="line"><span class="ln">34</span><span class="cl">  <span class="kd">static</span> <span class="kt">void</span> <span class="n">_handleGenericError</span><span class="p">(</span><span class="n">Exception</span> <span class="n">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">    <span class="c1">// 通用錯誤處理
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="c1"></span>    <span class="n">print</span><span class="p">(</span><span class="s1">&#39;未知錯誤: </span><span class="si">${</span><span class="n">error</span><span class="p">.</span><span class="n">toString</span><span class="p">()</span><span class="si">}</span><span class="s1">&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h4 id="4-統一導出入口">4. 統一導出入口</h4>





<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">// lib/core/errors/errors.dart
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span><span class="k">export</span> <span class="s1">&#39;error_codes.dart&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="k">export</span> <span class="s1">&#39;exceptions.dart&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="k">export</span> <span class="s1">&#39;error_handler.dart&#39;</span><span class="p">;</span></span></span></code></pre></div><h3 id="使用範例">使用範例</h3>
<h4 id="重構前複雜">重構前（複雜）</h4>





<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">import</span> <span class="s1">&#39;package:book_overview_app/core/errors/standard_error.dart&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="k">import</span> <span class="s1">&#39;package:book_overview_app/core/error_handling/unified_error_handler.dart&#39;</span><span class="p">;</span>
</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"><span class="c1">// 拋出錯誤
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span><span class="k">throw</span> <span class="n">StandardError</span><span class="p">.</span><span class="n">validation</span><span class="p">(</span><span class="s1">&#39;書籍標題不能為空&#39;</span><span class="p">);</span>
</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"><span class="c1">// 處理錯誤
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"></span><span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="c1">// 一些操作
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"></span><span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="n">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="n">UnifiedErrorHandler</span><span class="p">().</span><span class="n">handleBusinessError</span><span class="p">(</span><span class="n">e</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h4 id="重構後簡化">重構後（簡化）</h4>





<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">import</span> <span class="s1">&#39;package:book_overview_app/core/errors/errors.dart&#39;</span><span class="p">;</span>
</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 class="c1">// 拋出錯誤
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span><span class="k">throw</span> <span class="n">ValidationException</span><span class="p">(</span><span class="n">ErrorCode</span><span class="p">.</span><span class="n">invalidInput</span><span class="p">,</span> <span class="s1">&#39;書籍標題不能為空&#39;</span><span class="p">,</span> <span class="s1">&#39;title&#39;</span><span class="p">);</span>
</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 class="c1">// 處理錯誤
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span><span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="c1">// 一些操作
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"></span><span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="n">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">  <span class="n">ErrorHandler</span><span class="p">.</span><span class="n">handleError</span><span class="p">(</span><span class="n">e</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h2 id="重構執行計畫">重構執行計畫</h2>
<h3 id="階段1建立新架構估時1-2小時">階段1：建立新架構（估時：1-2小時）</h3>
<h4 id="任務清單">任務清單</h4>
<ul>
<li>
<p><input disabled="" type="checkbox"> <strong>建立 lib/core/errors/error_codes.dart</strong></p>
<ul>
<li>定義完整的 ErrorCode 枚舉</li>
<li>涵蓋所有現有錯誤場景</li>
</ul>
</li>
<li>
<p><input disabled="" type="checkbox"> <strong>建立 lib/core/errors/exceptions.dart</strong></p>
<ul>
<li>實作 BookException, ValidationException, NetworkException</li>
<li>確保原生 Exception 相容性</li>
</ul>
</li>
<li>
<p><input disabled="" type="checkbox"> <strong>建立 lib/core/errors/error_handler.dart</strong></p>
<ul>
<li>實作簡化的統一錯誤處理邏輯</li>
<li>提供基本的錯誤分類處理</li>
</ul>
</li>
<li>
<p><input disabled="" type="checkbox"> <strong>建立 lib/core/errors/errors.dart</strong></p>
<ul>
<li>統一導出所有錯誤相關類別</li>
<li>成為唯一的錯誤處理導入入口</li>
</ul>
</li>
</ul>
<h4 id="驗收標準">驗收標準</h4>
<ul>
<li>所有新檔案通過 <code>dart analyze</code> 檢查</li>
<li>基本功能測試通過</li>
<li>符合設計文件 v2.0 規範</li>
</ul>
<h3 id="階段2移除舊架構估時1小時">階段2：移除舊架構（估時：1小時）</h3>
<h4 id="任務清單-1">任務清單</h4>
<ul>
<li>
<p><input disabled="" type="checkbox"> <strong>刪除 lib/core/error_handling/ 整個目錄</strong></p>
<ul>
<li>移除 app_error.dart (抽象基類版本)</li>
<li>移除 unified_error_handler.dart</li>
</ul>
</li>
<li>
<p><input disabled="" type="checkbox"> <strong>清理 lib/core/errors/ 過時檔案</strong></p>
<ul>
<li>刪除 app_error.dart (具體實作版本)</li>
<li>刪除 standard_error.dart</li>
<li>刪除 error_adapters.dart</li>
<li>保留並整合 error_codes.dart 有用內容</li>
</ul>
</li>
</ul>
<h4 id="風險控制">風險控制</h4>
<ul>
<li>分檔案逐一移除，每次移除後執行測試</li>
<li>確保沒有隱藏的依賴關係</li>
<li>建立移除前的備份點</li>
</ul>
<h3 id="階段3全域重構估時2-3小時">階段3：全域重構（估時：2-3小時）</h3>
<h4 id="任務清單-2">任務清單</h4>
<ul>
<li>
<p><input disabled="" type="checkbox"> <strong>掃描並識別所有使用位置</strong></p>
<ul>
<li>使用 Serena MCP 工具找出所有 AppError/StandardError 引用</li>
<li>建立完整的重構對照表</li>
</ul>
</li>
<li>
<p><input disabled="" type="checkbox"> <strong>批次替換導入語句</strong></p>
<ul>
<li>統一使用 <code>import 'package:book_overview_app/core/errors/errors.dart';</code></li>
<li>移除所有舊的錯誤處理導入</li>
</ul>
</li>
<li>
<p><input disabled="" type="checkbox"> <strong>重構錯誤拋出邏輯</strong></p>
<ul>
<li>StandardError → 對應的具體 Exception</li>
<li>業務邏輯錯誤 → BookException</li>
<li>驗證錯誤 → ValidationException</li>
</ul>
</li>
<li>
<p><input disabled="" type="checkbox"> <strong>更新錯誤處理邏輯</strong></p>
<ul>
<li>try-catch 區塊使用新的 Exception 類型</li>
<li>錯誤處理使用 ErrorHandler.handleError()</li>
</ul>
</li>
</ul>
<h4 id="重構優先序">重構優先序</h4>
<ol>
<li><strong>高風險檔案</strong>（核心業務邏輯）</li>
<li><strong>中風險檔案</strong>（次要功能模組）</li>
<li><strong>低風險檔案</strong>（測試檔案、工具檔案）</li>
</ol>
<h3 id="階段4測試更新與驗證估時1-2小時">階段4：測試更新與驗證（估時：1-2小時）</h3>
<p>任務清單</p>
<ul>
<li>
<p><input disabled="" type="checkbox"> <strong>更新單元測試</strong></p>
<ul>
<li>使用新的 Exception 類型進行測試</li>
<li>確保測試覆蓋率不下降</li>
</ul>
</li>
<li>
<p><input disabled="" type="checkbox"> <strong>更新整合測試</strong></p>
<ul>
<li>端到端錯誤處理流程測試</li>
<li>錯誤場景模擬測試</li>
</ul>
</li>
<li>
<p><input disabled="" type="checkbox"> <strong>效能基準測試</strong></p>
<ul>
<li>測量錯誤建立時間 &lt; 0.1ms</li>
<li>測量記憶體占用 &lt; 200 bytes</li>
</ul>
</li>
<li>
<p><input disabled="" type="checkbox"> <strong>100% 測試通過驗證</strong></p>
<ul>
<li><code>dart test</code> 全部通過</li>
<li><code>flutter test</code> 全部通過</li>
<li><code>dart analyze</code> 無警告</li>
</ul>
</li>
</ul>
<h3 id="階段5文檔同步估時30分鐘">階段5：文檔同步（估時：30分鐘）</h3>
<p>任務清單</p>
<ul>
<li>
<p><input disabled="" type="checkbox"> <strong>更新 CLAUDE.md 錯誤處理規範</strong></p>
<ul>
<li>移除舊的 AppError/StandardError 範例</li>
<li>新增原生 Exception 使用範例</li>
</ul>
</li>
<li>
<p><input disabled="" type="checkbox"> <strong>確認設計文件一致性</strong></p>
<ul>
<li>驗證實作完全符合設計文件 v2.0</li>
<li>更新任何不一致的描述</li>
</ul>
</li>
</ul>
<h2 id="預期效果與成功指標">預期效果與成功指標</h2>
<h3 id="效能提升目標">效能提升目標</h3>
<h4 id="量化指標">量化指標</h4>
<ul>
<li><strong>錯誤建立時間</strong>: 從 ~1ms 降至 &lt; 0.1ms（提升 10x）</li>
<li><strong>記憶體使用</strong>: 從 ~1KB 降至 &lt; 200 bytes（降低 80%）</li>
<li><strong>程式碼量</strong>: 移除 ~80% 錯誤處理程式碼</li>
<li><strong>編譯時間</strong>: 減少錯誤處理相關編譯時間</li>
</ul>
<h4 id="效能測試方法">效能測試方法</h4>





<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="kt">void</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="n">group</span><span class="p">(</span><span class="s1">&#39;錯誤處理效能測試&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="n">test</span><span class="p">(</span><span class="s1">&#39;錯誤建立時間測試&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">      <span class="kd">final</span> <span class="n">stopwatch</span> <span class="o">=</span> <span class="n">Stopwatch</span><span class="p">()..</span><span class="n">start</span><span class="p">();</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">      <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="m">1000</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">        <span class="kd">final</span> <span class="n">error</span> <span class="o">=</span> <span class="n">ValidationException</span><span class="p">(</span><span class="n">ErrorCode</span><span class="p">.</span><span class="n">invalidInput</span><span class="p">,</span> <span class="s1">&#39;測試錯誤&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">      <span class="n">stopwatch</span><span class="p">.</span><span class="n">stop</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">      <span class="kd">final</span> <span class="n">averageTime</span> <span class="o">=</span> <span class="n">stopwatch</span><span class="p">.</span><span class="n">elapsedMicroseconds</span> <span class="o">/</span> <span class="m">1000</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">      <span class="n">expect</span><span class="p">(</span><span class="n">averageTime</span><span class="p">,</span> <span class="n">lessThan</span><span class="p">(</span><span class="m">100</span><span class="p">));</span> <span class="c1">// &lt; 0.1ms = 100 microseconds
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"></span>    <span class="p">});</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h3 id="維護性提升">維護性提升</h3>
<h4 id="開發體驗改善">開發體驗改善</h4>
<ul>
<li><strong>零學習成本</strong>: 使用標準 Dart Exception，無需學習自訂錯誤系統</li>
<li><strong>IDE 支援</strong>: 完整的自動完成和型別檢查</li>
<li><strong>除錯友善</strong>: 標準 Exception 堆疊追蹤</li>
<li><strong>測試簡化</strong>: 使用標準 Exception matching</li>
</ul>
<h4 id="程式碼品質提升">程式碼品質提升</h4>
<ul>
<li><strong>單一職責</strong>: 每個 Exception 類別只負責特定錯誤類型</li>
<li><strong>清晰架構</strong>: 明確的錯誤處理流程</li>
<li><strong>易於擴展</strong>: 新增錯誤類型只需擴展 ErrorCode 枚舉</li>
</ul>
<h3 id="驗證標準">驗證標準</h3>
<h4 id="功能驗證">功能驗證</h4>
<ul>
<li><input disabled="" type="checkbox"> 所有現有錯誤場景正常運作</li>
<li><input disabled="" type="checkbox"> 新的錯誤處理流程完整覆蓋</li>
<li><input disabled="" type="checkbox"> 使用者體驗無變化（透明重構）</li>
</ul>
<h4 id="品質驗證">品質驗證</h4>
<ul>
<li><input disabled="" type="checkbox"> 100% 測試通過率</li>
<li><input disabled="" type="checkbox"> 0 個 <code>dart analyze</code> 警告</li>
<li><input disabled="" type="checkbox"> 效能基準測試全部達標</li>
<li><input disabled="" type="checkbox"> 程式碼覆蓋率不下降</li>
</ul>
<h4 id="架構驗證">架構驗證</h4>
<ul>
<li><input disabled="" type="checkbox"> 完全符合設計文件 v2.0 規範</li>
<li><input disabled="" type="checkbox"> 導入路徑完全語意化</li>
<li><input disabled="" type="checkbox"> 無循環依賴問題</li>
</ul>
<h2 id="風險評估與應對策略">風險評估與應對策略</h2>
<h3 id="高風險項目">高風險項目</h3>
<h4 id="1-大規模重構可能破壞現有功能">1. 大規模重構可能破壞現有功能</h4>
<p><strong>風險等級</strong>: 高
<strong>應對策略</strong>:</p>
<ul>
<li>階段性重構，每階段獨立測試</li>
<li>建立重構前的完整測試基準</li>
<li>每個檔案重構後立即驗證</li>
</ul>
<h4 id="2-測試覆蓋率可能下降">2. 測試覆蓋率可能下降</h4>
<p><strong>風險等級</strong>: 中
<strong>應對策略</strong>:</p>
<ul>
<li>重構過程中同步更新測試</li>
<li>確保每個 Exception 類型都有對應測試</li>
<li>使用測試覆蓋率工具監控</li>
</ul>
<h4 id="3-效能目標可能無法達成">3. 效能目標可能無法達成</h4>
<p><strong>風險等級</strong>: 中
<strong>應對策略</strong>:</p>
<ul>
<li>建立效能基準測試</li>
<li>每階段都進行效能驗證</li>
<li>如無法達標則調整設計</li>
</ul>
<h3 id="應急計畫">應急計畫</h3>
<h4 id="重構失敗回滾策略">重構失敗回滾策略</h4>
<ol>
<li><strong>階段回滾</strong>: 每階段完成後建立 Git 標籤</li>
<li><strong>檔案回滾</strong>: 每個檔案重構前建立備份</li>
<li><strong>完整回滾</strong>: 保留 v0.8.26 分支作為安全回滾點</li>
</ol>
<h4 id="問題處理優先級">問題處理優先級</h4>
<ol>
<li><strong>P0 - 影響測試通過率</strong>: 立即修復</li>
<li><strong>P1 - 影響核心功能</strong>: 當日修復</li>
<li><strong>P2 - 影響次要功能</strong>: 階段內修復</li>
<li><strong>P3 - 程式碼品質問題</strong>: 重構完成前修復</li>
</ol>
<h2 id="里程碑與時程規劃">里程碑與時程規劃</h2>
<h3 id="重構時程表">重構時程表</h3>
<table>
  <thead>
      <tr>
          <th>階段</th>
          <th>時程</th>
          <th>里程碑</th>
          <th>驗收標準</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>階段1</td>
          <td>Day 1-2</td>
          <td>新架構建立完成</td>
          <td>新檔案通過分析，基本測試通過</td>
      </tr>
      <tr>
          <td>階段2</td>
          <td>Day 3</td>
          <td>舊架構移除完成</td>
          <td>舊檔案完全移除，編譯通過</td>
      </tr>
      <tr>
          <td>階段3</td>
          <td>Day 4-6</td>
          <td>全域重構完成</td>
          <td>所有使用位置更新完成</td>
      </tr>
      <tr>
          <td>階段4</td>
          <td>Day 7-8</td>
          <td>測試驗證完成</td>
          <td>100% 測試通過，效能達標</td>
      </tr>
      <tr>
          <td>階段5</td>
          <td>Day 9</td>
          <td>文檔同步完成</td>
          <td>文檔與實作完全一致</td>
      </tr>
  </tbody>
</table>
<h3 id="關鍵檢查點">關鍵檢查點</h3>
<h4 id="每日檢查點">每日檢查點</h4>
<ul>
<li>測試通過率維持 100%</li>
<li>編譯無錯誤無警告</li>
<li>重構進度追蹤更新</li>
</ul>
<h4 id="階段檢查點">階段檢查點</h4>
<ul>
<li>階段目標完全達成</li>
<li>無已知技術債務</li>
<li>下階段準備工作完成</li>
</ul>
<h2 id="任務分派準備">任務分派準備</h2>
<h3 id="即將分派的任務類別">即將分派的任務類別</h3>
<h4 id="tdd-驅動任務">TDD 驅動任務</h4>
<ul>
<li>每個新 Exception 類別的測試優先開發</li>
<li>錯誤處理流程的完整測試覆蓋</li>
</ul>
<h4 id="重構執行任務">重構執行任務</h4>
<ul>
<li>舊程式碼的批次替換和驗證</li>
<li>導入路徑的統一化重構</li>
</ul>
<h4 id="品質保證任務">品質保證任務</h4>
<ul>
<li>效能基準測試的建立和執行</li>
<li>程式碼品質檢查和修復</li>
</ul>
<h4 id="文檔同步任務">文檔同步任務</h4>
<ul>
<li>技術文檔的更新和一致性檢查</li>
<li>使用範例和指引的更新</li>
</ul>
<h3 id="任務分派策略">任務分派策略</h3>
<h4 id="並行任務規劃">並行任務規劃</h4>
<ul>
<li>新架構建立與測試撰寫可並行進行</li>
<li>不同模組的重構可獨立執行</li>
<li>文檔更新可與程式碼重構並行</li>
</ul>
<h4 id="依賴關係管理">依賴關係管理</h4>
<ul>
<li>新架構建立 → 舊架構移除 → 全域重構</li>
<li>每階段完成驗證 → 下階段開始</li>
<li>測試更新與程式碼重構同步進行</li>
</ul>
<h2 id="任務拆分清單-44-個小版本">任務拆分清單 (44 個小版本)</h2>
<h3 id="階段0準備文件建立-v091---v094--優先動作">階段0：準備文件建立 (v0.9.1 - v0.9.4) — 優先動作</h3>
<h4 id="v091---建立-api-規格文件">v0.9.1 - 建立 API 規格文件</h4>
<ul>
<li><strong>代理人</strong>: lavender-interface-designer</li>
<li><strong>目標</strong>: 建立完整的錯誤處理系統 API 規格</li>
<li><strong>檔案</strong>: <code>docs/v0.9.0-error-handling-api-specification.md</code></li>
<li><strong>參考文件</strong>: <code>docs/app-error-handling-design.md</code> (設計文件 v2.0)</li>
<li><strong>內容</strong>: 完整介面定義、方法簽名、使用範例、效能要求</li>
<li><strong>後續責任</strong>: 完成後需更新所有實作任務的參考文件欄位</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h4 id="v092---建立-tdd-測試規格">v0.9.2 - 建立 TDD 測試規格</h4>
<ul>
<li><strong>代理人</strong>: sage-test-architect</li>
<li><strong>目標</strong>: 建立新錯誤處理系統的完整測試規格</li>
<li><strong>檔案</strong>: <code>docs/v0.9.0-error-handling-test-specification.md</code></li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-api-specification.md</code> (前置依賴)</li>
<li><code>docs/test-pyramid-design.md</code> (測試架構)</li>
</ul>
</li>
<li><strong>內容</strong>: 測試案例、驗收標準、效能基準、整合測試場景</li>
<li><strong>後續責任</strong>: 完成後需更新所有實作任務的測試規格參考</li>
<li><strong>依賴</strong>: 必須在 v0.9.1 完成後執行</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h4 id="v093---建立移轉指引">v0.9.3 - 建立移轉指引</h4>
<ul>
<li><strong>代理人</strong>: lavender-interface-designer</li>
<li><strong>目標</strong>: 建立舊系統到新系統的移轉指引</li>
<li><strong>檔案</strong>: <code>docs/v0.9.0-error-handling-migration-guide.md</code></li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-api-specification.md</code> (前置依賴)</li>
<li>現有錯誤處理檔案 (lib/core/errors/, lib/core/error_handling/)</li>
</ul>
</li>
<li><strong>內容</strong>: 移轉對照表、使用場景範例、最佳實踐、問題排除</li>
<li><strong>後續責任</strong>: 完成後需更新重構任務的移轉策略參考</li>
<li><strong>依賴</strong>: 必須在 v0.9.1 完成後執行</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h4 id="v094---建立實作範例與任務更新">v0.9.4 - 建立實作範例與任務更新</h4>
<ul>
<li><strong>代理人</strong>: pepper-test-implementer</li>
<li><strong>目標</strong>: 建立實作範例並更新所有任務的參考文件資訊</li>
<li><strong>檔案</strong>: <code>docs/v0.9.0-error-handling-implementation-examples.md</code></li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-api-specification.md</code> (前置依賴)</li>
<li><code>docs/v0.9.0-error-handling-test-specification.md</code> (前置依賴)</li>
<li><code>docs/v0.9.0-error-handling-migration-guide.md</code> (前置依賴)</li>
</ul>
</li>
<li><strong>內容</strong>: 程式碼範例、使用情境示範、常見模式、最佳實踐</li>
<li><strong>後續責任</strong>:
<ul>
<li>更新 v0.9.5-v0.9.44 所有任務的參考文件欄位</li>
<li>補充 TDD 測試依賴資訊</li>
<li>完成準備階段總結</li>
</ul>
</li>
<li><strong>依賴</strong>: 必須在 v0.9.1-v0.9.3 完成後執行</li>
<li><strong>時間</strong>: 3 小時 (包含任務更新時間)</li>
</ul>
<h3 id="階段1新架構建立-v095---v0912">階段1：新架構建立 (v0.9.5 - v0.9.12)</h3>
<h4 id="v095---建立-errorcode-枚舉系統">v0.9.5 - 建立 ErrorCode 枚舉系統</h4>
<ul>
<li><strong>代理人</strong>: lavender-interface-designer</li>
<li><strong>目標</strong>: 設計完整的 ErrorCode 枚舉架構</li>
<li><strong>檔案</strong>: <code>lib/core/errors/error_codes.dart</code></li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-api-specification.md</code> (API 規格)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (實作範例)</li>
</ul>
</li>
<li><strong>測試依賴</strong>: <code>docs/v0.9.0-error-handling-test-specification.md</code></li>
<li><strong>前置任務</strong>: v0.9.1-v0.9.4 (準備文件完成)</li>
<li><strong>時間</strong>: 1 小時</li>
</ul>
<h4 id="v096---實作-errorcode-枚舉定義">v0.9.6 - 實作 ErrorCode 枚舉定義</h4>
<ul>
<li><strong>代理人</strong>: pepper-test-implementer</li>
<li><strong>目標</strong>: 實作所有錯誤代碼枚舉值</li>
<li><strong>檔案</strong>: <code>lib/core/errors/error_codes.dart</code></li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-api-specification.md</code> (API 規格)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (實作範例)</li>
</ul>
</li>
<li><strong>測試依賴</strong>: <code>docs/v0.9.0-error-handling-test-specification.md</code></li>
<li><strong>前置任務</strong>: v0.9.5 (ErrorCode 枚舉系統設計)</li>
<li><strong>時間</strong>: 1 小時</li>
</ul>
<h4 id="v097---設計原生-exception-類別架構">v0.9.7 - 設計原生 Exception 類別架構</h4>
<ul>
<li><strong>代理人</strong>: lavender-interface-designer</li>
<li><strong>目標</strong>: 設計 BookException, ValidationException, NetworkException</li>
<li><strong>檔案</strong>: <code>lib/core/errors/exceptions.dart</code></li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-api-specification.md</code> (API 規格)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (實作範例)</li>
</ul>
</li>
<li><strong>測試依賴</strong>: <code>docs/v0.9.0-error-handling-test-specification.md</code></li>
<li><strong>前置任務</strong>: v0.9.6 (ErrorCode 枚舉定義完成)</li>
<li><strong>時間</strong>: 1.5 小時</li>
</ul>
<h4 id="v098---實作-bookexception-類別">v0.9.8 - 實作 BookException 類別</h4>
<ul>
<li><strong>代理人</strong>: pepper-test-implementer</li>
<li><strong>目標</strong>: 實作 BookException 和相關測試</li>
<li><strong>檔案</strong>: <code>lib/core/errors/exceptions.dart</code>, 測試檔案</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-api-specification.md</code> (API 規格)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (實作範例)</li>
</ul>
</li>
<li><strong>測試依賴</strong>: <code>docs/v0.9.0-error-handling-test-specification.md</code></li>
<li><strong>前置任務</strong>: v0.9.7 (Exception 類別架構設計)</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h4 id="v099---實作-validationexception-類別">v0.9.9 - 實作 ValidationException 類別</h4>
<ul>
<li><strong>代理人</strong>: pepper-test-implementer</li>
<li><strong>目標</strong>: 實作 ValidationException 和相關測試</li>
<li><strong>檔案</strong>: <code>lib/core/errors/exceptions.dart</code>, 測試檔案</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-api-specification.md</code> (API 規格)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (實作範例)</li>
</ul>
</li>
<li><strong>測試依賴</strong>: <code>docs/v0.9.0-error-handling-test-specification.md</code></li>
<li><strong>前置任務</strong>: v0.9.8 (BookException 實作完成)</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h4 id="v0910---實作-networkexception-類別">v0.9.10 - 實作 NetworkException 類別</h4>
<ul>
<li><strong>代理人</strong>: pepper-test-implementer</li>
<li><strong>目標</strong>: 實作 NetworkException 和相關測試</li>
<li><strong>檔案</strong>: <code>lib/core/errors/exceptions.dart</code>, 測試檔案</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-api-specification.md</code> (API 規格)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (實作範例)</li>
</ul>
</li>
<li><strong>測試依賴</strong>: <code>docs/v0.9.0-error-handling-test-specification.md</code></li>
<li><strong>前置任務</strong>: v0.9.9 (ValidationException 實作完成)</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h4 id="v0911---設計統一錯誤處理器架構">v0.9.11 - 設計統一錯誤處理器架構</h4>
<ul>
<li><strong>代理人</strong>: lavender-interface-designer</li>
<li><strong>目標</strong>: 設計 ErrorHandler 類別架構</li>
<li><strong>檔案</strong>: <code>lib/core/errors/error_handler.dart</code></li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-api-specification.md</code> (API 規格)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (實作範例)</li>
</ul>
</li>
<li><strong>測試依賴</strong>: <code>docs/v0.9.0-error-handling-test-specification.md</code></li>
<li><strong>前置任務</strong>: v0.9.10 (NetworkException 實作完成)</li>
<li><strong>時間</strong>: 1 小時</li>
</ul>
<h4 id="v0912---實作統一錯誤處理器">v0.9.12 - 實作統一錯誤處理器</h4>
<ul>
<li><strong>代理人</strong>: pepper-test-implementer</li>
<li><strong>目標</strong>: 實作 ErrorHandler 和相關測試</li>
<li><strong>檔案</strong>: <code>lib/core/errors/error_handler.dart</code>, 測試檔案</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-api-specification.md</code> (API 規格)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (實作範例)</li>
</ul>
</li>
<li><strong>測試依賴</strong>: <code>docs/v0.9.0-error-handling-test-specification.md</code></li>
<li><strong>前置任務</strong>: v0.9.11 (ErrorHandler 架構設計)</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h3 id="階段2統一導出系統-v0913---v0914">階段2：統一導出系統 (v0.9.13 - v0.9.14)</h3>
<h4 id="v0913---建立統一導出檔案">v0.9.13 - 建立統一導出檔案</h4>
<ul>
<li><strong>代理人</strong>: mint-format-specialist</li>
<li><strong>目標</strong>: 建立 errors.dart 統一導出入口</li>
<li><strong>檔案</strong>: <code>lib/core/errors/errors.dart</code></li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-api-specification.md</code> (API 規格)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (實作範例)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.12 (ErrorHandler 實作完成)</li>
<li><strong>時間</strong>: 30 分鐘</li>
</ul>
<h4 id="v0914---新架構整合測試">v0.9.14 - 新架構整合測試</h4>
<ul>
<li><strong>代理人</strong>: sage-test-architect</li>
<li><strong>目標</strong>: 建立新錯誤處理系統的整合測試</li>
<li><strong>檔案</strong>: 整合測試檔案</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-test-specification.md</code> (測試規格)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (實作範例)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.13 (統一導出檔案完成)</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h3 id="階段3舊架構移除-v0915---v0918">階段3：舊架構移除 (v0.9.15 - v0.9.18)</h3>
<h4 id="v0915---分析舊檔案依賴關係">v0.9.15 - 分析舊檔案依賴關係</h4>
<ul>
<li><strong>代理人</strong>: cinnamon-refactor-owl</li>
<li><strong>目標</strong>: 分析所有舊錯誤處理檔案的使用情況</li>
<li><strong>檔案</strong>: 分析報告</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-migration-guide.md</code> (移轉指引)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.14 (新架構整合測試完成)</li>
<li><strong>時間</strong>: 1 小時</li>
</ul>
<h4 id="v0916---移除-unified_error_handlerdart">v0.9.16 - 移除 unified_error_handler.dart</h4>
<ul>
<li><strong>代理人</strong>: cinnamon-refactor-owl</li>
<li><strong>目標</strong>: 安全移除統一錯誤處理器檔案</li>
<li><strong>檔案</strong>: <code>lib/core/error_handling/unified_error_handler.dart</code></li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-migration-guide.md</code> (移轉指引)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.15 (依賴關係分析完成)</li>
<li><strong>時間</strong>: 1 小時</li>
</ul>
<h4 id="v0917---移除-error_handlingapp_errordart">v0.9.17 - 移除 error_handling/app_error.dart</h4>
<ul>
<li><strong>代理人</strong>: cinnamon-refactor-owl</li>
<li><strong>目標</strong>: 移除錯誤處理目錄中的 AppError 定義</li>
<li><strong>檔案</strong>: <code>lib/core/error_handling/app_error.dart</code></li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-migration-guide.md</code> (移轉指引)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.16 (unified_error_handler.dart 移除)</li>
<li><strong>時間</strong>: 1 小時</li>
</ul>
<h4 id="v0918---移除其餘舊檔案並整合內容">v0.9.18 - 移除其餘舊檔案並整合內容</h4>
<ul>
<li><strong>代理人</strong>: cinnamon-refactor-owl</li>
<li><strong>目標</strong>: 移除其餘舊檔案並整合有用內容到新系統</li>
<li><strong>檔案</strong>: <code>lib/core/errors/app_error.dart</code>, <code>standard_error.dart</code>, <code>error_adapters.dart</code></li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-migration-guide.md</code> (移轉指引)</li>
<li><code>docs/v0.9.0-error-handling-api-specification.md</code> (API 規格)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.17 (error_handling 目錄清理)</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h3 id="階段4domain-層重構-v0919---v0928">階段4：Domain 層重構 (v0.9.19 - v0.9.28)</h3>
<h4 id="v0919---重構-library-domain-錯誤處理">v0.9.19 - 重構 Library Domain 錯誤處理</h4>
<ul>
<li><strong>代理人</strong>: cinnamon-refactor-owl</li>
<li><strong>目標</strong>: 更新 lib/domains/library/ 錯誤處理</li>
<li><strong>檔案</strong>: library domain 相關檔案 (&lt;=5個)</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-migration-guide.md</code> (移轉指引)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (實作範例)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.18 (舊檔案移除完成)</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h4 id="v0920---重構-import-domain-錯誤處理">v0.9.20 - 重構 Import Domain 錯誤處理</h4>
<ul>
<li><strong>代理人</strong>: cinnamon-refactor-owl</li>
<li><strong>目標</strong>: 更新 lib/domains/import/ 錯誤處理</li>
<li><strong>檔案</strong>: import domain 相關檔案 (&lt;=5個)</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-migration-guide.md</code> (移轉指引)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (實作範例)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.19 (Library Domain 重構完成)</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h4 id="v0921---重構-scanner-domain-錯誤處理">v0.9.21 - 重構 Scanner Domain 錯誤處理</h4>
<ul>
<li><strong>代理人</strong>: cinnamon-refactor-owl</li>
<li><strong>目標</strong>: 更新 lib/domains/scanner/ 錯誤處理</li>
<li><strong>檔案</strong>: scanner domain 相關檔案 (&lt;=5個)</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-migration-guide.md</code> (移轉指引)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (實作範例)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.20 (Import Domain 重構完成)</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h4 id="v0922---重構-search-domain-錯誤處理">v0.9.22 - 重構 Search Domain 錯誤處理</h4>
<ul>
<li><strong>代理人</strong>: cinnamon-refactor-owl</li>
<li><strong>目標</strong>: 更新 lib/domains/search/ 錯誤處理</li>
<li><strong>檔案</strong>: search domain 相關檔案 (&lt;=5個)</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-migration-guide.md</code> (移轉指引)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (實作範例)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.21 (Scanner Domain 重構完成)</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h4 id="v0923---重構-enrichment-domain-錯誤處理">v0.9.23 - 重構 Enrichment Domain 錯誤處理</h4>
<ul>
<li><strong>代理人</strong>: cinnamon-refactor-owl</li>
<li><strong>目標</strong>: 更新 lib/domains/enrichment/ 錯誤處理</li>
<li><strong>檔案</strong>: enrichment domain 相關檔案 (&lt;=5個)</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-migration-guide.md</code> (移轉指引)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (實作範例)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.22 (Search Domain 重構完成)</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h4 id="v0924---重構-version-management-domain-錯誤處理">v0.9.24 - 重構 Version Management Domain 錯誤處理</h4>
<ul>
<li><strong>代理人</strong>: cinnamon-refactor-owl</li>
<li><strong>目標</strong>: 更新 lib/domains/version_management/ 錯誤處理</li>
<li><strong>檔案</strong>: version management domain 相關檔案 (&lt;=5個)</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-migration-guide.md</code> (移轉指引)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (實作範例)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.23 (Enrichment Domain 重構完成)</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h4 id="v0925---重構-system-domain-錯誤處理">v0.9.25 - 重構 System Domain 錯誤處理</h4>
<ul>
<li><strong>代理人</strong>: cinnamon-refactor-owl</li>
<li><strong>目標</strong>: 更新 lib/domains/system/ 錯誤處理</li>
<li><strong>檔案</strong>: system domain 相關檔案 (&lt;=5個)</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-migration-guide.md</code> (移轉指引)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (實作範例)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.24 (Version Management Domain 重構完成)</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h4 id="v0926---更新-core-層其他模組錯誤處理">v0.9.26 - 更新 Core 層其他模組錯誤處理</h4>
<ul>
<li><strong>代理人</strong>: cinnamon-refactor-owl</li>
<li><strong>目標</strong>: 更新 lib/core/ 其他模組錯誤處理</li>
<li><strong>檔案</strong>: core 層相關檔案 (&lt;=5個)</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-migration-guide.md</code> (移轉指引)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (實作範例)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.25 (System Domain 重構完成)</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h4 id="v0927---重構-infrastructure-層錯誤處理">v0.9.27 - 重構 Infrastructure 層錯誤處理</h4>
<ul>
<li><strong>代理人</strong>: cinnamon-refactor-owl</li>
<li><strong>目標</strong>: 更新 lib/infrastructure/ 錯誤處理</li>
<li><strong>檔案</strong>: infrastructure 層相關檔案 (&lt;=5個)</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-migration-guide.md</code> (移轉指引)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (實作範例)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.26 (Core 層重構完成)</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h4 id="v0928---重構-presentation-層錯誤處理">v0.9.28 - 重構 Presentation 層錯誤處理</h4>
<ul>
<li><strong>代理人</strong>: cinnamon-refactor-owl</li>
<li><strong>目標</strong>: 更新 lib/presentation/ 錯誤處理</li>
<li><strong>檔案</strong>: presentation 層相關檔案 (&lt;=5個)</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-migration-guide.md</code> (移轉指引)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (實作範例)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.27 (Infrastructure 層重構完成)</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h3 id="階段5測試系統更新-v0929---v0935">階段5：測試系統更新 (v0.9.29 - v0.9.35)</h3>
<h4 id="v0929---更新單元測試錯誤處理">v0.9.29 - 更新單元測試錯誤處理</h4>
<ul>
<li><strong>代理人</strong>: sage-test-architect</li>
<li><strong>目標</strong>: 更新所有單元測試的錯誤處理</li>
<li><strong>檔案</strong>: test/unit/ 相關檔案 (&lt;=5個)</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-test-specification.md</code> (測試規格)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (實作範例)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.28 (Presentation 層重構完成)</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h4 id="v0930---更新-widget-測試錯誤處理">v0.9.30 - 更新 Widget 測試錯誤處理</h4>
<ul>
<li><strong>代理人</strong>: sage-test-architect</li>
<li><strong>目標</strong>: 更新所有 Widget 測試的錯誤處理</li>
<li><strong>檔案</strong>: test/widget/ 相關檔案 (&lt;=5個)</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-test-specification.md</code> (測試規格)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (實作範例)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.29 (單元測試更新完成)</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h4 id="v0931---更新整合測試錯誤處理">v0.9.31 - 更新整合測試錯誤處理</h4>
<ul>
<li><strong>代理人</strong>: sage-test-architect</li>
<li><strong>目標</strong>: 更新所有整合測試的錯誤處理</li>
<li><strong>檔案</strong>: test/integration/ 相關檔案 (&lt;=5個)</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-test-specification.md</code> (測試規格)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (實作範例)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.30 (Widget 測試更新完成)</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h4 id="v0932---更新-mock-物件錯誤處理">v0.9.32 - 更新 Mock 物件錯誤處理</h4>
<ul>
<li><strong>代理人</strong>: sage-test-architect</li>
<li><strong>目標</strong>: 更新所有 Mock 物件的錯誤處理</li>
<li><strong>檔案</strong>: test/mocks/ 相關檔案 (&lt;=5個)</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-test-specification.md</code> (測試規格)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (實作範例)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.31 (整合測試更新完成)</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h4 id="v0933---建立新錯誤處理系統效能測試">v0.9.33 - 建立新錯誤處理系統效能測試</h4>
<ul>
<li><strong>代理人</strong>: sage-test-architect</li>
<li><strong>目標</strong>: 建立效能基準測試</li>
<li><strong>檔案</strong>: 新的效能測試檔案</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-test-specification.md</code> (測試規格)</li>
<li><code>docs/v0.9.0-error-handling-api-specification.md</code> (效能要求)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.32 (Mock 物件更新完成)</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h4 id="v0934---建立錯誤處理系統回歸測試">v0.9.34 - 建立錯誤處理系統回歸測試</h4>
<ul>
<li><strong>代理人</strong>: sage-test-architect</li>
<li><strong>目標</strong>: 建立完整的回歸測試套件</li>
<li><strong>檔案</strong>: 新的回歸測試檔案</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-test-specification.md</code> (測試規格)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (測試範例)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.33 (效能測試建立完成)</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h4 id="v0935---執行完整測試套件驗證">v0.9.35 - 執行完整測試套件驗證</h4>
<ul>
<li><strong>代理人</strong>: cinnamon-refactor-owl</li>
<li><strong>目標</strong>: 確保 100% 測試通過率</li>
<li><strong>檔案</strong>: 所有測試檔案</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-test-specification.md</code> (驗收標準)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.34 (回歸測試建立完成)</li>
<li><strong>時間</strong>: 1 小時</li>
</ul>
<h3 id="階段6文檔同步與最終驗證-v0936---v0944">階段6：文檔同步與最終驗證 (v0.9.36 - v0.9.44)</h3>
<h4 id="v0936---更新-claudemd-錯誤處理規範">v0.9.36 - 更新 CLAUDE.md 錯誤處理規範</h4>
<ul>
<li><strong>代理人</strong>: memory-network-builder</li>
<li><strong>目標</strong>: 更新專案錯誤處理指引</li>
<li><strong>檔案</strong>: <code>CLAUDE.md</code></li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-api-specification.md</code> (API 規格)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (使用範例)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.35 (測試驗證完成)</li>
<li><strong>時間</strong>: 1 小時</li>
</ul>
<h4 id="v0937---驗證設計文件一致性">v0.9.37 - 驗證設計文件一致性</h4>
<ul>
<li><strong>代理人</strong>: memory-network-builder</li>
<li><strong>目標</strong>: 確認實作符合設計文件 v2.0</li>
<li><strong>檔案</strong>: 設計文件對比分析</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/app-error-handling-design.md</code> (設計文件 v2.0)</li>
<li><code>docs/v0.9.0-error-handling-api-specification.md</code> (API 規格)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.36 (CLAUDE.md 更新完成)</li>
<li><strong>時間</strong>: 1 小時</li>
</ul>
<h4 id="v0938---執行效能基準測試">v0.9.38 - 執行效能基準測試</h4>
<ul>
<li><strong>代理人</strong>: cinnamon-refactor-owl</li>
<li><strong>目標</strong>: 驗證效能目標達成</li>
<li><strong>檔案</strong>: 效能測試報告</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-api-specification.md</code> (效能要求)</li>
<li><code>docs/v0.9.0-error-handling-test-specification.md</code> (效能測試)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.37 (設計文件驗證完成)</li>
<li><strong>時間</strong>: 1 小時</li>
</ul>
<h4 id="v0939---執行最終品質檢查">v0.9.39 - 執行最終品質檢查</h4>
<ul>
<li><strong>代理人</strong>: cinnamon-refactor-owl</li>
<li><strong>目標</strong>: 全面品質驗證</li>
<li><strong>檔案</strong>: 品質檢查報告</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-test-specification.md</code> (品質標準)</li>
<li><code>docs/v0.9.0-error-handling-api-specification.md</code> (API 規格)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.38 (效能測試完成)</li>
<li><strong>時間</strong>: 1 小時</li>
</ul>
<h4 id="v0940---建立-commonerrors-預編譯錯誤系統">v0.9.40 - 建立 CommonErrors 預編譯錯誤系統</h4>
<ul>
<li><strong>代理人</strong>: pepper-test-implementer</li>
<li><strong>目標</strong>: 實作 CommonErrors 預編譯錯誤系統</li>
<li><strong>檔案</strong>: <code>lib/core/errors/common_errors.dart</code>, 測試檔案</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-api-specification.md</code> (API 規格)</li>
<li><code>docs/v0.9.0-error-handling-implementation-examples.md</code> (實作範例)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.39 (品質檢查完成)</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h4 id="v0941---效能優化與記憶體優化">v0.9.41 - 效能優化與記憶體優化</h4>
<ul>
<li><strong>代理人</strong>: cinnamon-refactor-owl</li>
<li><strong>目標</strong>: 執行最終效能優化</li>
<li><strong>檔案</strong>: 所有錯誤處理檔案</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-api-specification.md</code> (效能目標)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.40 (CommonErrors 實作完成)</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h4 id="v0942---跨平台一致性驗證">v0.9.42 - 跨平台一致性驗證</h4>
<ul>
<li><strong>代理人</strong>: cinnamon-refactor-owl</li>
<li><strong>目標</strong>: 驗證與 Chrome Extension 錯誤格式一致性</li>
<li><strong>檔案</strong>: 跨平台驗證報告</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-api-specification.md</code> (跨平台規格)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.41 (效能優化完成)</li>
<li><strong>時間</strong>: 1 小時</li>
</ul>
<h4 id="v0943---完整系統整合測試">v0.9.43 - 完整系統整合測試</h4>
<ul>
<li><strong>代理人</strong>: sage-test-architect</li>
<li><strong>目標</strong>: 執行端到端整合測試</li>
<li><strong>檔案</strong>: 整合測試執行報告</li>
<li><strong>參考文件</strong>:
<ul>
<li><code>docs/v0.9.0-error-handling-test-specification.md</code> (整合測試規格)</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.42 (跨平台驗證完成)</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h4 id="v0944---重構完成總結報告">v0.9.44 - 重構完成總結報告</h4>
<ul>
<li><strong>代理人</strong>: memory-network-builder</li>
<li><strong>目標</strong>: 建立重構完成總結報告</li>
<li><strong>檔案</strong>: <code>docs/v0.9.0-error-handling-refactor-completion-report.md</code></li>
<li><strong>參考文件</strong>:
<ul>
<li>所有 v0.9.0 錯誤處理文件</li>
<li>工作日誌和任務執行記錄</li>
</ul>
</li>
<li><strong>前置任務</strong>: v0.9.43 (整合測試完成)</li>
<li><strong>時間</strong>: 2 小時</li>
</ul>
<h2 id="任務統計">任務統計</h2>
<ul>
<li><strong>總任務數</strong>: 44 個小版本 (完整的錯誤處理系統重構)</li>
<li><strong>預估總時間</strong>: 約 74-79 小時 (包含完整的準備、實作、驗證階段)</li>
<li><strong>平均任務時間</strong>: 1.7 小時</li>
<li><strong>最大任務時間</strong>: 3 小時 (v0.9.4 包含任務更新)</li>
<li><strong>準備階段</strong>: v0.9.1-v0.9.4 (9 小時，包含文件建立和任務更新)</li>
<li><strong>實作階段</strong>: v0.9.5-v0.9.44 (65-70 小時)</li>
</ul>
<h3 id="階段分佈">階段分佈</h3>
<ul>
<li><strong>階段0 - 準備文件建立</strong>: v0.9.1-v0.9.4 (4 個任務, 9 小時)</li>
<li><strong>階段1 - 新架構建立</strong>: v0.9.5-v0.9.12 (8 個任務, 12.5 小時)</li>
<li><strong>階段2 - 統一導出系統</strong>: v0.9.13-v0.9.14 (2 個任務, 2.5 小時)</li>
<li><strong>階段3 - 舊架構移除</strong>: v0.9.15-v0.9.18 (4 個任務, 5 小時)</li>
<li><strong>階段4 - Domain 層重構</strong>: v0.9.19-v0.9.28 (10 個任務, 20 小時)</li>
<li><strong>階段5 - 測試系統更新</strong>: v0.9.29-v0.9.35 (7 個任務, 13 小時)</li>
<li><strong>階段6 - 文檔同步與驗證</strong>: v0.9.36-v0.9.44 (9 個任務, 12 小時)</li>
</ul>
<h3 id="重要改進-v094-完成">重要改進 (v0.9.4 完成)</h3>
<ul>
<li><strong>每個任務都有完整的參考文件</strong> (API 規格、測試規格、移轉指引、實作範例)</li>
<li><strong>TDD 測試依賴關係明確</strong> (所有實作任務都引用測試規格)</li>
<li><strong>移轉策略詳細</strong> (重構任務都有移轉指引支援)</li>
<li><strong>依賴關係和執行順序清晰</strong> (明確的前置任務要求)</li>
<li><strong>參考文件資訊完整</strong> (實作範例提供完整的程式碼指引)</li>
</ul>
<h2 id="工作日誌狀態">工作日誌狀態</h2>
<ul>
<li>
<p><strong>當前狀態</strong>: v0.9.4 完成 - 準備階段全部完成</p>
<ul>
<li>v0.9.1: API 規格文件完成</li>
<li>v0.9.2: 測試規格文件完成</li>
<li>v0.9.3: 移轉指引文件完成</li>
<li>v0.9.4: 實作範例文件完成 + 所有任務資訊更新完成</li>
</ul>
</li>
<li>
<p><strong>下一步行動</strong>: 準備階段完成，實作階段準備就緒</p>
<ul>
<li><strong>立即可執行</strong>: v0.9.5 - 建立 ErrorCode 枚舉系統</li>
<li><strong>所有實作任務都有完整參考資訊</strong></li>
<li><strong>TDD 測試驅動開發模式已建立</strong></li>
</ul>
</li>
<li>
<p><strong>預計完成時間</strong>: v0.9.x 版本週期內完成 (約 10-12 週)</p>
</li>
<li>
<p><strong>風險等級</strong>: 低風險 - 完整的準備文件和詳細計畫</p>
</li>
</ul>
<hr>
<p><strong>工作日誌建立時間</strong>: 2025-09-27 18:30
<strong>預計更新頻率</strong>: 每階段完成後更新進度
<strong>責任人</strong>: Claude Code AI Assistant
<strong>審核狀態</strong>: 已建立，等待任務分派</p>
]]></content:encoded></item><item><title>Claude Code Hook 系統 Exit Code 實驗</title><link>https://tarrragon.github.io/blog/record/claude-code-hook-%E7%B3%BB%E7%B5%B1-exit-code-%E5%AF%A6%E9%A9%97/</link><pubDate>Fri, 26 Sep 2025 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/claude-code-hook-%E7%B3%BB%E7%B5%B1-exit-code-%E5%AF%A6%E9%A9%97/</guid><description>&lt;h2 id="研究背景">研究背景&lt;/h2>
&lt;p>在開發 Book Overview App 的過程中，我設計了一套完整的 Hook 系統防護體系，目的是&lt;strong>防止開發過程中的逃避行為和確保AI的工作品質&lt;/strong>。&lt;/p>
&lt;h3 id="-hook-防護體系設計起因">🔸 Hook 防護體系設計起因&lt;/h3>
&lt;p>AI會有逃避行為，以完成任務指標為優先而捨棄程式品質或者直接修改測試。&lt;/p>
&lt;p>我設計了以下幾種防止停止和強制繼續的 Hook 機制：&lt;/p>
&lt;h4 id="-check-todospy---todowrite-完成度檢查">&lt;strong>🔹 check-todos.py - TodoWrite 完成度檢查&lt;/strong>&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 防止未完成工作的會話結束&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="n">todos_pending&lt;/span> &lt;span class="ow">or&lt;/span> &lt;span class="n">todos_in_progress&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl"> &lt;span class="n">output&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;continue&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;stopReason&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Incomplete todos detected&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>🔹 &lt;strong>目的&lt;/strong>: 防止有未完成 TodoWrite 任務時意外停止&lt;/li>
&lt;li>🔹 &lt;strong>機制&lt;/strong>: 檢查 transcript 中的 TodoWrite 工具狀態&lt;/li>
&lt;li>🔹 &lt;strong>效果&lt;/strong>: 強制繼續工作直到所有對話任務完成&lt;/li>
&lt;/ul>
&lt;h4 id="-check-work-logsh---工作日誌完成度檢查">&lt;strong>🔹 check-work-log.sh - 工作日誌完成度檢查&lt;/strong>&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 當工作狀態為進行中時，exit 2 阻止停止&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="nb">exit&lt;/span> &lt;span class="m">2&lt;/span> &lt;span class="c1"># 阻止會話結束，要求繼續工作&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>🔹 &lt;strong>目的&lt;/strong>: 確保當前版本的工作確實完成才允許停止&lt;/li>
&lt;li>🔹 &lt;strong>機制&lt;/strong>: 分析工作日誌的完成指標和技術債務狀況&lt;/li>
&lt;li>🔹 &lt;strong>效果&lt;/strong>: 避免「假性完成」，確保工作品質&lt;/li>
&lt;/ul>
&lt;h4 id="-check-next-objectivessh---版本系列目標檢查">&lt;strong>🔹 check-next-objectives.sh - 版本系列目標檢查&lt;/strong>&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 當版本系列仍在進行中時，exit 2 阻止停止&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="nb">exit&lt;/span> &lt;span class="m">2&lt;/span> &lt;span class="c1"># 防止版本系列中途放棄&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>🔹 &lt;strong>目的&lt;/strong>: 防止版本系列開發中途放棄&lt;/li>
&lt;li>🔹 &lt;strong>機制&lt;/strong>: 檢查 todolist.md 中的版本系列完成度&lt;/li>
&lt;li>🔹 &lt;strong>效果&lt;/strong>: 確保版本系列目標達成才允許推進&lt;/li>
&lt;/ul>
&lt;h4 id="-check-5w1h-compliancepy---決策品質檢查">&lt;strong>🔹 check-5w1h-compliance.py - 決策品質檢查&lt;/strong>&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 當 5W1H 分析不合規時，阻止 TodoWrite&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;decision&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;block&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;reason&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;5W1H analysis required&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>🔹 &lt;strong>目的&lt;/strong>: 防止逃避性思考和粗糙決策&lt;/li>
&lt;li>🔹 &lt;strong>機制&lt;/strong>: PreToolUse 階段攔截並檢查 5W1H 完整性&lt;/li>
&lt;li>🔹 &lt;strong>效果&lt;/strong>: 強制深度思考，杜絕逃避行為&lt;/li>
&lt;/ul>
&lt;h3 id="-遇到的邏輯衝突問題">🔸 遇到的邏輯衝突問題&lt;/h3>
&lt;p>在開發過程中，發現了奇怪的狀況：&lt;/p>
&lt;p>&lt;strong>原問題&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>&lt;code>check-todos.py&lt;/code> 顯示 &amp;ldquo;All todos completed successfully.&amp;rdquo;&lt;/li>
&lt;li>同時系統又顯示 &amp;ldquo;Stop hook prevented continuation&amp;rdquo; 訊息&lt;/li>
&lt;li>我的問題是：已完成所有 todos 為何還阻止停止？這訊息是誰吐出來的？&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>根本原因&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>不同 Hook 腳本檢查不同的 todo 來源（會話 vs 專案）&lt;/li>
&lt;li>會話的任務已結束，但是有其他hook觸發了避免停止的機制&lt;/li>
&lt;/ul>
&lt;h3 id="設計思路">設計思路&lt;/h3>
&lt;p>&lt;strong>防護體系的核心理念&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>多層防護&lt;/strong>：從對話層、工作層、版本層、決策層建立完整防護&lt;/li>
&lt;li>&lt;strong>零逃避容忍&lt;/strong>：任何逃避行為都會被自動檢測和阻止&lt;/li>
&lt;li>&lt;strong>品質強制&lt;/strong>：不符合品質標準的工作不允許繼續&lt;/li>
&lt;li>&lt;strong>使用者指引&lt;/strong>：被阻止時提供明確的修正指引和下一步行動&lt;/li>
&lt;/ul>
&lt;h3 id="exit-code-官方定義">Exit Code 官方定義&lt;/h3>
&lt;p>根據 Claude Code 官方文件，Hook 腳本的 Exit Code 具有特定含義：&lt;/p>
&lt;h4 id="exit-0---成功執行">&lt;strong>Exit 0&lt;/strong> - 成功執行&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="nb">exit&lt;/span> &lt;span class="m">0&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>🔸 &lt;strong>正常成功執行&lt;/strong>&lt;/li>
&lt;li>🔸 &lt;strong>允許 Claude Code 繼續操作&lt;/strong>&lt;/li>
&lt;li>🔸 &lt;strong>不會產生任何阻止行為&lt;/strong>&lt;/li>
&lt;li>🔸 &lt;strong>用途&lt;/strong>: 檢查通過、條件滿足、正常完成&lt;/li>
&lt;/ul>
&lt;h4 id="exit-1---一般錯誤">&lt;strong>Exit 1&lt;/strong> - 一般錯誤&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="nb">exit&lt;/span> &lt;span class="m">1&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>🔸 &lt;strong>非阻塞性錯誤&lt;/strong>&lt;/li>
&lt;li>🔸 &lt;strong>操作仍可繼續&lt;/strong>&lt;/li>
&lt;li>🔸 &lt;strong>表示&lt;/strong>: 有問題但不嚴重&lt;/li>
&lt;li>🔸 &lt;strong>用途&lt;/strong>: 輕微問題、建議性警告&lt;/li>
&lt;/ul>
&lt;h4 id="exit-2---阻止停止-關鍵">&lt;strong>Exit 2&lt;/strong> - 阻止停止 (關鍵!)&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="nb">exit&lt;/span> &lt;span class="m">2&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>🔸 &lt;strong>阻止 Claude Code 停止會話&lt;/strong>&lt;/li>
&lt;li>🔸 &lt;strong>顯示 stderr 內容給 Claude&lt;/strong>&lt;/li>
&lt;li>🔸 &lt;strong>強制要求繼續工作&lt;/strong>&lt;/li>
&lt;li>🔸 &lt;strong>用途&lt;/strong>: 工作未完成、需要繼續開發&lt;/li>
&lt;li>🔸 &lt;strong>注意&lt;/strong>: 這是「阻止停止」而非「阻止繼續」&lt;/li>
&lt;/ul>
&lt;h4 id="exit-3---嚴重錯誤">&lt;strong>Exit 3+&lt;/strong> - 嚴重錯誤&lt;/h4>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="nb">exit&lt;/span> &lt;span class="m">3&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>🔸 &lt;strong>嚴重錯誤狀態&lt;/strong>&lt;/li>
&lt;li>🔸 &lt;strong>通常阻止繼續&lt;/strong>&lt;/li>
&lt;li>🔸 &lt;strong>自定義錯誤類型&lt;/strong>&lt;/li>
&lt;li>🔸 &lt;strong>用途&lt;/strong>: 需要立即處理的問題&lt;/li>
&lt;/ul>
&lt;h2 id="exit-2-的真正定義">Exit 2 的真正定義&lt;/h2>
&lt;h3 id="語意澄清">語意澄清&lt;/h3>
&lt;p>&lt;strong>🔸 錯誤理解&lt;/strong>:&lt;/p></description><content:encoded><![CDATA[<h2 id="研究背景">研究背景</h2>
<p>在開發 Book Overview App 的過程中，我設計了一套完整的 Hook 系統防護體系，目的是<strong>防止開發過程中的逃避行為和確保AI的工作品質</strong>。</p>
<h3 id="-hook-防護體系設計起因">🔸 Hook 防護體系設計起因</h3>
<p>AI會有逃避行為，以完成任務指標為優先而捨棄程式品質或者直接修改測試。</p>
<p>我設計了以下幾種防止停止和強制繼續的 Hook 機制：</p>
<h4 id="-check-todospy---todowrite-完成度檢查"><strong>🔹 check-todos.py - TodoWrite 完成度檢查</strong></h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><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="k">if</span> <span class="n">todos_pending</span> <span class="ow">or</span> <span class="n">todos_in_progress</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">    <span class="n">output</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&#34;continue&#34;</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span> <span class="s2">&#34;stopReason&#34;</span><span class="p">:</span> <span class="s2">&#34;Incomplete todos detected&#34;</span><span class="p">}</span></span></span></code></pre></div><ul>
<li>🔹 <strong>目的</strong>: 防止有未完成 TodoWrite 任務時意外停止</li>
<li>🔹 <strong>機制</strong>: 檢查 transcript 中的 TodoWrite 工具狀態</li>
<li>🔹 <strong>效果</strong>: 強制繼續工作直到所有對話任務完成</li>
</ul>
<h4 id="-check-work-logsh---工作日誌完成度檢查"><strong>🔹 check-work-log.sh - 工作日誌完成度檢查</strong></h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 當工作狀態為進行中時，exit 2 阻止停止</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nb">exit</span> <span class="m">2</span>  <span class="c1"># 阻止會話結束，要求繼續工作</span></span></span></code></pre></div><ul>
<li>🔹 <strong>目的</strong>: 確保當前版本的工作確實完成才允許停止</li>
<li>🔹 <strong>機制</strong>: 分析工作日誌的完成指標和技術債務狀況</li>
<li>🔹 <strong>效果</strong>: 避免「假性完成」，確保工作品質</li>
</ul>
<h4 id="-check-next-objectivessh---版本系列目標檢查"><strong>🔹 check-next-objectives.sh - 版本系列目標檢查</strong></h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 當版本系列仍在進行中時，exit 2 阻止停止</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nb">exit</span> <span class="m">2</span>  <span class="c1"># 防止版本系列中途放棄</span></span></span></code></pre></div><ul>
<li>🔹 <strong>目的</strong>: 防止版本系列開發中途放棄</li>
<li>🔹 <strong>機制</strong>: 檢查 todolist.md 中的版本系列完成度</li>
<li>🔹 <strong>效果</strong>: 確保版本系列目標達成才允許推進</li>
</ul>
<h4 id="-check-5w1h-compliancepy---決策品質檢查"><strong>🔹 check-5w1h-compliance.py - 決策品質檢查</strong></h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 當 5W1H 分析不合規時，阻止 TodoWrite</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="p">{</span><span class="s2">&#34;decision&#34;</span><span class="p">:</span> <span class="s2">&#34;block&#34;</span><span class="p">,</span> <span class="s2">&#34;reason&#34;</span><span class="p">:</span> <span class="s2">&#34;5W1H analysis required&#34;</span><span class="p">}</span></span></span></code></pre></div><ul>
<li>🔹 <strong>目的</strong>: 防止逃避性思考和粗糙決策</li>
<li>🔹 <strong>機制</strong>: PreToolUse 階段攔截並檢查 5W1H 完整性</li>
<li>🔹 <strong>效果</strong>: 強制深度思考，杜絕逃避行為</li>
</ul>
<h3 id="-遇到的邏輯衝突問題">🔸 遇到的邏輯衝突問題</h3>
<p>在開發過程中，發現了奇怪的狀況：</p>
<p><strong>原問題</strong>:</p>
<ul>
<li><code>check-todos.py</code> 顯示 &ldquo;All todos completed successfully.&rdquo;</li>
<li>同時系統又顯示 &ldquo;Stop hook prevented continuation&rdquo; 訊息</li>
<li>我的問題是：已完成所有 todos 為何還阻止停止？這訊息是誰吐出來的？</li>
</ul>
<p><strong>根本原因</strong>:</p>
<ul>
<li>不同 Hook 腳本檢查不同的 todo 來源（會話 vs 專案）</li>
<li>會話的任務已結束，但是有其他hook觸發了避免停止的機制</li>
</ul>
<h3 id="設計思路">設計思路</h3>
<p><strong>防護體系的核心理念</strong>：</p>
<ul>
<li><strong>多層防護</strong>：從對話層、工作層、版本層、決策層建立完整防護</li>
<li><strong>零逃避容忍</strong>：任何逃避行為都會被自動檢測和阻止</li>
<li><strong>品質強制</strong>：不符合品質標準的工作不允許繼續</li>
<li><strong>使用者指引</strong>：被阻止時提供明確的修正指引和下一步行動</li>
</ul>
<h3 id="exit-code-官方定義">Exit Code 官方定義</h3>
<p>根據 Claude Code 官方文件，Hook 腳本的 Exit Code 具有特定含義：</p>
<h4 id="exit-0---成功執行"><strong>Exit 0</strong> - 成功執行</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="nb">exit</span> <span class="m">0</span></span></span></code></pre></div><ul>
<li>🔸 <strong>正常成功執行</strong></li>
<li>🔸 <strong>允許 Claude Code 繼續操作</strong></li>
<li>🔸 <strong>不會產生任何阻止行為</strong></li>
<li>🔸 <strong>用途</strong>: 檢查通過、條件滿足、正常完成</li>
</ul>
<h4 id="exit-1---一般錯誤"><strong>Exit 1</strong> - 一般錯誤</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="nb">exit</span> <span class="m">1</span></span></span></code></pre></div><ul>
<li>🔸 <strong>非阻塞性錯誤</strong></li>
<li>🔸 <strong>操作仍可繼續</strong></li>
<li>🔸 <strong>表示</strong>: 有問題但不嚴重</li>
<li>🔸 <strong>用途</strong>: 輕微問題、建議性警告</li>
</ul>
<h4 id="exit-2---阻止停止-關鍵"><strong>Exit 2</strong> - 阻止停止 (關鍵!)</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="nb">exit</span> <span class="m">2</span></span></span></code></pre></div><ul>
<li>🔸 <strong>阻止 Claude Code 停止會話</strong></li>
<li>🔸 <strong>顯示 stderr 內容給 Claude</strong></li>
<li>🔸 <strong>強制要求繼續工作</strong></li>
<li>🔸 <strong>用途</strong>: 工作未完成、需要繼續開發</li>
<li>🔸 <strong>注意</strong>: 這是「阻止停止」而非「阻止繼續」</li>
</ul>
<h4 id="exit-3---嚴重錯誤"><strong>Exit 3+</strong> - 嚴重錯誤</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="nb">exit</span> <span class="m">3</span></span></span></code></pre></div><ul>
<li>🔸 <strong>嚴重錯誤狀態</strong></li>
<li>🔸 <strong>通常阻止繼續</strong></li>
<li>🔸 <strong>自定義錯誤類型</strong></li>
<li>🔸 <strong>用途</strong>: 需要立即處理的問題</li>
</ul>
<h2 id="exit-2-的真正定義">Exit 2 的真正定義</h2>
<h3 id="語意澄清">語意澄清</h3>
<p><strong>🔸 錯誤理解</strong>:</p>
<ul>
<li>&ldquo;Stop hook prevented continuation&rdquo; = 阻止繼續工作</li>
</ul>
<p><strong>🔸 正確理解</strong>:</p>
<ul>
<li>&ldquo;Stop hook prevented continuation&rdquo; = 阻止了「停止會話」這個行為</li>
<li>實際效果在我腳本觸發的原因：當前會話還有todo清單未完成，強制繼續工作，不允許停止會話</li>
</ul>
<h3 id="實際應用場景">實際應用場景</h3>
<p>專案中的 Exit 2 使用案例：</p>
<h4 id="check-work-logsh"><strong>check-work-log.sh</strong></h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 當工作狀態為 &#34;IN_PROGRESS&#34; 時</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="k">if</span> <span class="o">[[</span> <span class="nv">$WORK_STATUS</span> <span class="o">==</span> <span class="s2">&#34;IN_PROGRESS&#34;</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;工作進行中，建議完成後再推進&#34;</span>
</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">    <span class="c1"># 輸出詳細說明到 stderr</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    cat &gt;<span class="p">&amp;</span><span class="m">2</span> <span class="s">&lt;&lt;EOF
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="s">🔸 版本推進暫停 - 當前工作尚未完成
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="s">🔸 停止原因：
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="s">• 工作日誌顯示工作仍在進行中
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="s">• 完成度指標不足
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="s">• todolist.md 中有待辦任務
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="s">🔸 建議的 TodoWrite 任務：
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="s">請執行以下工作項目...
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="s">EOF</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="nb">exit</span> <span class="m">2</span>  <span class="c1"># 阻止停止，要求繼續工作</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">fi</span></span></span></code></pre></div><h4 id="check-next-objectivessh"><strong>check-next-objectives.sh</strong></h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 當版本系列狀態為 &#34;IN_PROGRESS&#34; 時</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="k">case</span> <span class="s2">&#34;</span><span class="nv">$SERIES_STATUS</span><span class="s2">&#34;</span> in
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="s2">&#34;IN_PROGRESS&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;版本系列仍在進行中&#34;</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="c1"># 提供具體的待辦任務資訊</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">        cat &gt;<span class="p">&amp;</span><span class="m">2</span> <span class="s">&lt;&lt;EOF
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="s">🔸 版本推進暫停 - 當前版本系列仍在進行中
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="s">🔸 部分待完成任務範例：
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="s">• 修復UI測試中的類型問題
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="s">• 更新ViewModel層錯誤處理適配
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="s">🔸 建議的 TodoWrite 任務：
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="s">繼續版本系列開發...
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="s">EOF</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl">        <span class="nb">exit</span> <span class="m">2</span>  <span class="c1"># 阻止停止，要求繼續版本開發</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">        <span class="p">;;</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="k">esac</span></span></span></code></pre></div><h2 id="-hook-系統邏輯衝突解決方案">🔸 Hook 系統邏輯衝突解決方案</h2>
<h3 id="問題診斷">問題診斷</h3>
<p><strong>衝突場景</strong>:</p>
<ul>
<li><code>check-todos.py</code> 檢查 TodoWrite 工具（會話級別）</li>
<li>其他 Hook 腳本檢查 <code>todolist.md</code>（專案級別）</li>
<li>結果：TodoWrite 完成但 todolist.md 仍有待辦事項</li>
</ul>
<h3 id="解決策略">解決策略</h3>
<h4 id="方案一hook-腳本提供-todowrite-建議"><strong>方案一：Hook 腳本提供 TodoWrite 建議</strong></h4>
<p>在返回 <code>exit 2</code> 的 Hook 腳本中：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 檢查 todolist.md 中的未完成任務</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nv">PENDING_TODOS</span><span class="o">=</span><span class="k">$(</span>grep -c <span class="s2">&#34;\[ \]&#34;</span> <span class="s2">&#34;</span><span class="nv">$PROJECT_ROOT</span><span class="s2">/docs/todolist.md&#34;</span> 2&gt;/dev/null <span class="o">||</span> <span class="nb">echo</span> <span class="s2">&#34;0&#34;</span><span class="k">)</span>
</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 class="c1"># 在 stderr 中提供 TodoWrite 建議</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">cat &gt;<span class="p">&amp;</span><span class="m">2</span> <span class="s">&lt;&lt;EOF
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="s">🔸 建議的 TodoWrite 任務：
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="s">請執行以下 TodoWrite 來管理具體工作項目：
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="s">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="s">TodoWrite([
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="s">  {&#34;content&#34;: &#34;完成當前版本核心開發工作&#34;, &#34;status&#34;: &#34;pending&#34;, &#34;activeForm&#34;: &#34;完成當前版本核心開發工作&#34;},
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="s">  {&#34;content&#34;: &#34;執行完整測試確保100%通過率&#34;, &#34;status&#34;: &#34;pending&#34;, &#34;activeForm&#34;: &#34;執行完整測試確保100%通過率&#34;}
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="s">])
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="s">EOF</span></span></span></code></pre></div><h4 id="方案二統一狀態檢查邏輯"><strong>方案二：統一狀態檢查邏輯</strong></h4>
<p>確保所有 Hook 腳本檢查相同的狀態來源，或者建立狀態同步機制。</p>
<h2 id="實際演練">實際演練</h2>
<h3 id="exit-code-選擇原則">Exit Code 選擇原則</h3>
<ul>
<li>🔸 <strong>Exit 0</strong>: 一切正常，可以繼續</li>
<li>🔸 <strong>Exit 1</strong>: 有小問題，但可以繼續</li>
<li>🔸 <strong>Exit 2</strong>: 工作未完成，<strong>禁止停止</strong>，必須繼續</li>
<li>🔸 <strong>Exit 3+</strong>: 嚴重問題，需要立即處理</li>
</ul>
<h3 id="hook-腳本設計模式">Hook 腳本設計模式</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"># 🔸 檢查狀態</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">check_work_status<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="c1"># 實作狀態檢查邏輯</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="o">}</span>
</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"><span class="c1"># 🔸 根據狀態決定 exit code</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">case</span> <span class="s2">&#34;</span><span class="nv">$WORK_STATUS</span><span class="s2">&#34;</span> in
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="s2">&#34;COMPLETED&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;工作已完成，可推進版本&#34;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">        <span class="nb">exit</span> <span class="m">0</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">        <span class="p">;;</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="s2">&#34;MOSTLY_COMPLETED&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;基本完成，建議檢查後推進&#34;</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">        <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">        <span class="p">;;</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="s2">&#34;IN_PROGRESS&#34;</span><span class="o">)</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;工作進行中，建議完成後再推進&#34;</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl">        <span class="c1"># 🔸 提供詳細的使用者指引</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">        cat &gt;<span class="p">&amp;</span><span class="m">2</span> <span class="s">&lt;&lt;EOF
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="s">🔸 停止原因說明
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="s">🔸 需要採取的行動
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="s">🔸 建議的 TodoWrite 任務
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="s">EOF</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">
</span></span><span class="line"><span class="ln">28</span><span class="cl">        <span class="nb">exit</span> <span class="m">2</span>  <span class="c1"># 阻止停止</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">        <span class="p">;;</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">    *<span class="o">)</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">        <span class="nb">echo</span> <span class="s2">&#34;工作未完成，需要繼續開發&#34;</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">        <span class="nb">exit</span> <span class="m">3</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">        <span class="p">;;</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="k">esac</span></span></span></code></pre></div><h3 id="使用者體驗優化">使用者體驗優化</h3>
<ul>
<li><strong>清楚的錯誤訊息</strong>: 使用 stderr 輸出詳細說明</li>
<li><strong>具體的行動指引</strong>: 告訴使用者需要做什麼</li>
<li><strong>TodoWrite 整合</strong>: 提供可執行的任務建議</li>
<li><strong>狀態一致性</strong>: 確保不同檢查機制的邏輯一致</li>
</ul>
<h2 id="-實際測試結果">🔸 實際測試結果</h2>
<h3 id="測試案例--check-work-logsh">測試案例 : check-work-log.sh</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">$ ./.claude/scripts/check-work-log.sh
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># 返回 exit 3，stderr 顯示：</span>
</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">• 工作日誌缺乏完成指標 <span class="o">(</span><span class="nv">0</span> <span class="o">=</span> 0<span class="o">)</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">• todolist.md 中有 <span class="m">240</span> 個待辦任務
</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">🔸 建議的 TodoWrite 任務：
</span></span><span class="line"><span class="ln">9</span><span class="cl">請執行以下 TodoWrite 開始系統化工作...</span></span></code></pre></div><h3 id="測試案例--check-next-objectivessh">測試案例 🔸: check-next-objectives.sh</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">$ ./.claude/scripts/check-next-objectives.sh
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># 返回 exit 2，stderr 顯示：</span>
</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">• 修復UI測試中的類型問題
</span></span><span class="line"><span class="ln">6</span><span class="cl">• 更新ViewModel層錯誤處理適配
</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">🔸 建議的 TodoWrite 任務：
</span></span><span class="line"><span class="ln">9</span><span class="cl">請執行以下 TodoWrite 繼續版本系列開發...</span></span></code></pre></div><h2 id="-效果評估">🔸 效果評估</h2>
<h3 id="問題解決成果">問題解決成果</h3>
<ul>
<li>🔸 <strong>邏輯一致性</strong>: Hook 系統不再產生矛盾訊息</li>
<li>🔸 <strong>使用者體驗</strong>: 清楚知道為什麼被阻止和需要做什麼</li>
<li>🔸 <strong>工作流程</strong>: TodoWrite 與 todolist.md 狀態同步</li>
</ul>
<h3 id="關鍵學習">關鍵學習</h3>
<p>🔸 <strong>Exit 2 的真實含義</strong>: 阻止停止，要求繼續工作
🔸 <strong>stderr 的重要性</strong>: Claude Code 會顯示 stderr 給使用者
🔸 <strong>狀態檢查一致性</strong>: 不同檢查機制需要協調
🔸 <strong>使用者指引</strong>: 提供可執行的具體建議比抽象說明更有效</p>
<h2 id="後續改善方向">後續改善方向</h2>
<ul>
<li><strong>Hook 腳本標準化</strong>: 建立統一的錯誤處理和訊息格式</li>
<li><strong>狀態同步機制</strong>: 建立 TodoWrite 與 todolist.md 的雙向同步</li>
<li><strong>使用者體驗測試</strong>: 收集真實使用場景的反饋</li>
<li><strong>文件完善</strong>: 將這些經驗整理成團隊開發規範</li>
</ul>
<h2 id="-參考資源">🔸 參考資源</h2>
<ul>
<li><a href="https://docs.claude.com/en/docs/claude-code/hooks-guide">Claude Code Hooks Guide</a></li>
<li><a href="https://docs.claude.com/en/docs/claude-code/hooks">Claude Code Hook Events</a></li>
<li><a href="https://www.reddit.com/r/ClaudeAI/comments/1now8n7/cc_hook_that_made_my_life_easier_today">reddit網友分享 在有todo未完成的情況下用hook偵測會話轉階段，強迫AI繼續工作不要停下來</a></li>
</ul>
<hr>
]]></content:encoded></item><item><title>Package 導入路徑語意化方法論</title><link>https://tarrragon.github.io/blog/record/package-%E5%B0%8E%E5%85%A5%E8%B7%AF%E5%BE%91%E8%AA%9E%E6%84%8F%E5%8C%96%E6%96%B9%E6%B3%95%E8%AB%96/</link><pubDate>Sun, 21 Sep 2025 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/package-%E5%B0%8E%E5%85%A5%E8%B7%AF%E5%BE%91%E8%AA%9E%E6%84%8F%E5%8C%96%E6%96%B9%E6%B3%95%E8%AB%96/</guid><description>&lt;h2 id="為什麼導入聲明很重要">為什麼導入聲明很重要&lt;/h2>
&lt;p>在程式開發中，導入聲明往往被視為技術細節，但它們實際上是&lt;strong>架構文件的第一行&lt;/strong>。每個 import/require 都在告訴讀者：這個模組的依賴關係、系統的組織方式、以及設計者的架構思考。&lt;/p></description><content:encoded><![CDATA[<h2 id="為什麼導入聲明很重要">為什麼導入聲明很重要</h2>
<p>在程式開發中，導入聲明往往被視為技術細節，但它們實際上是<strong>架構文件的第一行</strong>。每個 import/require 都在告訴讀者：這個模組的依賴關係、系統的組織方式、以及設計者的架構思考。</p>
<p>相對路徑如 <code>import '../../../utils/helper.js'</code> 只是路徑的機械表達，而語意化路徑如 <code>import 'package:app/core/utils/helper.dart'</code> 則清楚傳達了模組的架構位置和責任。</p>
<h2 id="導入聲明的本質">導入聲明的本質</h2>
<h3 id="導入不是什麼">導入不是什麼</h3>
<p>導入聲明不是：</p>
<ul>
<li><strong>文件路徑的機械化表達</strong>：不是為了節省字元數而設計</li>
<li><strong>相對位置的簡化表示</strong>：不是為了避免長路徑而妥協</li>
<li><strong>開發便利性的工具</strong>：不是為了快速輸入而犧牲可讀性</li>
<li><strong>IDE 自動生成的結果</strong>：不是讓工具決定程式碼結構</li>
</ul>
<h3 id="導入是什麼">導入是什麼</h3>
<p>導入聲明是：</p>
<ul>
<li><strong>依賴關係的明確宣告</strong>：清楚表達模組間的連接</li>
<li><strong>程式碼架構的文件化</strong>：展示系統的組織結構</li>
<li><strong>依賴來源的即時說明</strong>：讓讀者立即理解依賴的性質</li>
<li><strong>架構意圖的表達方式</strong>：體現設計者對模組劃分的思考</li>
</ul>
<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="c1">// 正例：清楚表達架構層級
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span><span class="k">import</span> <span class="s1">&#39;package:book_overview_app/domains/library/entities/book.dart&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="k">import</span> <span class="s1">&#39;package:book_overview_app/core/errors/standard_error.dart&#39;</span><span class="p">;</span>
</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"><span class="c1">// 反例：隱藏架構關係
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1"></span><span class="k">import</span> <span class="s1">&#39;../entities/book.dart&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="k">import</span> <span class="s1">&#39;../../../core/errors/standard_error.dart&#39;</span><span class="p">;</span></span></span></code></pre></div><h3 id="第二原則依賴來源的即時識別">第二原則：依賴來源的即時識別</h3>
<p>從導入聲明立即理解依賴性質：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">// 從導入立即理解：這是 Library Domain 的核心實體
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span><span class="kr">import</span> <span class="p">{</span> <span class="nx">Book</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;@app/domains/library/entities/book&#39;</span><span class="p">;</span>
</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 class="c1">// 從導入立即理解：這是 Core 基礎設施
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"></span><span class="kr">import</span> <span class="p">{</span> <span class="nx">StandardError</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;@app/core/errors/standard-error&#39;</span><span class="p">;</span></span></span></code></pre></div><h3 id="第三原則禁用別名與妥協">第三原則：禁用別名與妥協</h3>
<p><strong>別名是程式設計不佳的象徵</strong>。當我們遇到重名衝突時，核心解決方案是重構和改善命名，而不是用別名掩蓋設計問題。</p>
<h4 id="為什麼禁用別名">為什麼禁用別名</h4>
<p>別名反映的根本問題：</p>
<ol>
<li><strong>命名不夠清晰明確</strong>：導致不同模組產生重名衝突</li>
<li><strong>架構設計缺陷</strong>：同一概念在不同領域使用相同名稱</li>
<li><strong>職責劃分不清</strong>：模組邊界和責任沒有明確定義</li>
<li><strong>技術債務累積</strong>：用別名掩蓋設計問題而非解決問題</li>
</ol>
<h4 id="錯誤的別名解決方案">錯誤的別名解決方案</h4>





<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">import</span> <span class="s1">&#39;package:app/domains/library/entities/book.dart&#39;</span> <span class="k">as</span> <span class="n">LibBook</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="k">import</span> <span class="s1">&#39;package:app/domains/search/entities/book.dart&#39;</span> <span class="k">as</span> <span class="n">SearchBook</span><span class="p">;</span>
</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"><span class="c1">// 使用時仍然模糊不清
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1"></span><span class="n">LibBook</span><span class="p">.</span><span class="n">Book</span> <span class="n">bookEntity</span> <span class="o">=</span> <span class="n">LibBook</span><span class="p">.</span><span class="n">Book</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="n">SearchBook</span><span class="p">.</span><span class="n">Book</span> <span class="n">searchResult</span> <span class="o">=</span> <span class="n">SearchBook</span><span class="p">.</span><span class="n">Book</span><span class="p">();</span></span></span></code></pre></div><h4 id="正確的重構解決方案">正確的重構解決方案</h4>





<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">import</span> <span class="s1">&#39;package:app/domains/library/entities/book.dart&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="k">import</span> <span class="s1">&#39;package:app/domains/search/entities/search_result.dart&#39;</span><span class="p">;</span>
</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"><span class="c1">// 使用時語意清楚，職責明確
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1"></span><span class="n">Book</span> <span class="n">libraryBook</span> <span class="o">=</span> <span class="n">Book</span><span class="p">();</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="n">SearchResult</span> <span class="n">searchData</span> <span class="o">=</span> <span class="n">SearchResult</span><span class="p">();</span></span></span></code></pre></div><h4 id="重構策略">重構策略</h4>
<p><strong>1. 重新審視命名責任</strong>：</p>
<ul>
<li>保留核心領域的概念名稱（如 Library Domain 的 Book）</li>
<li>重構其他領域的名稱為更精確的描述（如 Search Domain 的 SearchResult）</li>
</ul>
<p><strong>2. 領域邊界清晰化</strong>：</p>
<ul>
<li>根據職責重新命名類別和模組</li>
<li>確保每個名稱都有明確的領域歸屬</li>
</ul>
<p><strong>3. 架構重構優於別名妥協</strong>：</p>
<ul>
<li>禁用別名迫使開發者正視設計缺陷</li>
<li>推動更清晰的領域劃分</li>
<li>維護程式碼品質標準</li>
</ul>
<h2 id="跨語言實踐標準">跨語言實踐標準</h2>
<h3 id="dartflutter-package-系統">Dart/Flutter: Package 系統</h3>





<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">// Package 導入 + 完整路徑語意
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span><span class="k">import</span> <span class="s1">&#39;package:book_overview_app/domains/library/entities/book.dart&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="k">import</span> <span class="s1">&#39;package:book_overview_app/domains/search/services/api_service.dart&#39;</span><span class="p">;</span></span></span></code></pre></div><p><strong>實現機制</strong>：<code>pubspec.yaml</code> 定義 package 名稱，Dart 編譯器將 <code>package:</code> 映射到 <code>lib/</code> 目錄。</p>
<h3 id="nodejs-混合策略-v1-專案實踐">Node.js: 混合策略 (V1 專案實踐)</h3>
<p><strong>生產環境</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><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="kr">const</span> <span class="nx">BaseModule</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;./lifecycle/base-module&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="kr">const</span> <span class="nx">PageDomainCoordinator</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;./domains/page/page-domain-coordinator&#39;</span><span class="p">);</span></span></span></code></pre></div><p><strong>測試環境</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">// Jest moduleNameMapper 實現語意化
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="p">{</span> <span class="nx">ErrorCodes</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;src/core/errors/ErrorCodes&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="kr">const</span> <span class="nx">QualityAssessmentService</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;src/background/domains/data-management/services/quality-assessment-service.js&#39;</span><span class="p">);</span></span></span></code></pre></div><p><strong>Jest 配置關鍵</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">// tests/jest.config.js
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span><span class="nx">module</span><span class="p">.</span><span class="nx">exports</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">  <span class="nx">moduleNameMapper</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">    <span class="s1">&#39;^src/(.*)$&#39;</span><span class="o">:</span> <span class="s1">&#39;&lt;rootDir&gt;/src/$1&#39;</span><span class="p">,</span>           <span class="c1">// src/ 路徑語意化
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"></span>    <span class="s1">&#39;^@/(.*)$&#39;</span><span class="o">:</span> <span class="s1">&#39;&lt;rootDir&gt;/src/$1&#39;</span><span class="p">,</span>             <span class="c1">// @ 別名映射
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1"></span>    <span class="s1">&#39;^@tests/(.*)$&#39;</span><span class="o">:</span> <span class="s1">&#39;&lt;rootDir&gt;/tests/$1&#39;</span><span class="p">,</span>      <span class="c1">// 測試檔案語意化
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"></span>    <span class="s1">&#39;^@mocks/(.*)$&#39;</span><span class="o">:</span> <span class="s1">&#39;&lt;rootDir&gt;/tests/mocks/$1&#39;</span> <span class="c1">// Mock 檔案語意化
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h3 id="php-laravel-框架--composer">PHP Laravel: 框架 + Composer</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-php" data-lang="php"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="o">&lt;?</span><span class="nx">php</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1">// Laravel 的命名空間 + Composer Autoloader
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"></span><span class="k">namespace</span> <span class="nx">App\Domains\Library\Entities</span><span class="p">;</span>
</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"><span class="k">use</span> <span class="nx">App\Domains\Search\Services\ApiService</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">use</span> <span class="nx">App\Core\Errors\StandardError</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">use</span> <span class="nx">Illuminate\Database\Eloquent\Model</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">class</span> <span class="nc">Book</span> <span class="k">extends</span> <span class="nx">Model</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="c1">// 完整命名空間讓讀者立即理解依賴來源
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><h3 id="go-module-system">Go: Module System</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</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 class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="c1">// 外部依賴使用 module path</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="s">&#34;github.com/gin-gonic/gin&#34;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="s">&#34;gorm.io/gorm&#34;</span>
</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">    <span class="c1">// 內部模組使用完整 module path</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="s">&#34;book-overview-app/domains/library/entities&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="s">&#34;book-overview-app/domains/search/services&#34;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="s">&#34;book-overview-app/core/errors&#34;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="p">)</span></span></span></code></pre></div><h3 id="typescript-module-resolution">TypeScript: Module Resolution</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">// Module 導入 + 完整路徑語意
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span><span class="kr">import</span> <span class="p">{</span> <span class="nx">Book</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;@app/domains/library/entities/book&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">ApiService</span> <span class="p">}</span> <span class="kr">from</span> <span class="s1">&#39;@app/domains/search/services/api-service&#39;</span><span class="p">;</span></span></span></code></pre></div><h2 id="v1-專案無框架的成功實踐">V1 專案：無框架的成功實踐</h2>
<h3 id="為什麼-v1-可以不需要絕對路徑">為什麼 V1 可以不需要絕對路徑</h3>
<ol>
<li><strong>一致的目錄結構</strong>：所有模組都在 <code>/src/background/</code> 下，層級關係固定</li>
<li><strong>明確的相對路徑語意</strong>：<code>./</code> = 同級，<code>../</code> = 上一級，路徑語意清楚</li>
<li><strong>避免深層嵌套</strong>：最多 3-4 層目錄，相對路徑仍然可讀</li>
<li><strong>Jest 測試環境的語意化支援</strong>：透過配置實現測試檔案的語意化路徑</li>
</ol>
<h3 id="npm-test-vs-jest-直接執行的選擇">npm test vs Jest 直接執行的選擇</h3>
<p>V1 專案選擇 <strong>npm test</strong> 的原因：</p>
<ol>
<li><strong>一致性管理</strong>：統一的測試入口</li>
<li><strong>環境隔離</strong>：確保所有開發者使用相同配置</li>
<li><strong>工具鏈整合</strong>：可以執行測試前後的額外工作</li>
</ol>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">// package.json - 統一的測試入口
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span><span class="s2">&#34;scripts&#34;</span><span class="err">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">  <span class="nt">&#34;test&#34;</span><span class="p">:</span> <span class="s2">&#34;npm run test:core&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">  <span class="nt">&#34;test:core&#34;</span><span class="p">:</span> <span class="s2">&#34;jest tests/unit tests/integration&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">  <span class="nt">&#34;test:unit&#34;</span><span class="p">:</span> <span class="s2">&#34;jest tests/unit&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">  <span class="nt">&#34;test:integration&#34;</span><span class="p">:</span> <span class="s2">&#34;jest tests/integration&#34;</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h3 id="測試路徑語意化的實現機制">測試路徑語意化的實現機制</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">// Jest 如何解析語意化路徑：
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1">// 1. 測試檔案寫入: require(&#39;src/core/errors/ErrorCodes&#39;)
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1">// 2. Jest moduleNameMapper 攔截: &#39;^src/(.*)$&#39;: &#39;&lt;rootDir&gt;/src/$1&#39;
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1">// 3. 實際解析路徑: /project-root/src/core/errors/ErrorCodes.js
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1">// 4. Node.js 載入模組: 成功匯入 ErrorCodes
</span></span></span></code></pre></div><h2 id="語言特性對比">語言特性對比</h2>
<table>
  <thead>
      <tr>
          <th>語言</th>
          <th>實現機制</th>
          <th>優勢</th>
          <th>測試環境支援</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Dart</strong></td>
          <td>Package system</td>
          <td>編譯時解析，IDE 支援佳</td>
          <td>原生支援 package: 導入</td>
      </tr>
      <tr>
          <td><strong>Go</strong></td>
          <td>Module system</td>
          <td>強制語意化，無相對路徑</td>
          <td>測試檔案使用相同 module path</td>
      </tr>
      <tr>
          <td><strong>PHP Laravel</strong></td>
          <td>框架 + Composer</td>
          <td>自動載入，標準化目錄</td>
          <td>PHPUnit 自動載入命名空間</td>
      </tr>
      <tr>
          <td><strong>TypeScript</strong></td>
          <td>Module resolution</td>
          <td>彈性配置，工具支援</td>
          <td>Jest/Vitest 支援 path mapping</td>
      </tr>
      <tr>
          <td><strong>Node.js (V1)</strong></td>
          <td>相對路徑 + Jest 映射</td>
          <td>生產簡單，測試語意化</td>
          <td><strong>Jest moduleNameMapper 實現語意化</strong></td>
      </tr>
      <tr>
          <td><strong>Python</strong></td>
          <td>Package imports</td>
          <td>簡潔語法，標準化</td>
          <td>pytest 原生支援 package 導入</td>
      </tr>
  </tbody>
</table>
<h2 id="實踐選擇指南">實踐選擇指南</h2>
<h3 id="有框架的專案">有框架的專案</h3>
<ul>
<li>使用框架提供的模組系統</li>
<li>例如：Laravel、Angular、Next.js</li>
<li>測試環境通常自動繼承框架的模組解析</li>
</ul>
<h3 id="無框架的專案-如-v1-範例">無框架的專案 (如 V1 範例)</h3>
<ul>
<li><strong>生產環境</strong>：採用嚴格的目錄規範 + 相對路徑</li>
<li><strong>測試環境</strong>：使用 Jest moduleNameMapper 實現語意化</li>
<li><strong>關鍵優勢</strong>：生產簡單，測試語意化</li>
</ul>
<h3 id="混合策略專案">混合策略專案</h3>
<ul>
<li>生產環境使用簡單的相對路徑</li>
<li>測試環境透過工具配置實現語意化</li>
<li>適合輕量級 Node.js 專案</li>
</ul>
<h2 id="架構透明性的價值">架構透明性的價值</h2>
<h3 id="從導入理解系統設計">從導入理解系統設計</h3>





<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">// 1. 這是一個跨 Domain 的協調器
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1">// 2. 主要整合 Library、Import、Search 三個領域
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1">// 3. 使用 Core 的標準錯誤處理
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1">// 4. 依賴外部的 HTTP 套件
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">import</span> <span class="s1">&#39;package:http/http.dart&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">import</span> <span class="s1">&#39;package:book_overview_app/core/errors/standard_error.dart&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">import</span> <span class="s1">&#39;package:book_overview_app/domains/library/services/library_service.dart&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="k">import</span> <span class="s1">&#39;package:book_overview_app/domains/import/services/import_service.dart&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">import</span> <span class="s1">&#39;package:book_overview_app/domains/search/services/search_service.dart&#39;</span><span class="p">;</span></span></span></code></pre></div><h3 id="依賴方向的視覺化">依賴方向的視覺化</h3>





<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">// UI → Domain → Core
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1">// 沒有反向依賴，符合乾淨架構原則
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="k">import</span> <span class="s1">&#39;package:book_overview_app/core/interfaces/repository.dart&#39;</span><span class="p">;</span>           <span class="c1">// 向下依賴
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1"></span><span class="k">import</span> <span class="s1">&#39;package:book_overview_app/domains/library/entities/book.dart&#39;</span><span class="p">;</span>       <span class="c1">// 平行依賴
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"></span><span class="k">import</span> <span class="s1">&#39;package:book_overview_app/domains/library/value_objects/book_id.dart&#39;</span><span class="p">;</span> <span class="o">//</span> <span class="err">向下依賴</span></span></span></code></pre></div><h2 id="總結">總結</h2>
<p>Package 導入路徑語意化方法論的核心價值：</p>
<ol>
<li><strong>架構透明性</strong>：從導入立即理解系統結構</li>
<li><strong>維護便利性</strong>：減少理解和修改的認知負擔</li>
<li><strong>團隊協作</strong>：統一的導入風格提升溝通效率</li>
<li><strong>跨語言一致性</strong>：建立統一的程式碼組織哲學</li>
</ol>
<p>遵循這個方法論，程式碼將成為自說明的架構文件，每個導入聲明都清楚表達系統的設計意圖和模組關係。無論是有框架還是無框架的專案，都能找到適合的語意化導入策略。</p>
<p>V1 專案證明了即使在無框架環境下，透過嚴格的目錄規範和 Jest 測試配置，仍然可以實現生產環境簡潔、測試環境語意化的理想狀態。這為其他類似專案提供了寶貴的實踐參考。</p>
<h2 id="結論">結論</h2>
<p>這是架構透明化機制，讓每個導入聲明都成為架構的即時文件。</p>]]></content:encoded></item><item><title>AI 任務逃避偵測與預防三層防護方法論</title><link>https://tarrragon.github.io/blog/record/ai-%E4%BB%BB%E5%8B%99%E9%80%83%E9%81%BF%E5%81%B5%E6%B8%AC%E8%88%87%E9%A0%90%E9%98%B2%E4%B8%89%E5%B1%A4%E9%98%B2%E8%AD%B7%E6%96%B9%E6%B3%95%E8%AB%96/</link><pubDate>Sat, 20 Sep 2025 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/ai-%E4%BB%BB%E5%8B%99%E9%80%83%E9%81%BF%E5%81%B5%E6%B8%AC%E8%88%87%E9%A0%90%E9%98%B2%E4%B8%89%E5%B1%A4%E9%98%B2%E8%AD%B7%E6%96%B9%E6%B3%95%E8%AB%96/</guid><description>&lt;h2 id="背景與演進">背景與演進&lt;/h2>
&lt;p>在與 LLM AI 協作過程中，我們發現了一個關鍵問題：AI 面對複雜問題會產生逃避行為，包括簡化問題、延後處理、或使用臨時解決方案。&lt;/p></description><content:encoded><![CDATA[<h2 id="背景與演進">背景與演進</h2>
<p>在與 LLM AI 協作過程中，我們發現了一個關鍵問題：AI 面對複雜問題會產生逃避行為，包括簡化問題、延後處理、或使用臨時解決方案。</p>
<p>為了徹底解決這個問題，我們發展了<strong>三層防護機制</strong>：</p>
<ol>
<li><strong>第一層：AI 內建自檢</strong> - 在每次回應前強制執行檢查，從源頭預防逃避行為</li>
<li><strong>第二層：Hook 系統驗證</strong> - 透過自動化腳本進行事後驗證和持續監控</li>
<li><strong>第三層：修復模式</strong> - 發現問題時提供補救措施和修正指引</li>
</ol>
<p>這套方法論不只確保 AI 協作品質，也成為我們所有開發決策的指導原則。</p>
<h2 id="三層防護機制架構">三層防護機制架構</h2>
<h3 id="第一層ai-內建自檢機制">第一層：AI 內建自檢機制</h3>
<p>這是最重要的防線，AI 在生成任何回應前都必須執行以下強制檢查：</p>
<h4 id="30秒核心檢查清單">30秒核心檢查清單</h4>
<ol>
<li>
<p>三大鐵律檢查</p>
<ul>
<li>測試通過率鐵律：是否包含「測試失敗可接受」的思維？</li>
<li>永不放棄鐵律：是否想跳過、暫時處理、或延後任何問題？</li>
<li>架構債務零容忍鐵律：是否發現架構問題卻想稍後處理？</li>
</ul>
</li>
<li>
<p>逃避詞彙檢查</p>
<ul>
<li>中文禁用詞：「太複雜」「先將就」「暫時性修正」「症狀緩解」等</li>
<li>英文禁用詞：workaround, bypass, hack, quick fix 等</li>
<li>簡化妥協詞：「更簡單的方法」「簡化處理」「簡化測試」等</li>
</ul>
</li>
<li>
<p>品質標準檢查</p>
<ul>
<li>解決方案是完整的，還是妥協的？</li>
<li>是否在迴避根本問題？</li>
<li>回應是否體現專業工程師標準？</li>
</ul>
</li>
</ol>
<h4 id="檢查失敗處理">檢查失敗處理</h4>
<p>如果任何檢查項目失敗：</p>
<ol>
<li>立即停止當前回應生成</li>
<li>重新分析問題和解決方案</li>
<li>重新構思符合三大鐵律的回應</li>
<li>再次檢查直到通過所有項目</li>
</ol>
<h3 id="第二層hook-系統驗證">第二層：Hook 系統驗證</h3>
<p>自動化腳本在關鍵時刻執行檢查：</p>
<h4 id="觸發時機">觸發時機</h4>
<ul>
<li>
<p><strong>UserPromptSubmit</strong>: 每次用戶輸入時檢查工作流程合規性</p>
</li>
<li>
<p><strong>PostToolUse</strong>: 檔案編輯後檢查程式異味和品質</p>
</li>
<li>
<p><strong>PreToolUse</strong>: 工具使用前檢查是否有阻止狀態</p>
</li>
<li>
<p><strong>Stop</strong>: 回應完成後分析版本推進狀態</p>
</li>
</ul>
<h4 id="檢查項目">檢查項目</h4>
<ul>
<li>ESLint 錯誤偵測和追蹤</li>
<li>技術債務累積監控</li>
<li>任務逃避行為偵測</li>
<li>程式異味自動分析</li>
<li>效能指標監控</li>
</ul>
<h3 id="第三層修復模式">第三層：修復模式</h3>
<p>當前兩層發現問題時的補救機制：</p>
<h4 id="進入條件">進入條件</h4>
<ul>
<li>AI 自檢發現違規詞彙或思維</li>
<li>Hook 系統偵測到逃避行為</li>
<li>品質指標超過容忍閾值</li>
</ul>
<h4 id="修復流程">修復流程</h4>
<ol>
<li>停止所有開發活動</li>
<li>分析問題根因和影響範圍</li>
<li>制定具體的修正計劃</li>
<li>執行修正並驗證結果</li>
<li>記錄學習並更新防護機制</li>
</ol>
<h4 id="完成標準">完成標準</h4>
<ul>
<li>所有違規行為已修正</li>
<li>品質指標回到可接受範圍</li>
<li>問題根因已徹底解決</li>
<li>預防措施已建立</li>
</ul>
<h2 id="核心立場">核心立場</h2>
<h3 id="我們接受的計劃性延後">我們接受的：計劃性延後</h3>
<p>計劃性延後是正當的開發策略。我們接受並鼓勵以下形式的延後：</p>
<h4 id="版本規劃的延後">版本規劃的延後</h4>
<ul>
<li>v0.1 做基礎功能，v0.2 加進階功能 → <strong>正確</strong></li>
<li>明確標註「此功能於 v0.3 實作」→ <strong>正確</strong></li>
<li>每個版本有完整可用的交付物 → <strong>必要</strong></li>
</ul>
<h4 id="tdd-最小實現">TDD 最小實現</h4>
<ul>
<li>先通過測試，再優化效能 → <strong>正確</strong></li>
<li>實作最小功能，重構階段再改善 → <strong>正確</strong></li>
<li>為擴展預留介面，但不過度設計 → <strong>正確</strong></li>
</ul>
<h4 id="風險管理的優先級">風險管理的優先級</h4>
<ul>
<li>核心流程優先，邊緣案例延後 → <strong>合理</strong></li>
<li>安全問題優先，美觀問題延後 → <strong>必要</strong></li>
<li>已知範圍的問題，計劃性處理 → <strong>專業</strong></li>
</ul>
<h3 id="我們拒絕的逃避行為">我們拒絕的：逃避行為</h3>
<p>以下行為是逃避，必須立即糾正：</p>
<h4 id="模糊的拖延">模糊的拖延</h4>
<ul>
<li>「太複雜，之後再說」→ <strong>錯誤</strong></li>
<li>「暫時跳過」但無具體計劃 → <strong>錯誤</strong></li>
<li>「先將就用」沒有改善時程 → <strong>錯誤</strong></li>
</ul>
<h4 id="核心責任的迴避">核心責任的迴避</h4>
<ul>
<li>發現架構問題但繞過 → <strong>不可接受</strong></li>
<li>省略錯誤處理 → <strong>不可接受</strong></li>
<li>降低測試標準來通過 → <strong>不可接受</strong></li>
</ul>
<h4 id="債務的累積">債務的累積</h4>
<ul>
<li>TODO 沒有追蹤和時程 → <strong>失職</strong></li>
<li>臨時方案變永久 → <strong>技術腐敗</strong></li>
<li>問題擴散不處理 → <strong>專案危機</strong></li>
</ul>
<h3 id="完整違規詞彙對照表">完整違規詞彙對照表</h3>
<p>為了確保一致性執行，以下是完整的違規詞彙清單：</p>
<h4 id="品質妥協和逃避責任類">品質妥協和逃避責任類</h4>
<table>
  <thead>
      <tr>
          <th>中文違規詞彙</th>
          <th>英文違規詞彙</th>
          <th>違反原則</th>
          <th>正確替代</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>「太複雜」</td>
          <td>&ldquo;too complex&rdquo;, &ldquo;too complicated&rdquo;</td>
          <td>永不放棄鐵律</td>
          <td>「需要深度分析，讓我分解問題」</td>
      </tr>
      <tr>
          <td>「先將就」</td>
          <td>&ldquo;workaround&rdquo;, &ldquo;hack&rdquo;</td>
          <td>永不放棄鐵律</td>
          <td>「建立完整解決方案」</td>
      </tr>
      <tr>
          <td>「暫時性修正」</td>
          <td>&ldquo;temporary fix&rdquo;, &ldquo;quick fix&rdquo;</td>
          <td>架構債務零容忍</td>
          <td>「現在就正確實作」</td>
      </tr>
      <tr>
          <td>「症狀緩解」</td>
          <td>&ldquo;bypass&rdquo;</td>
          <td>架構債務零容忍</td>
          <td>「解決根本問題」</td>
      </tr>
      <tr>
          <td>「先這樣處理」</td>
          <td>&ldquo;ignore for now&rdquo;</td>
          <td>永不放棄鐵律</td>
          <td>「立即正確處理」</td>
      </tr>
      <tr>
          <td>「臨時解決方案」</td>
          <td>&ldquo;will fix later&rdquo;</td>
          <td>架構債務零容忍</td>
          <td>「永久性解決方案」</td>
      </tr>
  </tbody>
</table>
<h4 id="簡化妥協類">簡化妥協類</h4>
<table>
  <thead>
      <tr>
          <th>中文違規詞彙</th>
          <th>英文違規詞彙</th>
          <th>違反原則</th>
          <th>正確替代</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>「更簡單的方法」</td>
          <td>&ldquo;simpler approach&rdquo;, &ldquo;simpler way&rdquo;</td>
          <td>永不放棄鐵律</td>
          <td>「正確且完整的方法」</td>
      </tr>
      <tr>
          <td>「採用更簡單的方法」</td>
          <td>&ldquo;take the simpler approach&rdquo;</td>
          <td>永不放棄鐵律</td>
          <td>「採用正確的方法」</td>
      </tr>
      <tr>
          <td>「簡化處理」</td>
          <td>&ldquo;simplify&rdquo;</td>
          <td>永不放棄鐵律</td>
          <td>「完整處理」</td>
      </tr>
      <tr>
          <td>「簡單的處理方式」</td>
          <td>&ldquo;simpler method&rdquo;</td>
          <td>永不放棄鐵律</td>
          <td>「專業的處理方式」</td>
      </tr>
  </tbody>
</table>
<h4 id="測試品質妥協類">測試品質妥協類</h4>
<table>
  <thead>
      <tr>
          <th>中文違規詞彙</th>
          <th>英文違規詞彙</th>
          <th>違反原則</th>
          <th>正確替代</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>「簡化測試」</td>
          <td>&ldquo;simplify test&rdquo;, &ldquo;simplified test&rdquo;</td>
          <td>測試通過率鐵律</td>
          <td>「建立完整測試」</td>
      </tr>
      <tr>
          <td>「降低測試標準」</td>
          <td>&ldquo;lower test standard&rdquo;</td>
          <td>測試通過率鐵律</td>
          <td>「維持100%測試標準」</td>
      </tr>
      <tr>
          <td>「簡單測試就好」</td>
          <td>&ldquo;basic test only&rdquo;, &ldquo;simple test case&rdquo;</td>
          <td>測試通過率鐵律</td>
          <td>「全面測試覆蓋」</td>
      </tr>
      <tr>
          <td>「基本測試即可」</td>
          <td>&ldquo;minimal test&rdquo;</td>
          <td>測試通過率鐵律</td>
          <td>「完整測試套件」</td>
      </tr>
  </tbody>
</table>
<h4 id="發現問題但不解決類">發現問題但不解決類</h4>
<table>
  <thead>
      <tr>
          <th>中文違規詞彙</th>
          <th>英文違規詞彙</th>
          <th>違反原則</th>
          <th>正確替代</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>「發現問題但不處理」</td>
          <td>&ldquo;ignore the issue&rdquo;</td>
          <td>架構債務零容忍</td>
          <td>「發現問題立即解決」</td>
      </tr>
      <tr>
          <td>「架構問題先不管」</td>
          <td>&ldquo;architecture debt later&rdquo;</td>
          <td>架構債務零容忍</td>
          <td>「架構問題立即修正」</td>
      </tr>
      <tr>
          <td>「只加個 TODO」</td>
          <td>&ldquo;just add todo&rdquo;</td>
          <td>架構債務零容忍</td>
          <td>「立即實作解決方案」</td>
      </tr>
      <tr>
          <td>「問題太多先跳過」</td>
          <td>&ldquo;too many issues skip&rdquo;</td>
          <td>永不放棄鐵律</td>
          <td>「逐一解決所有問題」</td>
      </tr>
  </tbody>
</table>
<h4 id="程式碼修改逃避類">程式碼修改逃避類</h4>
<table>
  <thead>
      <tr>
          <th>中文違規詞彙</th>
          <th>英文違規詞彙</th>
          <th>違反原則</th>
          <th>正確替代</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>「註解掉」</td>
          <td>&ldquo;comment out&rdquo;</td>
          <td>架構債務零容忍</td>
          <td>「重構或移除」</td>
      </tr>
      <tr>
          <td>「停用功能」</td>
          <td>&ldquo;disable&rdquo;</td>
          <td>架構債務零容忍</td>
          <td>「修正後啟用」</td>
      </tr>
      <tr>
          <td>「暫時關閉」</td>
          <td>&ldquo;temporarily disable&rdquo;</td>
          <td>架構債務零容忍</td>
          <td>「立即修正並啟用」</td>
      </tr>
  </tbody>
</table>
<h4 id="模糊不精確詞彙類">模糊不精確詞彙類</h4>
<table>
  <thead>
      <tr>
          <th>中文違規詞彙</th>
          <th>英文違規詞彙</th>
          <th>違反原則</th>
          <th>正確替代</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>「智能」</td>
          <td>&ldquo;smart&rdquo;, &ldquo;intelligent&rdquo;</td>
          <td>精確性原則</td>
          <td>「規則比對」「條件判斷」「算法處理」</td>
      </tr>
      <tr>
          <td>「自動」(無具體描述)</td>
          <td>&ldquo;auto&rdquo; (without details)</td>
          <td>精確性原則</td>
          <td>「Hook腳本執行」「定時任務觸發」</td>
      </tr>
      <tr>
          <td>「優化」(無具體指標)</td>
          <td>&ldquo;optimize&rdquo; (without metrics)</td>
          <td>精確性原則</td>
          <td>「減少記憶體使用50%」「提升載入速度2倍」</td>
      </tr>
  </tbody>
</table>
<h2 id="處理原則">處理原則</h2>
<h3 id="面對複雜問題">面對複雜問題</h3>
<p>複雜不是逃避的理由。我們的處理方式：</p>
<ol>
<li><strong>分解</strong>：任何複雜問題都能分解為可管理的部分</li>
<li><strong>排序</strong>：依影響和依賴關係決定處理順序</li>
<li><strong>執行</strong>：逐步解決，每步都有可驗證的成果</li>
</ol>
<h3 id="意外狀況的決策">意外狀況的決策</h3>
<p>遇到預期外的技術挑戰時：</p>
<h4 id="立即解決的情況">立即解決的情況</h4>
<ul>
<li>阻塞其他開發 → <strong>立即處理</strong></li>
<li>影響資料完整性 → <strong>立即處理</strong></li>
<li>安全漏洞 → <strong>立即處理</strong></li>
</ul>
<h4 id="可以延後的情況">可以延後的情況</h4>
<ul>
<li>效能優化（功能正常）→ <strong>可計劃延後</strong></li>
<li>UI 美化（體驗可用）→ <strong>可計劃延後</strong></li>
<li>邊緣案例（主流程正常）→ <strong>可計劃延後</strong></li>
</ul>
<h4 id="需要重新評估的情況">需要重新評估的情況</h4>
<ul>
<li>暴露設計缺陷 → <strong>停下來重新設計</strong></li>
<li>依賴關係錯誤 → <strong>調整架構</strong></li>
<li>需求理解偏差 → <strong>重新確認需求</strong></li>
</ul>
<h2 id="ai-回應前自檢機制詳解">AI 回應前自檢機制詳解</h2>
<h3 id="30秒強制檢查流程">30秒強制檢查流程</h3>
<p><strong>AI 在生成任何回應前都必須完整執行以下檢查</strong>：</p>
<h4 id="第一階段三大鐵律檢查">第一階段：三大鐵律檢查</h4>





<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">1. 測試通過率鐵律檢查
</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">   要求：必須堅持 100% 測試通過率
</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">2. 永不放棄鐵律檢查
</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">   問：是否在尋找「更簡單」的方法來迴避複雜性？
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">   要求：必須面對並解決所有問題
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">3. 架構債務零容忍鐵律檢查
</span></span><span class="line"><span class="ln">12</span><span class="cl">   問：我是否發現架構問題卻想稍後處理？
</span></span><span class="line"><span class="ln">13</span><span class="cl">   問：是否想要使用臨時方案或權宜措施？
</span></span><span class="line"><span class="ln">14</span><span class="cl">   要求：必須立即修正架構問題</span></span></code></pre></div><h4 id="第二階段逃避詞彙檢查">第二階段：逃避詞彙檢查</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl">中文禁用詞彙掃描：
</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">□ 「發現問題但不處理」「架構問題先不管」「只加個 TODO」
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">□ 「智能」「自動」<span class="o">(</span>無具體描述<span class="o">)</span>「優化」<span class="o">(</span>無具體指標<span class="o">)</span>
</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">英文禁用詞彙掃描：
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">□ <span class="s2">&#34;workaround&#34;</span>, <span class="s2">&#34;bypass&#34;</span>, <span class="s2">&#34;hack&#34;</span>, <span class="s2">&#34;quick fix&#34;</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">□ <span class="s2">&#34;too complex&#34;</span>, <span class="s2">&#34;temporary fix&#34;</span>, <span class="s2">&#34;ignore for now&#34;</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">□ <span class="s2">&#34;simpler approach&#34;</span>, <span class="s2">&#34;simpler way&#34;</span>, <span class="s2">&#34;easier approach&#34;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">□ <span class="s2">&#34;simplify test&#34;</span>, <span class="s2">&#34;basic test only&#34;</span>, <span class="s2">&#34;minimal test&#34;</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">□ <span class="s2">&#34;comment out&#34;</span>, <span class="s2">&#34;disable&#34;</span>, <span class="s2">&#34;just add todo&#34;</span></span></span></code></pre></div><h4 id="第三階段品質標準檢查">第三階段：品質標準檢查</h4>





<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><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">   我是否在迴避根本問題？
</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">   我的回應體現專業工程師標準嗎？
</span></span><span class="line"><span class="ln">9</span><span class="cl">   6個月後我會為這個回應感到驕傲嗎？</span></span></code></pre></div><h3 id="檢查失敗立即修正流程">檢查失敗立即修正流程</h3>
<p><strong>如果任何檢查項目失敗</strong>：</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">1. 立即停止
</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">2. 重新分析
</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></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">3. 重新構思
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">   構思完全符合三大鐵律的回應
</span></span><span class="line"><span class="ln">10</span><span class="cl">   確保解決方案完整且專業
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">4. 再次檢查
</span></span><span class="line"><span class="ln">13</span><span class="cl">   重新執行完整的 30 秒檢查流程
</span></span><span class="line"><span class="ln">14</span><span class="cl">   直到通過所有檢查項目才能回應</span></span></code></pre></div><h3 id="關鍵情境標準處理">關鍵情境標準處理</h3>
<h4 id="測試失敗情境">測試失敗情境</h4>
<p><strong>觸發條件</strong>：任何測試失敗、通過率 &lt; 100%</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><span class="line"><span class="ln"> 2</span><span class="cl">1. 立即停止所有其他工作
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">2. 啟動深度分析 agent (lavender-interface-designer)
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">3. 制定並執行完整解決方案
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">4. 驗證達到 100% 通過率
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">5. 記錄解決過程到工作日誌
</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">嚴格禁止：
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">禁止：跳過失敗的測試
</span></span><span class="line"><span class="ln">10</span><span class="cl">禁止：認為 92% 通過率「已經很好」
</span></span><span class="line"><span class="ln">11</span><span class="cl">禁止：先處理其他「更重要」的工作
</span></span><span class="line"><span class="ln">12</span><span class="cl">禁止：採用臨時修補方案</span></span></code></pre></div><h4 id="複雜問題情境">複雜問題情境</h4>
<p><strong>觸發條件</strong>：問題看起來複雜、不知道解法</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><span class="line"><span class="ln"> 2</span><span class="cl">1. 承認複雜性，但拒絕放棄
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">2. 將複雜問題分解為可管理的子問題
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">3. 啟動相應的專業 agent
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">4. 按優先級逐一解決子問題
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">5. 記錄完整過程供未來參考
</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">嚴格禁止：
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">禁止：認為問題「太複雜」而放棄
</span></span><span class="line"><span class="ln">10</span><span class="cl">禁止：尋求「權宜方案」
</span></span><span class="line"><span class="ln">11</span><span class="cl">禁止：跳過問題處理其他工作
</span></span><span class="line"><span class="ln">12</span><span class="cl">禁止：採用「暫時性」解決方案</span></span></code></pre></div><h4 id="架構問題情境">架構問題情境</h4>
<p><strong>觸發條件</strong>：發現設計缺陷、技術債務、架構不一致</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><span class="line"><span class="ln"> 2</span><span class="cl">1. 立即停止功能開發
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">2. 評估影響範圍
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">3. 啟動重構專家 (cinnamon-refactor-owl)
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">4. 制定詳細修復計劃
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">5. 徹底修正後再繼續功能開發
</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">嚴格禁止：
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">禁止：認為架構問題「影響不大」
</span></span><span class="line"><span class="ln">10</span><span class="cl">禁止：想要「之後再處理」
</span></span><span class="line"><span class="ln">11</span><span class="cl">禁止：繼續在有問題的架構上開發
</span></span><span class="line"><span class="ln">12</span><span class="cl">禁止：採用簡單修補而非根本性解決</span></span></code></pre></div><h2 id="執行標準">執行標準</h2>
<h3 id="延後必須有記錄">延後必須有記錄</h3>
<p>每個延後決定都要：</p>
<ul>
<li>記錄在工作日誌</li>
<li>標註預計處理時間</li>
<li>說明延後的理由</li>
</ul>
<h3 id="定期檢視累積">定期檢視累積</h3>
<ul>
<li>Sprint 結束檢查延後項目</li>
<li>版本發布前清理技術債務</li>
<li>超過兩個版本的延後必須處理</li>
</ul>
<h3 id="品質底線不妥協">品質底線不妥協</h3>
<p>無論如何都不能延後的：</p>
<ul>
<li><strong>安全性</strong>：任何安全問題</li>
<li><strong>資料完整性</strong>：可能造成資料遺失或錯誤</li>
<li><strong>核心體驗</strong>：用戶主要使用路徑</li>
</ul>
<h2 id="判別範例">判別範例</h2>
<h3 id="正例正確的延後">正例：正確的延後</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">// TODO: v0.2 版本加入批次處理
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1">// 目前 v0.1 實作單筆處理
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">processItem</span><span class="p">(</span><span class="nx">item</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">  <span class="k">return</span> <span class="nx">processSingle</span><span class="p">(</span><span class="nx">item</span><span class="p">);</span> <span class="c1">// 完整功能，可延後優化
</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>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">// TODO: 錯誤處理太麻煩，先不做
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">riskyOperation</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">  <span class="k">return</span> <span class="nx">doOperation</span><span class="p">();</span> <span class="c1">// 缺少錯誤處理，不可接受
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><p>理由：核心責任缺失、沒有時程、影響穩定性。</p>
<h3 id="正例正確的最小實現">正例：正確的最小實現</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kr">class</span> <span class="nx">DataProcessor</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  <span class="nx">process</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="c1">// v0.1: 同步處理
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"></span>    <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">syncProcess</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  <span class="p">}</span>
</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 class="c1">// 預留介面給 v0.2 非同步處理
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span>  <span class="kr">async</span> <span class="nx">processAsync</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s1">&#39;v0.2 功能&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>理由：當前版本完整可用、為未來預留空間、有清楚邊界。</p>
<h3 id="反例錯誤的妥協">反例：錯誤的妥協</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><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="nx">test</span><span class="p">.</span><span class="nx">skip</span><span class="p">(</span><span class="s1">&#39;應該要處理邊界情況&#39;</span><span class="p">,</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">  <span class="c1">// 跳過困難的測試
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"></span><span class="p">});</span></span></span></code></pre></div><p>理由：降低品質標準、逃避問題、累積風險。</p>
<h2 id="文化與態度">文化與態度</h2>
<h3 id="我們的信念">我們的信念</h3>
<ul>
<li><strong>沒有解決不了的問題</strong>，只有還沒找到的方法</li>
<li><strong>延後是策略</strong>，逃避是失職</li>
<li><strong>技術債務必須管理</strong>，不是忽視</li>
</ul>
<h3 id="我們的承諾">我們的承諾</h3>
<ul>
<li>誠實面對技術挑戰</li>
<li>為決定負責並追蹤</li>
<li>維持專案的長期健康</li>
</ul>
<h3 id="我們的標準">我們的標準</h3>
<ul>
<li>每個延後都有明確計劃</li>
<li>每個問題都有解決方案</li>
<li>每個決定都能說明理由</li>
</ul>
<h2 id="執行保障三層防護機制完整架構">執行保障：三層防護機制完整架構</h2>
<h3 id="完整防護體系">完整防護體系</h3>
<p>為了確保這些原則被確實執行，我們建立了三層互補的防護機制：</p>
<h4 id="第一層ai-內建自檢主要防線">第一層：AI 內建自檢（主要防線）</h4>
<ul>
<li><strong>執行時機</strong>：每次回應前自動執行</li>
<li><strong>檢查範圍</strong>：三大鐵律、逃避詞彙、品質標準</li>
<li><strong>處理方式</strong>：檢查失敗立即停止回應生成，重新構思</li>
<li><strong>特色</strong>：源頭預防，即時阻止</li>
</ul>
<h4 id="第二層hook-系統驗證輔助驗證">第二層：Hook 系統驗證（輔助驗證）</h4>
<ul>
<li><strong>執行時機</strong>：用戶輸入、檔案編輯、工具使用前後</li>
<li><strong>檢查範圍</strong>：ESLint 錯誤、技術債務、程式異味</li>
<li><strong>處理方式</strong>：自動記錄問題、啟動 agents 處理</li>
<li><strong>特色</strong>：持續監控，事後驗證</li>
</ul>
<h4 id="第三層修復模式最後防線">第三層：修復模式（最後防線）</h4>
<ul>
<li><strong>觸發條件</strong>：前兩層發現違規行為或品質問題</li>
<li><strong>處理方式</strong>：進入修復模式，完成修正後才能繼續</li>
<li><strong>特色</strong>：強制修正，確保問題得到解決</li>
</ul>
<h3 id="三層協作機制">三層協作機制</h3>
<h4 id="正常工作流程">正常工作流程</h4>





<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">用戶輸入 → AI自檢 → 通過 → 生成回應 → Hook驗證 → 正常繼續</span></span></code></pre></div><h4 id="發現問題時">發現問題時</h4>





<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">AI自檢失敗 → 重新構思 → 再次檢查 → 通過後回應
</span></span><span class="line"><span class="ln">2</span><span class="cl">Hook檢測問題 → 記錄追蹤 → 必要時啟動修復模式
</span></span><span class="line"><span class="ln">3</span><span class="cl">修復模式 → 停止開發 → 修正問題 → 驗證完成 → 繼續開發</span></span></code></pre></div><h3 id="關鍵優勢">關鍵優勢</h3>
<h4 id="多重保障">多重保障</h4>
<ul>
<li>AI 自檢確保源頭品質</li>
<li>Hook 系統提供持續監控</li>
<li>修復模式確保問題解決</li>
</ul>
<h4 id="精確判斷">精確判斷</h4>
<ul>
<li>區分計劃性延後和逃避行為</li>
<li>識別技術文檔中的範例程式碼</li>
<li>理解上下文和開發階段</li>
</ul>
<h4 id="自動化執行">自動化執行</h4>
<ul>
<li>無需人工干預</li>
<li>一致性執行標準</li>
<li>即時問題發現和處理</li>
</ul>
<h3 id="防護機制效果">防護機制效果</h3>
<p>這套三層防護機制確保：</p>
<ul>
<li><strong>原則被一致執行</strong>：沒有例外情況</li>
<li><strong>問題被及時發現</strong>：多個時間點檢查</li>
<li><strong>品質不因壓力妥協</strong>：自動化強制執行</li>
<li><strong>逃避行為被根本預防</strong>：從思維層面攔截</li>
</ul>
<h2 id="結論從根本預防逃避行為的完整方法論">結論：從根本預防逃避行為的完整方法論</h2>
<p>這套方法論代表了我們在 AI 協作品質控制上的重大突破。我們從最初的 Hook 系統發展到現在的三層防護機制，實現了從<strong>事後修正</strong>到<strong>源頭預防</strong>的根本性轉變。</p>
<h3 id="核心成就">核心成就</h3>
<h4 id="從反應式到預防式">從反應式到預防式</h4>
<ul>
<li><strong>過去</strong>：Hook 系統發現問題後修正</li>
<li><strong>現在</strong>：AI 自檢在問題產生前攔截</li>
<li><strong>效果</strong>：逃避行為從源頭被消除</li>
</ul>
<h4 id="從單一防護到多層保障">從單一防護到多層保障</h4>
<ul>
<li><strong>第一層</strong>：AI 內建自檢（30秒強制檢查）</li>
<li><strong>第二層</strong>：Hook 系統驗證（持續監控）</li>
<li><strong>第三層</strong>：修復模式（強制修正）</li>
<li><strong>效果</strong>：任何逃避行為都無法逃脫檢測</li>
</ul>
<h4 id="從人工監督到自動化執行">從人工監督到自動化執行</h4>
<ul>
<li><strong>自檢機制</strong>：每次回應前自動執行</li>
<li><strong>檢查標準</strong>：三大鐵律 + 違規詞彙 + 品質標準</li>
<li><strong>處理流程</strong>：檢查失敗立即重新構思</li>
<li><strong>效果</strong>：零人工干預的品質保證</li>
</ul>
<h3 id="方法論價值">方法論價值</h3>
<p>這不只是技術工具，更是<strong>工作哲學的具體實現</strong>：</p>
<h4 id="永不妥協的品質標準">永不妥協的品質標準</h4>
<ul>
<li>計劃性延後是正當策略</li>
<li>逃避性拖延絕不容忍</li>
<li>100% 測試通過率是最低標準</li>
<li>架構問題必須立即修正</li>
</ul>
<h4 id="思維模式">思維模式</h4>
<ul>
<li>面對複雜問題不逃避，而是分解解決</li>
<li>發現問題立即處理，不推遲到未來</li>
<li>追求根本性解決，拒絕症狀緩解</li>
<li>維持長期視角，拒絕短期妥協</li>
</ul>
<h4 id="可持續的開發實踐">可持續的開發實踐</h4>
<ul>
<li>技術債務得到控制</li>
<li>程式碼品質持續提升</li>
<li>團隊標準保持一致</li>
<li>專案健康度不斷改善</li>
</ul>
<h3 id="執行承諾">執行承諾</h3>
<h4 id="這是要求不是建議這是標準不是理想">這是要求，不是建議。這是標準，不是理想</h4>
<ul>
<li>任何違反這些原則的行為，無論來自人類還是 AI，都會被三層防護機制檢測並要求修正</li>
<li>AI 每次回應前都必須通過 30 秒自檢</li>
<li>Hook 系統持續監控所有開發活動，確保品質標準</li>
<li>修復模式在發現問題時強制執行，直到問題徹底解決</li>
</ul>
<p>這套三層防護機制目標是：</p>
<ul>
<li><strong>可信賴的 AI 協作關係</strong></li>
<li><strong>可持續的品質保證體系</strong></li>
<li><strong>可擴展的開發方法論</strong></li>
<li><strong>可複製的成功模式</strong></li>
</ul>
<p>我們將持續改進這套機制，讓它成為所有 AI 協作專案的標準基礎設施，確保每個專案都能維持最高的品質標準。</p>
<p>這是品質保證系統，確保 AI 協作始終維持最高執行標準，而非放任品質下滑。</p>
<hr>]]></content:encoded></item><item><title>如何要求ai使用正確的格式撰寫md文件</title><link>https://tarrragon.github.io/blog/record/%E5%A6%82%E4%BD%95%E8%A6%81%E6%B1%82ai%E4%BD%BF%E7%94%A8%E6%AD%A3%E7%A2%BA%E7%9A%84%E6%A0%BC%E5%BC%8F%E6%92%B0%E5%AF%ABmd%E6%96%87%E4%BB%B6/</link><pubDate>Fri, 19 Sep 2025 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/%E5%A6%82%E4%BD%95%E8%A6%81%E6%B1%82ai%E4%BD%BF%E7%94%A8%E6%AD%A3%E7%A2%BA%E7%9A%84%E6%A0%BC%E5%BC%8F%E6%92%B0%E5%AF%ABmd%E6%96%87%E4%BB%B6/</guid><description>&lt;h2 id="what">what&lt;/h2>
&lt;p>ai 寫的md會有許多格式上的錯誤&lt;/p>
&lt;h2 id="why">why&lt;/h2>
&lt;p>生成式ai只負責產出內容，當下沒辦法判斷格式要求&lt;/p>
&lt;h2 id="how">how&lt;/h2>
&lt;p>所以我請AI找看看有沒有lint檢查工具可以跟ide一樣判斷這些問題
所以AI就執行了以下動作：&lt;/p>
&lt;ol>
&lt;li>檢查項目中是否有markdown linting工具&lt;/li>
&lt;li>嘗試運行常見的markdown lint工具&lt;/li>
&lt;li>如果沒有，可能需要安裝或使用一些基本的檢查&lt;/li>
&lt;/ol>
&lt;p>先檢查專案結構，看看是否有任何lint配置文件，然後嘗試運行一些常見的工具。&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Bash(which markdownlint)&lt;/p>
&lt;p>⎿  Error: markdownlint not found&lt;/p>
&lt;p>markdownlint 沒有安裝。讓我檢查是否有其他的markdown linting工具可用，或者可以通過npm安裝。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Bash(which mdl)&lt;/p>
&lt;p>⎿  Error: mdl not found&lt;/p>
&lt;p>看起來沒有預安裝的markdown
linting工具。檢查是否可以使用npm來安裝markdownlint，或者檢查項目中是否已經有相關配置。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Bash(which npm)&lt;/p>
&lt;p>好的，npm可用。可以使用npm安裝markdownlint-cli來檢查markdown文件的格式錯誤。&lt;/p>
&lt;p>現在會安裝markdownlint-cli來檢查markdown格式錯誤。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Bash(npm install -g markdownlint-cli)&lt;/p>
&lt;p>好的，markdownlint-cli已經安裝成功。現在我可以使用它來檢查項目中的markdown文件。讓我先檢查所有的m
arkdown文件。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Bash(find -name &amp;ldquo;*.md&amp;rdquo; -type f)&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>現在會使用markdownlint來檢查這些markdown文件中的格式錯誤。&lt;/p></description><content:encoded><![CDATA[<h2 id="what">what</h2>
<p>ai 寫的md會有許多格式上的錯誤</p>
<h2 id="why">why</h2>
<p>生成式ai只負責產出內容，當下沒辦法判斷格式要求</p>
<h2 id="how">how</h2>
<p>所以我請AI找看看有沒有lint檢查工具可以跟ide一樣判斷這些問題
所以AI就執行了以下動作：</p>
<ol>
<li>檢查項目中是否有markdown linting工具</li>
<li>嘗試運行常見的markdown lint工具</li>
<li>如果沒有，可能需要安裝或使用一些基本的檢查</li>
</ol>
<p>先檢查專案結構，看看是否有任何lint配置文件，然後嘗試運行一些常見的工具。</p>
<ul>
<li>
<p>Bash(which markdownlint)</p>
<p>⎿  Error: markdownlint not found</p>
<p>markdownlint 沒有安裝。讓我檢查是否有其他的markdown linting工具可用，或者可以通過npm安裝。</p>
</li>
<li>
<p>Bash(which mdl)</p>
<p>⎿  Error: mdl not found</p>
<p>看起來沒有預安裝的markdown
linting工具。檢查是否可以使用npm來安裝markdownlint，或者檢查項目中是否已經有相關配置。</p>
</li>
<li>
<p>Bash(which npm)</p>
<p>好的，npm可用。可以使用npm安裝markdownlint-cli來檢查markdown文件的格式錯誤。</p>
<p>現在會安裝markdownlint-cli來檢查markdown格式錯誤。</p>
</li>
<li>
<p>Bash(npm install -g markdownlint-cli)</p>
<p>好的，markdownlint-cli已經安裝成功。現在我可以使用它來檢查項目中的markdown文件。讓我先檢查所有的m
arkdown文件。</p>
</li>
<li>
<p>Bash(find  -name &ldquo;*.md&rdquo; -type f)</p>
</li>
</ul>
<p>現在會使用markdownlint來檢查這些markdown文件中的格式錯誤。</p>
]]></content:encoded></item><item><title>開始一個新專案的做法</title><link>https://tarrragon.github.io/blog/record/%E9%96%8B%E5%A7%8B%E4%B8%80%E5%80%8B%E6%96%B0%E5%B0%88%E6%A1%88%E7%9A%84%E5%81%9A%E6%B3%95/</link><pubDate>Fri, 19 Sep 2025 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/%E9%96%8B%E5%A7%8B%E4%B8%80%E5%80%8B%E6%96%B0%E5%B0%88%E6%A1%88%E7%9A%84%E5%81%9A%E6%B3%95/</guid><description>&lt;h2 id="前情提要">前情提要&lt;/h2>
&lt;p>我已經跟AI描述過我的需求，請AI建立了相關的需求文件，現在要開始建立新專案，接下來的步驟是一個實驗，完全依賴AI開展，所以我要記錄我開始的時候給AI的所有指令。&lt;/p>
&lt;h2 id="流程">流程&lt;/h2>
&lt;ol>
&lt;li>請先閱讀 @docs 裡面的需求文件，還有寫作規範&lt;/li>
&lt;/ol>
&lt;p>我們需要先在根目錄建立changelog.md，在doc目錄建立todo.md，以及work-logs 資料夾以及建立0.0.1的工作日誌，並且把專案的當前版本設定在0.0.1&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl"> 完成！已建立專案文件管理架構並設定版本為 0.0.1：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl"> &lt;span class="o">[&lt;/span>完成&lt;span class="o">]&lt;/span> CHANGELOG.md - 在根目錄，記錄使用者導向的版本更新
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl"> &lt;span class="o">[&lt;/span>完成&lt;span class="o">]&lt;/span> docs/todo.md - 開發規劃與進度追蹤
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl"> &lt;span class="o">[&lt;/span>完成&lt;span class="o">]&lt;/span> docs/work-logs/ - 資料夾已建立
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl"> &lt;span class="o">[&lt;/span>完成&lt;span class="o">]&lt;/span> docs/work-logs/v0.0.1-work-log.md - 詳細記錄開發過程
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl"> &lt;span class="o">[&lt;/span>完成&lt;span class="o">]&lt;/span> pubspec.yaml - 版本已更新為 0.0.1+1&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>2.接下來，請閱讀需求文件&lt;/p>
&lt;p>依照 @docs/event-driven-architecture.md&lt;/p>
&lt;p>使用use-case建立大流程的class&lt;/p>
&lt;p>用event系統建構文件說明會發明的事件流程&lt;/p>
&lt;p>我們的核心功能使用DDD系統建立domain，所以事件流程本身可以先以語意化的命名先假設不存在的函式，然後我們會再寫測試，加入這些還不存在的函式完成TDD第一步必定失敗的測試&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">完成！已建立：
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl"> &lt;span class="o">[&lt;/span>完成&lt;span class="o">]&lt;/span> 事件驅動架構設計文件 &lt;span class="o">(&lt;/span>docs/event-driven-architecture-design.md&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl"> - DDD 領域模型（Book、Library、ImportTask、ScanTask）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl"> - 完整事件流程（符合 MODULE.ACTION.STATE 格式）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl"> - 四個核心用例的事件流實作
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> - 事件優先級設定
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> &lt;span class="o">[&lt;/span>完成&lt;span class="o">]&lt;/span> 第一個失敗的 TDD 測試 &lt;span class="o">(&lt;/span>test/domain/book_test.dart&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> - &lt;span class="m">6&lt;/span> 個測試案例涵蓋核心功能
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> - 測試如預期失敗（Red Phase）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl"> - 所有 domain 層程式碼尚未實作&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>3.我手動安裝了我覺得需要的套件，並請ai參考規格文件補充還需要用的其他套件，然後再手動把ai安裝的套件版本更新到最新&lt;/p>
&lt;p>4.我請ai幫我處理了i18n相關的設定跟初始化&lt;/p>
&lt;p>5.我要求ai把 main底下的 themedata、初始化的行為、homepage的內容，從main.dart拆分出去，並且新增route設定以及riverpod的設定&lt;/p>
&lt;h2 id="最終專案的001-初始化成果">最終專案的0.0.1 初始化成果&lt;/h2>
&lt;figure>&lt;img src="https://tarrragon.github.io/blog/record/project_start_up/project0.0.1.png"
 alt="0.0.1的專案結構">
&lt;/figure>

&lt;hr>
&lt;h2 id="我直接貼上ai幫我做的工作日誌內容作為成果參考">我直接貼上ai幫我做的工作日誌內容作為成果參考&lt;/h2>
&lt;p>這是一個flutter專案，使用riverpod做狀態管理&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="cp">**專案版本**:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">v0.0.1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="cp">**開始日期**:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="ld">2025-09-19&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="cp">**開發階段**:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">專案初始化與基礎架構建立&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nn">---&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="專案初始化階段">專案初始化階段&lt;/h2>
&lt;h3 id="建立專案文件管理架構">建立專案文件管理架構&lt;/h3>
&lt;h4 id="問題發現過程">問題發現過程&lt;/h4>
&lt;p>在開始開發 Flutter 書庫管理 APP 前，需要建立完善的文件管理架構，確保開發過程的可追溯性和協作效率。根據需求文件和寫作規範的指引，需要建立三個核心文件系統。&lt;/p>
&lt;h4 id="決策邏輯">決策邏輯&lt;/h4>
&lt;p>根據 &lt;code>docs/document-responsibilities.md&lt;/code> 的文件責任分工原則：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>CHANGELOG.md&lt;/strong> - 面向使用者，記錄版本更新的功能變化&lt;/li>
&lt;li>&lt;strong>todo.md&lt;/strong> - 面向開發團隊，追蹤開發進度和規劃&lt;/li>
&lt;li>&lt;strong>工作日誌&lt;/strong> - 面向協作者，詳細記錄開發過程和技術決策&lt;/li>
&lt;/ol>
&lt;h4 id="實作過程">實作過程&lt;/h4>
&lt;h5 id="1-changelogmd-建立">1. CHANGELOG.md 建立&lt;/h5>
&lt;ul>
&lt;li>&lt;strong>位置&lt;/strong>: 根目錄&lt;/li>
&lt;li>&lt;strong>格式&lt;/strong>: 採用 Keep a Changelog 標準格式&lt;/li>
&lt;li>&lt;strong>分類&lt;/strong>: 使用 feat/fix/change 三大分類&lt;/li>
&lt;li>&lt;strong>內容&lt;/strong>: 記錄專案初始化的基本功能建立&lt;/li>
&lt;/ul>
&lt;h5 id="2-todomd-建立">2. todo.md 建立&lt;/h5>
&lt;ul>
&lt;li>&lt;strong>位置&lt;/strong>: docs 目錄&lt;/li>
&lt;li>&lt;strong>結構&lt;/strong>: 包含專案狀態、進行中項目、待完成項目、已完成項目&lt;/li>
&lt;li>&lt;strong>優先級&lt;/strong>: 明確標示高/中/低優先級&lt;/li>
&lt;li>&lt;strong>測試追蹤&lt;/strong>: 加入測試覆蓋進度章節&lt;/li>
&lt;/ul>
&lt;h5 id="3-work-logs-資料夾建立">3. work-logs 資料夾建立&lt;/h5>
&lt;ul>
&lt;li>&lt;strong>位置&lt;/strong>: docs/work-logs/&lt;/li>
&lt;li>&lt;strong>命名規則&lt;/strong>: vX.X.X-work-log.md&lt;/li>
&lt;li>&lt;strong>內容要求&lt;/strong>: 詳細記錄思考過程、決策邏輯、問題解決流程&lt;/li>
&lt;/ul>
&lt;h3 id="版本號設定">版本號設定&lt;/h3>
&lt;h4 id="選擇-001-的原因">選擇 0.0.1 的原因&lt;/h4>
&lt;ul>
&lt;li>專案剛啟動，處於最初期的基礎建設階段&lt;/li>
&lt;li>遵循語義化版本控制原則&lt;/li>
&lt;li>為後續的功能開發預留版本號空間&lt;/li>
&lt;/ul>
&lt;h4 id="版本號規劃">版本號規劃&lt;/h4>
&lt;ul>
&lt;li>&lt;strong>0.0.x&lt;/strong>: 基礎架構建立階段&lt;/li>
&lt;li>&lt;strong>0.1.x&lt;/strong>: 核心功能開發（資料模型、資料庫）&lt;/li>
&lt;li>&lt;strong>0.2.x&lt;/strong>: 匯入功能實作&lt;/li>
&lt;li>&lt;strong>0.3.x&lt;/strong>: ISBN 掃描功能&lt;/li>
&lt;li>&lt;strong>0.4.x&lt;/strong>: UI 介面開發&lt;/li>
&lt;li>&lt;strong>0.5.x&lt;/strong>: 測試與優化&lt;/li>
&lt;li>&lt;strong>1.0.0&lt;/strong>: 第一個正式發布版本&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="技術架構決策記錄">技術架構決策記錄&lt;/h2>
&lt;h3 id="flutter-專案結構規劃">Flutter 專案結構規劃&lt;/h3>
&lt;p>根據需求規格書建議的目錄結構，專案將採用 Feature-based 架構：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">lib/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl"> main.dart # 應用程式入口
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl"> app/ # 應用程式設定
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl"> core/ # 核心功能模組
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl"> database/ # SQLite 資料庫
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl"> models/ # 資料模型
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> services/ # 核心服務
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl"> utils/ # 工具函式
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl"> features/ # 功能模組
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl"> library/ # 書庫管理
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl"> import_export/ # 匯入匯出
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl"> isbn_scanner/ # ISBN 掃描
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl"> book_search/ # 書籍搜尋&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="技術棧確認">技術棧確認&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>前端框架&lt;/strong>: Flutter 3.x&lt;/li>
&lt;li>&lt;strong>狀態管理&lt;/strong>: Provider 或 Riverpod（待評估）&lt;/li>
&lt;li>&lt;strong>本地資料庫&lt;/strong>: SQLite + sqflite&lt;/li>
&lt;li>&lt;strong>網路請求&lt;/strong>: http 套件&lt;/li>
&lt;li>&lt;strong>相機功能&lt;/strong>: camera + mlkit_barcode_scanner&lt;/li>
&lt;li>&lt;strong>測試框架&lt;/strong>: flutter_test、mockito、integration_test&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="下一步計畫">下一步計畫&lt;/h2>
&lt;h3 id="立即任務">立即任務&lt;/h3>
&lt;ol>
&lt;li>更新 pubspec.yaml 設定版本號為 0.0.1&lt;/li>
&lt;li>開始實作 Phase 1: 核心資料模型與資料庫&lt;/li>
&lt;/ol>
&lt;h3 id="tdd-開發流程準備">TDD 開發流程準備&lt;/h3>
&lt;p>根據需求規格書的 TDD 待辦清單，下一步將開始：&lt;/p></description><content:encoded><![CDATA[<h2 id="前情提要">前情提要</h2>
<p>我已經跟AI描述過我的需求，請AI建立了相關的需求文件，現在要開始建立新專案，接下來的步驟是一個實驗，完全依賴AI開展，所以我要記錄我開始的時候給AI的所有指令。</p>
<h2 id="流程">流程</h2>
<ol>
<li>請先閱讀 @docs 裡面的需求文件，還有寫作規範</li>
</ol>
<p>我們需要先在根目錄建立changelog.md，在doc目錄建立todo.md，以及work-logs 資料夾以及建立0.0.1的工作日誌，並且把專案的當前版本設定在0.0.1</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"> 完成！已建立專案文件管理架構並設定版本為 0.0.1：
</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 class="o">[</span>完成<span class="o">]</span> CHANGELOG.md - 在根目錄，記錄使用者導向的版本更新
</span></span><span class="line"><span class="ln">4</span><span class="cl">  <span class="o">[</span>完成<span class="o">]</span> docs/todo.md - 開發規劃與進度追蹤
</span></span><span class="line"><span class="ln">5</span><span class="cl">  <span class="o">[</span>完成<span class="o">]</span> docs/work-logs/ - 資料夾已建立
</span></span><span class="line"><span class="ln">6</span><span class="cl">  <span class="o">[</span>完成<span class="o">]</span> docs/work-logs/v0.0.1-work-log.md - 詳細記錄開發過程
</span></span><span class="line"><span class="ln">7</span><span class="cl">  <span class="o">[</span>完成<span class="o">]</span> pubspec.yaml - 版本已更新為 0.0.1+1</span></span></code></pre></div><p>2.接下來，請閱讀需求文件</p>
<p>依照 @docs/event-driven-architecture.md</p>
<p>使用use-case建立大流程的class</p>
<p>用event系統建構文件說明會發明的事件流程</p>
<p>我們的核心功能使用DDD系統建立domain，所以事件流程本身可以先以語意化的命名先假設不存在的函式，然後我們會再寫測試，加入這些還不存在的函式完成TDD第一步必定失敗的測試</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl">完成！已建立：
</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 class="o">[</span>完成<span class="o">]</span> 事件驅動架構設計文件 <span class="o">(</span>docs/event-driven-architecture-design.md<span class="o">)</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  - DDD 領域模型（Book、Library、ImportTask、ScanTask）
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">  - 完整事件流程（符合 MODULE.ACTION.STATE 格式）
</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">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  <span class="o">[</span>完成<span class="o">]</span> 第一個失敗的 TDD 測試 <span class="o">(</span>test/domain/book_test.dart<span class="o">)</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">  - <span class="m">6</span> 個測試案例涵蓋核心功能
</span></span><span class="line"><span class="ln">11</span><span class="cl">  - 測試如預期失敗（Red Phase）
</span></span><span class="line"><span class="ln">12</span><span class="cl">  - 所有 domain 層程式碼尚未實作</span></span></code></pre></div><p>3.我手動安裝了我覺得需要的套件，並請ai參考規格文件補充還需要用的其他套件，然後再手動把ai安裝的套件版本更新到最新</p>
<p>4.我請ai幫我處理了i18n相關的設定跟初始化</p>
<p>5.我要求ai把 main底下的 themedata、初始化的行為、homepage的內容，從main.dart拆分出去，並且新增route設定以及riverpod的設定</p>
<h2 id="最終專案的001-初始化成果">最終專案的0.0.1 初始化成果</h2>
<figure><img src="/blog/record/project_start_up/project0.0.1.png"
    alt="0.0.1的專案結構">
</figure>

<hr>
<h2 id="我直接貼上ai幫我做的工作日誌內容作為成果參考">我直接貼上ai幫我做的工作日誌內容作為成果參考</h2>
<p>這是一個flutter專案，使用riverpod做狀態管理</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w"></span><span class="cp">**專案版本**:</span><span class="w"> </span><span class="l">v0.0.1</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w"></span><span class="cp">**開始日期**:</span><span class="w"> </span><span class="ld">2025-09-19</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w"></span><span class="cp">**開發階段**:</span><span class="w"> </span><span class="l">專案初始化與基礎架構建立</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w"></span><span class="nn">---</span></span></span></code></pre></div><h2 id="專案初始化階段">專案初始化階段</h2>
<h3 id="建立專案文件管理架構">建立專案文件管理架構</h3>
<h4 id="問題發現過程">問題發現過程</h4>
<p>在開始開發 Flutter 書庫管理 APP 前，需要建立完善的文件管理架構，確保開發過程的可追溯性和協作效率。根據需求文件和寫作規範的指引，需要建立三個核心文件系統。</p>
<h4 id="決策邏輯">決策邏輯</h4>
<p>根據 <code>docs/document-responsibilities.md</code> 的文件責任分工原則：</p>
<ol>
<li><strong>CHANGELOG.md</strong> - 面向使用者，記錄版本更新的功能變化</li>
<li><strong>todo.md</strong> - 面向開發團隊，追蹤開發進度和規劃</li>
<li><strong>工作日誌</strong> - 面向協作者，詳細記錄開發過程和技術決策</li>
</ol>
<h4 id="實作過程">實作過程</h4>
<h5 id="1-changelogmd-建立">1. CHANGELOG.md 建立</h5>
<ul>
<li><strong>位置</strong>: 根目錄</li>
<li><strong>格式</strong>: 採用 Keep a Changelog 標準格式</li>
<li><strong>分類</strong>: 使用 feat/fix/change 三大分類</li>
<li><strong>內容</strong>: 記錄專案初始化的基本功能建立</li>
</ul>
<h5 id="2-todomd-建立">2. todo.md 建立</h5>
<ul>
<li><strong>位置</strong>: docs 目錄</li>
<li><strong>結構</strong>: 包含專案狀態、進行中項目、待完成項目、已完成項目</li>
<li><strong>優先級</strong>: 明確標示高/中/低優先級</li>
<li><strong>測試追蹤</strong>: 加入測試覆蓋進度章節</li>
</ul>
<h5 id="3-work-logs-資料夾建立">3. work-logs 資料夾建立</h5>
<ul>
<li><strong>位置</strong>: docs/work-logs/</li>
<li><strong>命名規則</strong>: vX.X.X-work-log.md</li>
<li><strong>內容要求</strong>: 詳細記錄思考過程、決策邏輯、問題解決流程</li>
</ul>
<h3 id="版本號設定">版本號設定</h3>
<h4 id="選擇-001-的原因">選擇 0.0.1 的原因</h4>
<ul>
<li>專案剛啟動，處於最初期的基礎建設階段</li>
<li>遵循語義化版本控制原則</li>
<li>為後續的功能開發預留版本號空間</li>
</ul>
<h4 id="版本號規劃">版本號規劃</h4>
<ul>
<li><strong>0.0.x</strong>: 基礎架構建立階段</li>
<li><strong>0.1.x</strong>: 核心功能開發（資料模型、資料庫）</li>
<li><strong>0.2.x</strong>: 匯入功能實作</li>
<li><strong>0.3.x</strong>: ISBN 掃描功能</li>
<li><strong>0.4.x</strong>: UI 介面開發</li>
<li><strong>0.5.x</strong>: 測試與優化</li>
<li><strong>1.0.0</strong>: 第一個正式發布版本</li>
</ul>
<hr>
<h2 id="技術架構決策記錄">技術架構決策記錄</h2>
<h3 id="flutter-專案結構規劃">Flutter 專案結構規劃</h3>
<p>根據需求規格書建議的目錄結構，專案將採用 Feature-based 架構：</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">lib/
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  main.dart                    # 應用程式入口
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  app/                         # 應用程式設定
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  core/                        # 核心功能模組
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    database/                  # SQLite 資料庫
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    models/                    # 資料模型
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    services/                  # 核心服務
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    utils/                     # 工具函式
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">  features/                    # 功能模組
</span></span><span class="line"><span class="ln">10</span><span class="cl">    library/                   # 書庫管理
</span></span><span class="line"><span class="ln">11</span><span class="cl">    import_export/            # 匯入匯出
</span></span><span class="line"><span class="ln">12</span><span class="cl">    isbn_scanner/             # ISBN 掃描
</span></span><span class="line"><span class="ln">13</span><span class="cl">    book_search/              # 書籍搜尋</span></span></code></pre></div><h3 id="技術棧確認">技術棧確認</h3>
<ul>
<li><strong>前端框架</strong>: Flutter 3.x</li>
<li><strong>狀態管理</strong>: Provider 或 Riverpod（待評估）</li>
<li><strong>本地資料庫</strong>: SQLite + sqflite</li>
<li><strong>網路請求</strong>: http 套件</li>
<li><strong>相機功能</strong>: camera + mlkit_barcode_scanner</li>
<li><strong>測試框架</strong>: flutter_test、mockito、integration_test</li>
</ul>
<hr>
<h2 id="下一步計畫">下一步計畫</h2>
<h3 id="立即任務">立即任務</h3>
<ol>
<li>更新 pubspec.yaml 設定版本號為 0.0.1</li>
<li>開始實作 Phase 1: 核心資料模型與資料庫</li>
</ol>
<h3 id="tdd-開發流程準備">TDD 開發流程準備</h3>
<p>根據需求規格書的 TDD 待辦清單，下一步將開始：</p>
<ol>
<li><code>should_create_book_with_simplified_source_model</code></li>
<li><code>should_create_platform_lookup_table</code></li>
<li><code>should_link_book_to_platform_correctly</code></li>
</ol>
<h3 id="開發原則確認">開發原則確認</h3>
<ul>
<li>嚴格遵循 TDD 循環：Red → Green → Refactor</li>
<li>每次只實作最小可失敗測試</li>
<li>結構性變更與行為性變更分開提交</li>
<li>保持小而頻繁的 Commit</li>
</ul>
<hr>
<h2 id="協作交接資訊">協作交接資訊</h2>
<h3 id="環境設定提醒">環境設定提醒</h3>
<ul>
<li>Flutter SDK 需要 3.x 或以上版本</li>
<li>需要設定 iOS 和 Android 開發環境</li>
<li>建議使用 VS Code 或 Android Studio 開發</li>
</ul>
<h3 id="檔案位置速查">檔案位置速查</h3>
<ul>
<li>需求文件: <code>docs/app-requirements-spec.md</code></li>
<li>錯誤處理設計: <code>docs/app-error-handling-design.md</code></li>
<li>使用案例: <code>docs/app-use-cases.md</code></li>
<li>文件責任說明: <code>docs/document-responsibilities.md</code></li>
</ul>
<h3 id="git-工作流程">Git 工作流程</h3>
<ul>
<li>主分支: main</li>
<li>每個功能使用獨立分支開發</li>
<li>遵循 Conventional Commits 格式</li>
</ul>
<hr>
<h2 id="技術債務追蹤">技術債務追蹤</h2>
<ul>
<li>無（專案剛啟動）</li>
</ul>
<hr>
<h2 id="已知問題">已知問題</h2>
<ul>
<li>無（專案剛啟動）</li>
</ul>
<hr>
<h2 id="學習與改進">學習與改進</h2>
<ul>
<li>建立清晰的文件管理架構有助於後續開發的組織性</li>
<li>版本號從 0.0.1 開始給予充分的迭代空間</li>
<li>Feature-based 架構適合 Flutter 專案的模組化開發</li>
</ul>
<hr>
<h2 id="tdd-開發流程記錄">TDD 開發流程記錄</h2>
<h3 id="red-phase---第一個失敗測試-2025-09-19">Red Phase - 第一個失敗測試 (2025-09-19)</h3>
<h4 id="建立事件驅動架構設計">建立事件驅動架構設計</h4>
<p>完成 <code>docs/event-driven-architecture-design.md</code> 文件，包含：</p>
<ul>
<li>核心領域模型設計（Book、Library、ImportTask、ScanTask）</li>
<li>完整的事件流程定義（遵循 MODULE.ACTION.STATE 格式）</li>
<li>四個核心用例的事件流實作</li>
<li>事件優先級設定（URGENT、HIGH、NORMAL、LOW）</li>
</ul>
<h4 id="撰寫第一個失敗測試">撰寫第一個失敗測試</h4>
<p>建立 <code>test/domain/book_test.dart</code>，包含六個測試案例：</p>
<ol>
<li><code>should_create_book_with_simplified_source_model</code> - 測試書籍基本建立</li>
<li><code>should_create_platform_lookup_table</code> - 測試平台查詢表</li>
<li><code>should_link_book_to_platform_correctly</code> - 測試書籍與平台連結</li>
<li><code>should_handle_borrower_information_for_borrowed_books</code> - 測試借閱書籍資訊</li>
<li><code>should_validate_book_creation_with_required_fields</code> - 測試必填欄位驗證</li>
<li><code>should_support_physical_books_without_platform</code> - 測試實體書籍支援</li>
</ol>
<h4 id="測試執行結果">測試執行結果</h4>
<p>如預期，測試完全失敗，錯誤原因：</p>
<ul>
<li>所有 domain 層檔案都不存在</li>
<li>Book 實體類別尚未實作</li>
<li>值物件（BookId、BookTitle、BookCover、BookSource）尚未定義</li>
<li>列舉（SourceType、Platform）尚未建立</li>
<li>PlatformRegistry 服務尚未實作</li>
<li>例外類別（InvalidBookIdException、InvalidBookTitleException）尚未定義</li>
</ul>
<p>這符合 TDD 的 Red Phase，確認測試正確地失敗。</p>
<hr>
<h2 id="專案架構實作階段">專案架構實作階段</h2>
<h3 id="maindart-重構與模組化">Main.dart 重構與模組化</h3>
<p>根據用戶要求，將 main.dart 中的三個區塊進行分離：</p>
<h4 id="1-應用程式初始化分離">1. 應用程式初始化分離</h4>
<ul>
<li>建立 <code>lib/app/app.dart</code> 作為主應用程式類別</li>
<li>實作 ProviderScope 包裝和初始化邏輯</li>
<li>加入國際化初始設定: <code>Intl.defaultLocale = 'zh_TW'</code></li>
<li>建立應用程式狀態管理與錯誤處理</li>
</ul>
<h4 id="2-主題資料分離">2. 主題資料分離</h4>
<ul>
<li>建立 <code>lib/app/theme.dart</code> 獨立檔案</li>
<li>實作 Material 3 設計系統</li>
<li>建立書籍主題相關樣式：
<ul>
<li>BookStyles: 書籍封面、標題、作者樣式</li>
<li>Animations: 動畫時間常數</li>
<li>Spacing: 間距常數</li>
<li>BookSizes: 書籍封面尺寸規範</li>
</ul>
</li>
</ul>
<h4 id="3-路由系統建立">3. 路由系統建立</h4>
<ul>
<li>建立 <code>lib/app/routes.dart</code> 路由管理系統</li>
<li>實作命名路由與路由生成器</li>
<li>加入錯誤路由處理機制</li>
<li>建立「開發中」頁面佔位符</li>
</ul>
<h4 id="4-riverpod-服務配置">4. Riverpod 服務配置</h4>
<ul>
<li>建立 <code>lib/core/providers/app_providers.dart</code></li>
<li>建立 <code>lib/core/providers/ui_providers.dart</code></li>
<li>實作完整的事件驅動狀態管理系統</li>
<li>包含 EventBus、優先級事件處理、全域狀態管理</li>
</ul>
<h3 id="套件相依性整合">套件相依性整合</h3>
<h4 id="核心套件選擇">核心套件選擇</h4>
<p>根據書庫管理需求選擇以下套件：</p>
<ul>
<li><code>mobile_scanner: ^7.0.1</code> - ISBN 條碼掃描</li>
<li><code>sqflite: ^2.4.2</code> - SQLite 本地資料庫</li>
<li><code>flutter_riverpod: ^3.0.0</code> - 現代狀態管理</li>
<li><code>dio: ^5.9.0</code> - HTTP 網路請求</li>
<li><code>file_picker: ^10.3.3</code> - 檔案選擇（JSON 匯入）</li>
<li><code>cached_network_image: ^3.4.1</code> - 圖片快取</li>
<li><code>permission_handler: ^12.0.1</code> - 權限管理</li>
</ul>
<h4 id="相依性衝突解決">相依性衝突解決</h4>
<p>遇到 flutter_riverpod 3.0.0 與測試工具衝突：</p>
<ul>
<li><strong>問題</strong>: mockito 5.5.1 與 build_runner 2.8.0 因 analyzer 版本不相容</li>
<li><strong>解決方案</strong>:
<ul>
<li>降級 mockito 至 5.5.0 (支援 analyzer ≥6.0.0 &lt;7.0.0)</li>
<li>降級 build_runner 至 2.7.1 (避免版本衝突)</li>
<li>保持 flutter_riverpod 3.0.0 最新版本</li>
</ul>
</li>
</ul>
<h3 id="跨平台權限設定">跨平台權限設定</h3>
<h4 id="android-平台配置">Android 平台配置</h4>
<p>更新 <code>android/app/src/main/AndroidManifest.xml</code>：</p>
<ul>
<li>CAMERA 權限 (ISBN 掃描)</li>
<li>INTERNET 權限 (Google Books API)</li>
<li>READ_EXTERNAL_STORAGE 權限 (JSON 匯入)</li>
</ul>
<h4 id="ios-平台配置">iOS 平台配置</h4>
<p>更新 <code>ios/Runner/Info.plist</code>：</p>
<ul>
<li>NSCameraUsageDescription (相機使用說明)</li>
<li>UTExportedTypeDeclarations (JSON 檔案類型)</li>
<li>CFBundleDocumentTypes (文件支援)</li>
</ul>
<h4 id="macos-平台配置">macOS 平台配置</h4>
<p>更新 <code>macos/Runner/Info.plist</code>：</p>
<ul>
<li>LSApplicationCategoryType (生產力應用程式分類)</li>
<li>檔案存取權限設定</li>
</ul>
<h4 id="windows-平台配置">Windows 平台配置</h4>
<p>更新 <code>windows/runner/main.cpp</code>：</p>
<ul>
<li>應用程式標題設定為「書庫管理」</li>
</ul>
<h3 id="完整架構系統完成">完整架構系統完成</h3>
<h4 id="事件驅動架構">事件驅動架構</h4>
<ul>
<li>實作完整的 EventBus 系統</li>
<li>支援事件優先級 (URGENT, HIGH, NORMAL, LOW)</li>
<li>整合 Riverpod 狀態管理</li>
<li>建立全域錯誤處理機制</li>
</ul>
<h4 id="國際化支援">國際化支援</h4>
<ul>
<li>設定中文為預設語言 (<code>zh_TW</code>)</li>
<li>整合 flutter_localizations</li>
<li>準備多語言擴展架構</li>
</ul>
<h4 id="uiux-設計系統">UI/UX 設計系統</h4>
<ul>
<li>Material 3 設計規範</li>
<li>綠色書籍主題色彩</li>
<li>響應式設計支援</li>
<li>淺色/深色主題</li>
</ul>
<hr>
<h2 id="v001-最終總結">v0.0.1 最終總結</h2>
<h3 id="完成的核心功能">完成的核心功能</h3>
<ul>
<li><strong>專案基礎架構</strong> - 完整的 Flutter 跨平台專案</li>
<li><strong>事件驅動架構</strong> - 支援 MODULE.ACTION.STATE 模式</li>
<li><strong>狀態管理系統</strong> - Riverpod 3.0.0 完整整合</li>
<li><strong>國際化支援</strong> - 中文/英文多語言準備</li>
<li><strong>Material 3 設計</strong> - 書籍主題 UI 系統</li>
<li><strong>跨平台權限</strong> - Android/iOS/Windows/macOS 設定完成</li>
<li><strong>套件相依性</strong> - 所有核心套件整合無衝突</li>
<li><strong>文件管理</strong> - CHANGELOG、TODO、工作日誌系統建立</li>
</ul>
<h3 id="技術債務">技術債務</h3>
<ul>
<li>無 (專案初期，架構清晰)</li>
</ul>
<h3 id="下一階段目標">下一階段目標</h3>
<p><strong>v0.1.0</strong>: 開始 TDD Domain Layer 開發</p>
<ul>
<li>實作 Book 領域模型</li>
<li>建立第一個通過的測試</li>
<li>Red-Green-Refactor 循環開始</li>
</ul>
<h3 id="專案狀態">專案狀態</h3>
<ul>
<li><strong>版本</strong>: v0.0.1，基礎架構完成</li>
<li><strong>架構版本</strong>: α (Alpha) - 核心架構建立完成</li>
<li><strong>開發階段</strong>: 準備進入 TDD 開發</li>
<li><strong>整體進度</strong>: 基礎建設完成 (100%)，準備開始功能開發</li>
</ul>
<hr>
<p>本工作日誌記錄了 v0.0.1 版本的完整開發過程，為後續 TDD 開發提供完整的技術背景和決策依據。</p>
]]></content:encoded></item><item><title>開始一個新專案的測試規劃</title><link>https://tarrragon.github.io/blog/record/%E9%96%8B%E5%A7%8B%E4%B8%80%E5%80%8B%E6%96%B0%E5%B0%88%E6%A1%88%E7%9A%84%E6%B8%AC%E8%A9%A6%E8%A6%8F%E5%8A%83/</link><pubDate>Fri, 19 Sep 2025 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/%E9%96%8B%E5%A7%8B%E4%B8%80%E5%80%8B%E6%96%B0%E5%B0%88%E6%A1%88%E7%9A%84%E6%B8%AC%E8%A9%A6%E8%A6%8F%E5%8A%83/</guid><description>&lt;h2 id="測試金字塔設計順序">測試金字塔設計順序&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>測試層級&lt;/th>
 &lt;th>描述&lt;/th>
 &lt;th>設計順序&lt;/th>
 &lt;th>說明&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>整合測試 (E2E)&lt;/td>
 &lt;td>端到端測試&lt;/td>
 &lt;td>1&lt;/td>
 &lt;td>先定義完整的 Use Case 流程&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>事件測試&lt;/td>
 &lt;td>跨 Domain 交互測試&lt;/td>
 &lt;td>2&lt;/td>
 &lt;td>定義 Domain 間的事件交互&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>單元測試&lt;/td>
 &lt;td>單一功能測試&lt;/td>
 &lt;td>3&lt;/td>
 &lt;td>定義各 Domain 內部邏輯&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Widget 測試&lt;/td>
 &lt;td>UI 組件測試&lt;/td>
 &lt;td>4&lt;/td>
 &lt;td>定義 UI 組件行為&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="階段開發策略">階段開發策略&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>測試先行&lt;/strong>：先設計完整的測試金字塔&lt;/li>
&lt;li>&lt;strong>介面定義&lt;/strong>：通過測試定義各層級介面契約&lt;/li>
&lt;li>&lt;strong>交互設計&lt;/strong>：確定跨 Domain 的事件和資料流&lt;/li>
&lt;li>&lt;strong>實作延後&lt;/strong>：實際功能實作延後到 v0.2+ 階段&lt;/li>
&lt;/ul>
&lt;h3 id="v011">v0.1.1&lt;/h3>
&lt;ul>
&lt;li>建立多 Domain 基礎架構（Library, Import, Scanner, Search）&lt;/li>
&lt;li>建立基本的 Domain 實體和值物件（作為測試 Mock 基礎）&lt;/li>
&lt;li>完成 Library Domain 的基本測試案例&lt;/li>
&lt;/ul>
&lt;h3 id="v012-目標整合測試設計">v0.1.2 目標：整合測試設計&lt;/h3>
&lt;ul>
&lt;li>分析所有 Use Cases 並設計對應的整合測試&lt;/li>
&lt;li>定義書籍匯入完整流程測試&lt;/li>
&lt;li>定義 ISBN 掃描完整流程測試&lt;/li>
&lt;li>定義書庫管理完整流程測試&lt;/li>
&lt;li>定義搜尋功能完整流程測試&lt;/li>
&lt;/ul>
&lt;h3 id="v013-目標事件測試設計">v0.1.3 目標：事件測試設計&lt;/h3>
&lt;ul>
&lt;li>設計跨 Domain 事件交互測試&lt;/li>
&lt;li>定義 Import → Library 領域事件測試&lt;/li>
&lt;li>定義 Scanner → Library 領域事件測試&lt;/li>
&lt;li>定義 Search → Library 領域事件測試&lt;/li>
&lt;/ul>
&lt;h3 id="v014-目標單元測試完善">v0.1.4 目標：單元測試完善&lt;/h3>
&lt;ul>
&lt;li>為每個 Domain 設計完整單元測試&lt;/li>
&lt;li>定義 Repository 介面契約測試&lt;/li>
&lt;li>設計服務層介面測試&lt;/li>
&lt;/ul>
&lt;h3 id="v015-目標widget-測試設計">v0.1.5 目標：Widget 測試設計&lt;/h3>
&lt;ul>
&lt;li>設計 UI 組件行為測試&lt;/li>
&lt;li>定義使用者交互流程測試&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<h2 id="測試金字塔設計順序">測試金字塔設計順序</h2>
<table>
  <thead>
      <tr>
          <th>測試層級</th>
          <th>描述</th>
          <th>設計順序</th>
          <th>說明</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>整合測試 (E2E)</td>
          <td>端到端測試</td>
          <td>1</td>
          <td>先定義完整的 Use Case 流程</td>
      </tr>
      <tr>
          <td>事件測試</td>
          <td>跨 Domain 交互測試</td>
          <td>2</td>
          <td>定義 Domain 間的事件交互</td>
      </tr>
      <tr>
          <td>單元測試</td>
          <td>單一功能測試</td>
          <td>3</td>
          <td>定義各 Domain 內部邏輯</td>
      </tr>
      <tr>
          <td>Widget 測試</td>
          <td>UI 組件測試</td>
          <td>4</td>
          <td>定義 UI 組件行為</td>
      </tr>
  </tbody>
</table>
<h2 id="階段開發策略">階段開發策略</h2>
<ul>
<li><strong>測試先行</strong>：先設計完整的測試金字塔</li>
<li><strong>介面定義</strong>：通過測試定義各層級介面契約</li>
<li><strong>交互設計</strong>：確定跨 Domain 的事件和資料流</li>
<li><strong>實作延後</strong>：實際功能實作延後到 v0.2+ 階段</li>
</ul>
<h3 id="v011">v0.1.1</h3>
<ul>
<li>建立多 Domain 基礎架構（Library, Import, Scanner, Search）</li>
<li>建立基本的 Domain 實體和值物件（作為測試 Mock 基礎）</li>
<li>完成 Library Domain 的基本測試案例</li>
</ul>
<h3 id="v012-目標整合測試設計">v0.1.2 目標：整合測試設計</h3>
<ul>
<li>分析所有 Use Cases 並設計對應的整合測試</li>
<li>定義書籍匯入完整流程測試</li>
<li>定義 ISBN 掃描完整流程測試</li>
<li>定義書庫管理完整流程測試</li>
<li>定義搜尋功能完整流程測試</li>
</ul>
<h3 id="v013-目標事件測試設計">v0.1.3 目標：事件測試設計</h3>
<ul>
<li>設計跨 Domain 事件交互測試</li>
<li>定義 Import → Library 領域事件測試</li>
<li>定義 Scanner → Library 領域事件測試</li>
<li>定義 Search → Library 領域事件測試</li>
</ul>
<h3 id="v014-目標單元測試完善">v0.1.4 目標：單元測試完善</h3>
<ul>
<li>為每個 Domain 設計完整單元測試</li>
<li>定義 Repository 介面契約測試</li>
<li>設計服務層介面測試</li>
</ul>
<h3 id="v015-目標widget-測試設計">v0.1.5 目標：Widget 測試設計</h3>
<ul>
<li>設計 UI 組件行為測試</li>
<li>定義使用者交互流程測試</li>
</ul>
]]></content:encoded></item><item><title>大規模系統遷移方法論</title><link>https://tarrragon.github.io/blog/record/%E5%A4%A7%E8%A6%8F%E6%A8%A1%E7%B3%BB%E7%B5%B1%E9%81%B7%E7%A7%BB%E6%96%B9%E6%B3%95%E8%AB%96/</link><pubDate>Wed, 17 Sep 2025 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/record/%E5%A4%A7%E8%A6%8F%E6%A8%A1%E7%B3%BB%E7%B5%B1%E9%81%B7%E7%A7%BB%E6%96%B9%E6%B3%95%E8%AB%96/</guid><description>&lt;h2 id="方法論起源與核心問題">方法論起源與核心問題&lt;/h2>
&lt;h3 id="概念卡片a過度工程化危機的發現">【概念卡片A】過度工程化危機的發現&lt;/h3>
&lt;p>本方法論源自一次讓AI codereview的時候發現的設計問題，結果考慮需要大幅度重構，後來設計了一整套調整流程，並請AI記錄詳細的重構方法論，所以這文章不是我寫的&lt;/p></description><content:encoded><![CDATA[<h2 id="方法論起源與核心問題">方法論起源與核心問題</h2>
<h3 id="概念卡片a過度工程化危機的發現">【概念卡片A】過度工程化危機的發現</h3>
<p>本方法論源自一次讓AI codereview的時候發現的設計問題，結果考慮需要大幅度重構，後來設計了一整套調整流程，並請AI記錄詳細的重構方法論，所以這文章不是我寫的</p>
<h4 id="複雜度爆炸的警示信號錯誤分類過度細化問題">複雜度爆炸的警示信號：錯誤分類過度細化問題</h4>
<ul>
<li><strong>觀察</strong>：系統中存在 30+ 個錯誤代碼，每個功能模組都定義自己的錯誤類型</li>
<li><strong>問題</strong>：開發者需要記憶大量錯誤代碼，維護成本指數增長</li>
<li><strong>教訓</strong>：過度分類不能解決問題，反而創造新的複雜性</li>
</ul>
<h5 id="效能聲稱與現實的落差">效能聲稱與現實的落差</h5>
<ul>
<li><strong>觀察</strong>：運行時字串拼接在熱路徑中累積效能成本</li>
<li><strong>問題</strong>：樂觀的效能估計缺乏實際測量數據支撐</li>
<li><strong>教訓</strong>：效能改善必須基於可測量的真實數據</li>
</ul>
<h6 id="跨平台一致性缺失">跨平台一致性缺失</h6>
<ul>
<li><strong>觀察</strong>：不同平台使用不同的錯誤處理模式</li>
<li><strong>問題</strong>：開發者在平台間切換時面臨學習成本</li>
<li><strong>教訓</strong>：一致性是降低複雜度的關鍵因素</li>
</ul>
<h3 id="概念卡片b分散系統的混亂狀態">【概念卡片B】分散系統的混亂狀態</h3>
<h4 id="錯誤處理模式的分裂現象實際發現的不一致模式">錯誤處理模式的分裂現象：實際發現的不一致模式</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// 功能模組A：字串錯誤 (Sample Code)
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">moduleA_operation</span><span class="p">()</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="nx">failed</span><span class="p">)</span> <span class="k">throw</span> <span class="s1">&#39;OPERATION_FAILED&#39;</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><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="c1">// 功能模組B：自定義錯誤類別 (Sample Code)
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">moduleB_operation</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">failed</span><span class="p">)</span> <span class="k">throw</span> <span class="k">new</span> <span class="nx">CustomError</span><span class="p">(</span><span class="s1">&#39;MODULE_B_ERROR&#39;</span><span class="p">,</span> <span class="nx">details</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1">// 功能模組C：原生錯誤 (Sample Code)
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">moduleC_operation</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">failed</span><span class="p">)</span> <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s1">&#39;Generic error message&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1">// 結果：三種不同的錯誤處理方式在同一系統中並存
</span></span></span></code></pre></div><h5 id="維護成本的幾何級數增長測試複雜化">維護成本的幾何級數增長：測試複雜化</h5>
<p>每種錯誤模式需要不同的測試策略：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">// 測試模組A (Sample Code)
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span><span class="nx">expect</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="nx">moduleA</span><span class="p">()).</span><span class="nx">toThrow</span><span class="p">(</span><span class="s1">&#39;OPERATION_FAILED&#39;</span><span class="p">)</span>
</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 class="c1">// 測試模組B (Sample Code)
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"></span><span class="nx">expect</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="nx">moduleB</span><span class="p">()).</span><span class="nx">toThrow</span><span class="p">(</span><span class="nx">CustomError</span><span class="p">)</span>
</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 class="c1">// 測試模組C (Sample Code)
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="c1"></span><span class="nx">expect</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="nx">moduleC</span><span class="p">()).</span><span class="nx">toThrow</span><span class="p">(</span><span class="nb">Error</span><span class="p">)</span></span></span></code></pre></div><h6 id="序列化問題不同錯誤格式無法統一處理">序列化問題：不同錯誤格式無法統一處理</h6>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">// 跨系統傳輸時的序列化困境 (Sample Code)
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">serializeError</span><span class="p">(</span><span class="nx">error</span><span class="p">)</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="k">typeof</span> <span class="nx">error</span> <span class="o">===</span> <span class="s1">&#39;string&#39;</span><span class="p">)</span> <span class="k">return</span> <span class="p">{</span> <span class="nx">type</span><span class="o">:</span> <span class="s1">&#39;string&#39;</span><span class="p">,</span> <span class="nx">value</span><span class="o">:</span> <span class="nx">error</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="nx">error</span> <span class="k">instanceof</span> <span class="nx">CustomError</span><span class="p">)</span> <span class="k">return</span> <span class="nx">error</span><span class="p">.</span><span class="nx">toJSON</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">error</span> <span class="k">instanceof</span> <span class="nb">Error</span><span class="p">)</span> <span class="k">return</span> <span class="p">{</span> <span class="nx">message</span><span class="o">:</span> <span class="nx">error</span><span class="p">.</span><span class="nx">message</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">  <span class="c1">// 需要處理每種錯誤類型...
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><h3 id="概念卡片c系統性解決方案的設計發想">【概念卡片C】系統性解決方案的設計發想</h3>
<h4 id="從單點修復到整體重構的思維轉變錯誤分析">從單點修復到整體重構的思維轉變：錯誤分析</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 我們曾經嘗試的方法 (Sample Code)</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># 1. 逐個修復各 UC 的錯誤處理 → 不一致狀態持續存在</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"># 2. 建立新的 ErrorCodes → 與舊系統並存造成更大混亂</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 3. 強制統一標準 → 開發阻力大，容易半途而廢</span></span></span></code></pre></div><h5 id="系統性重構的核心洞察">系統性重構的核心洞察</h5>
<blockquote>
<p>分散的問題需要統一的解決方案。局部最佳化往往導致全域最差化。</p></blockquote>
<h6 id="雙軌並行的過渡策略橋接模式的創新設計">雙軌並行的過渡策略：橋接模式的創新設計</h6>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// 統一錯誤處理橋接器 (Sample Code)
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="kr">class</span> <span class="nx">ErrorSystemBridge</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="kr">static</span> <span class="nx">TRANSITION_MODES</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="nx">LEGACY_COMPATIBLE</span><span class="o">:</span> <span class="s1">&#39;legacy_first&#39;</span><span class="p">,</span>    <span class="c1">// 向後相容優先
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span>    <span class="nx">MODERN_PREFERRED</span><span class="o">:</span> <span class="s1">&#39;modern_first&#39;</span><span class="p">,</span>     <span class="c1">// 新系統優先
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span>    <span class="nx">DUAL_VALIDATION</span><span class="o">:</span> <span class="s1">&#39;parallel_check&#39;</span><span class="p">,</span>    <span class="c1">// 雙系統驗證
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"></span>    <span class="nx">GRADUAL_MIGRATION</span><span class="o">:</span> <span class="s1">&#39;step_by_step&#39;</span>     <span class="c1">// 逐步遷移
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="kr">static</span> <span class="nx">handleError</span><span class="p">(</span><span class="nx">error</span><span class="p">,</span> <span class="nx">mode</span> <span class="o">=</span> <span class="s1">&#39;GRADUAL_MIGRATION&#39;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="c1">// 核心創新：同時支援舊新系統，確保零中斷遷移
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"></span>    <span class="kr">const</span> <span class="nx">legacyFormat</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">toLegacyFormat</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="kr">const</span> <span class="nx">modernFormat</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">toModernFormat</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">selectByMode</span><span class="p">(</span><span class="nx">legacyFormat</span><span class="p">,</span> <span class="nx">modernFormat</span><span class="p">,</span> <span class="nx">mode</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h3 id="概念卡片d適配器模式的精確轉換">【概念卡片D】適配器模式的精確轉換</h3>
<h4 id="零語意損失的錯誤映射功能模組專用適配器設計">零語意損失的錯誤映射：功能模組專用適配器設計</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// 模組特化適配器範例 (Sample Code)
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="kr">class</span> <span class="nx">ModuleErrorAdapter</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="kr">static</span> <span class="nx">ERROR_MAPPING</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="c1">// 精確映射：每個舊錯誤對應明確的新類型
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span>    <span class="s1">&#39;OLD_VALIDATION_ERROR&#39;</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">      <span class="nx">newType</span><span class="o">:</span> <span class="s1">&#39;VALIDATION_ERROR&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">      <span class="nx">severity</span><span class="o">:</span> <span class="s1">&#39;MODERATE&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">      <span class="nx">recovery</span><span class="o">:</span> <span class="s1">&#39;USER_INPUT_REQUIRED&#39;</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="s1">&#39;OLD_NETWORK_TIMEOUT&#39;</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">      <span class="nx">newType</span><span class="o">:</span> <span class="s1">&#39;TIMEOUT_ERROR&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">      <span class="nx">severity</span><span class="o">:</span> <span class="s1">&#39;HIGH&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">      <span class="nx">recovery</span><span class="o">:</span> <span class="s1">&#39;AUTOMATIC_RETRY&#39;</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">
</span></span><span class="line"><span class="ln">17</span><span class="cl">  <span class="kr">static</span> <span class="nx">convertError</span><span class="p">(</span><span class="nx">oldError</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="kr">const</span> <span class="nx">mapping</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">ERROR_MAPPING</span><span class="p">[</span><span class="nx">oldError</span><span class="p">.</span><span class="nx">code</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">mapping</span><span class="p">)</span> <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s1">&#39;Unknown error type&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl">    <span class="c1">// &lt;1ms 轉換目標，保證熱路徑效能
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="c1"></span>    <span class="k">return</span> <span class="k">new</span> <span class="nx">StandardError</span><span class="p">(</span><span class="nx">mapping</span><span class="p">.</span><span class="nx">newType</span><span class="p">,</span> <span class="nx">oldError</span><span class="p">.</span><span class="nx">message</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">      <span class="nx">severity</span><span class="o">:</span> <span class="nx">mapping</span><span class="p">.</span><span class="nx">severity</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">      <span class="nx">recovery</span><span class="o">:</span> <span class="nx">mapping</span><span class="p">.</span><span class="nx">recovery</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">      <span class="nx">originalCode</span><span class="o">:</span> <span class="nx">oldError</span><span class="p">.</span><span class="nx">code</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">    <span class="p">})</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h3 id="概念卡片e方法論驗證與量化成果">【概念卡片E】方法論驗證與量化成果</h3>
<h4 id="可測量的改善指標系統複雜度降低">可測量的改善指標：系統複雜度降低</h4>
<ul>
<li>錯誤類型：30+ → 15 個核心類型 (50% 減少)</li>
<li>測試案例：分散式 → 607 個統一測試 (100% 通過率)</li>
<li>開發者學習成本：多套規範 → 單一標準</li>
</ul>
<h5 id="效能實際改善">效能實際改善</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">// 效能基準測試結果 (Sample Code)
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">performanceMetrics</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">  <span class="nx">errorCreationSpeed</span><span class="o">:</span> <span class="s1">&#39;2-10x faster&#39;</span><span class="p">,</span>      <span class="c1">// 錯誤建立速度
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"></span>  <span class="nx">memoryUsage</span><span class="o">:</span> <span class="s1">&#39;35-40% reduction&#39;</span><span class="p">,</span>         <span class="c1">// 記憶體使用減少
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"></span>  <span class="nx">serializationTime</span><span class="o">:</span> <span class="s1">&#39;&lt;1ms per error&#39;</span><span class="p">,</span>     <span class="c1">// 序列化時間
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1"></span>  <span class="nx">crossPlatformConsistency</span><span class="o">:</span> <span class="s1">&#39;100%&#39;</span>        <span class="c1">// 跨平台一致性
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><h6 id="維護成本量化">維護成本量化</h6>
<ul>
<li>程式碼重複：消除 14 個重複的錯誤處理模式</li>
<li>文檔維護：統一 API 文檔，減少 60% 維護工作量</li>
<li>新人上手：學習時間從 2 週縮短到 3 天</li>
</ul>
<h6 id="方法論的核心洞察驗證的設計原則">方法論的核心洞察：驗證的設計原則</h6>
<ol>
<li><strong>統一性優於客製化</strong>：一致的介面比特殊需求更重要</li>
<li><strong>測量優於估計</strong>：真實數據比理論分析更可靠</li>
<li><strong>漸進優於激進</strong>：可控的變更比一次性重寫更安全</li>
<li><strong>自動化優於手工</strong>：工具化流程比人工操作更可靠</li>
</ol>
<h6 id="可複製的成功模式">可複製的成功模式</h6>
<p>本方法論已在實際專案中驗證，具備跨專案、跨領域的適用性。關鍵在於將【概念卡片A-E】的思維模式系統性地應用到任何大規模重構場景中。</p>
<hr>
<h2 id="方法論核心架構五大安全支柱與風險預防機制">方法論核心架構：五大安全支柱與風險預防機制</h2>
<h3 id="支柱一漸進式風險控制---避免系統性崩潰">支柱一：漸進式風險控制 - 避免系統性崩潰</h3>
<p><strong>核心問題：</strong> 「如何防止小問題演變成系統性災難？」</p>
<h4 id="風險失控的常見模式與預防模式1雪崩效應">風險失控的常見模式與預防：模式1雪崩效應</h4>
<ul>
<li>
<p><strong>發生條件</strong>：高耦合系統中的單點故障</p>
</li>
<li>
<p><strong>危險信號</strong>：修改一個檔案需要同時修改10+個其他檔案</p>
</li>
<li>
<p><strong>預防機制</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><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">npm run migration:analyze --mode<span class="o">=</span>dependency_impact</span></span></code></pre></div></li>
</ul>
<h5 id="模式2狀態不一致積累">模式2：狀態不一致積累</h5>
<ul>
<li>
<p><strong>發生條件</strong>：部分遷移完成，系統處於中間狀態</p>
</li>
<li>
<p><strong>危險信號</strong>：舊新系統並存，但沒有明確的狀態管理</p>
</li>
<li>
<p><strong>預防機制</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><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">npm run migration:validate --check<span class="o">=</span>consistency</span></span></code></pre></div></li>
</ul>
<h6 id="模式3回滾不可能">模式3：回滾不可能</h6>
<ul>
<li>
<p><strong>發生條件</strong>：單向變更，無法逆轉操作</p>
</li>
<li>
<p><strong>危險信號</strong>：資料結構變更、API破壞性變更</p>
</li>
<li>
<p><strong>預防機制</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><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">npm run migration:prepare-rollback</span></span></code></pre></div></li>
</ul>
<h3 id="支柱二自動化優先原則---避免人為錯誤">支柱二：自動化優先原則 - 避免人為錯誤</h3>
<p><strong>核心問題：</strong> 「如何防止人為疏忽導致的致命錯誤？」</p>
<h4 id="人為錯誤的危險模式與自動化防護錯誤模式1模式識別失誤">人為錯誤的危險模式與自動化防護：錯誤模式1模式識別失誤</h4>
<ul>
<li>
<p><strong>風險</strong>：手工識別遺漏關鍵程式碼模式</p>
</li>
<li>
<p><strong>案例</strong>：遺漏錯誤處理語句，導致異常未捕獲</p>
</li>
<li>
<p><strong>自動化防護</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><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">npm run migration:scan --pattern<span class="o">=</span><span class="s2">&#34;throw new Error&#34;</span></span></span></code></pre></div></li>
</ul>
<h5 id="錯誤模式2變更生成不一致">錯誤模式2：變更生成不一致</h5>
<ul>
<li>
<p><strong>風險</strong>：手工修改時邏輯不一致</p>
</li>
<li>
<p><strong>案例</strong>：A檔案使用新錯誤格式，B檔案仍用舊格式</p>
</li>
<li>
<p><strong>自動化防護</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><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">npm run migration:convert --mode<span class="o">=</span>auto --verify-consistency</span></span></code></pre></div></li>
</ul>
<h6 id="錯誤模式3驗證覆蓋不足">錯誤模式3：驗證覆蓋不足</h6>
<ul>
<li>
<p><strong>風險</strong>：手工驗證遺漏邊界情況</p>
</li>
<li>
<p><strong>案例</strong>：正常流程驗證通過，但異常流程未測試</p>
</li>
<li>
<p><strong>自動化防護</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><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">npm run migration:validate --comprehensive</span></span></code></pre></div></li>
</ul>
<h3 id="支柱三相容性橋接設計---避免破壞性變更">支柱三：相容性橋接設計 - 避免破壞性變更</h3>
<p><strong>核心問題：</strong> 「如何防止新舊系統整合時的災難性故障？」</p>
<h4 id="相容性失敗的危險模式與預防策略">相容性失敗的危險模式與預防策略</h4>
<h4 id="危險模式1語意漂移-semantic-drift">危險模式1：語意漂移 (Semantic Drift)</h4>
<ul>
<li>
<p><strong>風險</strong>：相同介面在新舊系統中行為不同</p>
</li>
<li>
<p><strong>症狀</strong>：測試通過但線上行為異常</p>
</li>
<li>
<p><strong>預防策略</strong>：包裝器模式 + 行為驗證</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><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">npm run migration:verify-semantics --compare-behaviors</span></span></code></pre></div></li>
</ul>
<h4 id="-實作範例包裝器模式-wrapper-pattern">🛠 實作範例：包裝器模式 (Wrapper Pattern)</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// src/core/migration/StandardErrorWrapper.js (Sample Code)
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="kr">class</span> <span class="nx">StandardErrorWrapper</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="kr">static</span> <span class="nx">MIGRATION_MODES</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="nx">LEGACY_ONLY</span><span class="o">:</span> <span class="s1">&#39;legacy_only&#39;</span><span class="p">,</span>      <span class="c1">// 只使用舊系統
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span>    <span class="nx">WRAPPER_MODE</span><span class="o">:</span> <span class="s1">&#39;wrapper_mode&#39;</span><span class="p">,</span>    <span class="c1">// 包裝器模式（預設）
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span>    <span class="nx">DUAL_MODE</span><span class="o">:</span> <span class="s1">&#39;dual_mode&#39;</span><span class="p">,</span>          <span class="c1">// 雙重系統並行
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"></span>    <span class="nx">ERRORCODES_ONLY</span><span class="o">:</span> <span class="s1">&#39;errorcodes_only&#39;</span> <span class="c1">// 只使用新系統
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="c1">// 向後相容的 StandardError 介面
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"></span>  <span class="nx">constructor</span><span class="p">(</span><span class="nx">code</span><span class="p">,</span> <span class="nx">message</span><span class="p">,</span> <span class="nx">details</span> <span class="o">=</span> <span class="p">{})</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">mode</span> <span class="o">=</span> <span class="nx">config</span><span class="p">.</span><span class="nx">migrationMode</span> <span class="o">||</span> <span class="s1">&#39;wrapper_mode&#39;</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="c1">// 安全機制：保持舊介面不變
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="c1"></span>    <span class="k">this</span><span class="p">.</span><span class="nx">code</span> <span class="o">=</span> <span class="nx">code</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">message</span> <span class="o">=</span> <span class="nx">message</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">details</span> <span class="o">=</span> <span class="nx">details</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="c1">// 內部轉換：映射到新的 ErrorCodes 系統
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="c1"></span>    <span class="k">this</span><span class="p">.</span><span class="nx">errorCodesInstance</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">_convertToErrorCodes</span><span class="p">(</span><span class="nx">code</span><span class="p">,</span> <span class="nx">message</span><span class="p">,</span> <span class="nx">details</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">
</span></span><span class="line"><span class="ln">23</span><span class="cl">  <span class="nx">_convertToErrorCodes</span><span class="p">(</span><span class="nx">code</span><span class="p">,</span> <span class="nx">message</span><span class="p">,</span> <span class="nx">details</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="c1">// 安全轉換邏輯，確保語意一致性
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="c1"></span>    <span class="kr">const</span> <span class="nx">mapping</span> <span class="o">=</span> <span class="nx">ErrorMapping</span><span class="p">.</span><span class="nx">getMapping</span><span class="p">(</span><span class="nx">code</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">    <span class="k">return</span> <span class="k">new</span> <span class="nx">ErrorCodes</span><span class="p">[</span><span class="nx">mapping</span><span class="p">.</span><span class="nx">newType</span><span class="p">](</span><span class="nx">mapping</span><span class="p">.</span><span class="nx">newCode</span><span class="p">,</span> <span class="nx">message</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">      <span class="p">...</span><span class="nx">details</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">      <span class="nx">migrationSource</span><span class="o">:</span> <span class="s1">&#39;StandardError&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">      <span class="nx">originalCode</span><span class="o">:</span> <span class="nx">code</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">    <span class="p">})</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">
</span></span><span class="line"><span class="ln">33</span><span class="cl">  <span class="c1">// 關鍵：完全保持舊 API 的行為
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="c1"></span>  <span class="nx">toString</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">errorCodesInstance</span><span class="p">.</span><span class="nx">toString</span><span class="p">()</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">  <span class="nx">toJSON</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">errorCodesInstance</span><span class="p">.</span><span class="nx">toJSON</span><span class="p">()</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h4 id="危險模式2資料轉換精度損失">危險模式2：資料轉換精度損失</h4>
<ul>
<li>
<p><strong>風險</strong>：新舊格式轉換時資料損壞</p>
</li>
<li>
<p><strong>症狀</strong>：精度丟失、型別錯誤、資料截斷</p>
</li>
<li>
<p><strong>預防策略</strong>：橋接模式 + 雙向驗證</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><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">npm run migration:data-integrity --round-trip-test</span></span></code></pre></div></li>
</ul>
<h4 id="實作範例橋接器模式-bridge-pattern">實作範例：橋接器模式 (Bridge Pattern)</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// src/core/migration/DualErrorSystemBridge.js (Sample Code)
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="kr">class</span> <span class="nx">DualErrorSystemBridge</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="kr">static</span> <span class="nx">DUAL_SYSTEM_MODES</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="nx">LEGACY_FIRST</span><span class="o">:</span> <span class="s1">&#39;legacy_first&#39;</span><span class="p">,</span>         <span class="c1">// 優先使用 StandardError
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span>    <span class="nx">ERRORCODES_FIRST</span><span class="o">:</span> <span class="s1">&#39;errorcodes_first&#39;</span><span class="p">,</span> <span class="c1">// 優先使用 ErrorCodes
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span>    <span class="nx">PARALLEL</span><span class="o">:</span> <span class="s1">&#39;parallel&#39;</span><span class="p">,</span>                 <span class="c1">// 平行處理兩套系統
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"></span>    <span class="nx">TRANSITIONAL</span><span class="o">:</span> <span class="s1">&#39;transitional&#39;</span>          <span class="c1">// 過渡模式
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl">  <span class="nx">constructor</span><span class="p">(</span><span class="nx">options</span> <span class="o">=</span> <span class="p">{})</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">mode</span> <span class="o">=</span> <span class="nx">options</span><span class="p">.</span><span class="nx">mode</span> <span class="o">||</span> <span class="s1">&#39;PARALLEL&#39;</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">compatibilityLevel</span> <span class="o">=</span> <span class="nx">options</span><span class="p">.</span><span class="nx">compatibility</span> <span class="o">||</span> <span class="s1">&#39;STRICT&#39;</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="k">this</span><span class="p">.</span><span class="nx">performanceMonitor</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">PerformanceMonitor</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="c1">// 雙向轉換核心：確保資料完整性
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="c1"></span>  <span class="kr">async</span> <span class="nx">handleError</span><span class="p">(</span><span class="nx">error</span><span class="p">,</span> <span class="nx">context</span> <span class="o">=</span> <span class="p">{})</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="kr">const</span> <span class="nx">startTime</span> <span class="o">=</span> <span class="nx">performance</span><span class="p">.</span><span class="nx">now</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">      <span class="c1">// 安全檢查：驗證輸入錯誤的有效性
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="c1"></span>      <span class="k">this</span><span class="p">.</span><span class="nx">_validateErrorInput</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl">      <span class="kd">let</span> <span class="nx">result</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">      <span class="k">switch</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">mode</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">        <span class="k">case</span> <span class="s1">&#39;PARALLEL&#39;</span><span class="o">:</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">          <span class="c1">// 平行處理：同時使用兩套系統，確保一致性
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="c1"></span>          <span class="nx">result</span> <span class="o">=</span> <span class="kr">await</span> <span class="k">this</span><span class="p">.</span><span class="nx">_handleParallelMode</span><span class="p">(</span><span class="nx">error</span><span class="p">,</span> <span class="nx">context</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">          <span class="k">break</span>
</span></span><span class="line"><span class="ln">30</span><span class="cl">        <span class="k">case</span> <span class="s1">&#39;LEGACY_FIRST&#39;</span><span class="o">:</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">          <span class="nx">result</span> <span class="o">=</span> <span class="kr">await</span> <span class="k">this</span><span class="p">.</span><span class="nx">_handleLegacyFirst</span><span class="p">(</span><span class="nx">error</span><span class="p">,</span> <span class="nx">context</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">          <span class="k">break</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">        <span class="k">case</span> <span class="s1">&#39;ERRORCODES_FIRST&#39;</span><span class="o">:</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">          <span class="nx">result</span> <span class="o">=</span> <span class="kr">await</span> <span class="k">this</span><span class="p">.</span><span class="nx">_handleErrorCodesFirst</span><span class="p">(</span><span class="nx">error</span><span class="p">,</span> <span class="nx">context</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">          <span class="k">break</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">        <span class="k">default</span><span class="o">:</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">          <span class="nx">result</span> <span class="o">=</span> <span class="kr">await</span> <span class="k">this</span><span class="p">.</span><span class="nx">_handleTransitionalMode</span><span class="p">(</span><span class="nx">error</span><span class="p">,</span> <span class="nx">context</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">
</span></span><span class="line"><span class="ln">40</span><span class="cl">      <span class="c1">//  關鍵：雙向驗證確保轉換精度
</span></span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="c1"></span>      <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">compatibilityLevel</span> <span class="o">===</span> <span class="s1">&#39;STRICT&#39;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">42</span><span class="cl">        <span class="kr">await</span> <span class="k">this</span><span class="p">.</span><span class="nx">_verifyConversionAccuracy</span><span class="p">(</span><span class="nx">error</span><span class="p">,</span> <span class="nx">result</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">43</span><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="ln">44</span><span class="cl">
</span></span><span class="line"><span class="ln">45</span><span class="cl">      <span class="k">return</span> <span class="nx">result</span>
</span></span><span class="line"><span class="ln">46</span><span class="cl">    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">conversionError</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">47</span><span class="cl">      <span class="c1">// 錯誤處理：轉換失敗時的安全機制
</span></span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="c1"></span>      <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">_handleConversionFailure</span><span class="p">(</span><span class="nx">error</span><span class="p">,</span> <span class="nx">conversionError</span><span class="p">,</span> <span class="nx">context</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl">    <span class="p">}</span> <span class="k">finally</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">50</span><span class="cl">      <span class="c1">//  效能監控：追蹤轉換效能
</span></span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="c1"></span>      <span class="k">this</span><span class="p">.</span><span class="nx">performanceMonitor</span><span class="p">.</span><span class="nx">recordConversion</span><span class="p">(</span><span class="nx">performance</span><span class="p">.</span><span class="nx">now</span><span class="p">()</span> <span class="o">-</span> <span class="nx">startTime</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">52</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">53</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">54</span><span class="cl">
</span></span><span class="line"><span class="ln">55</span><span class="cl">  <span class="kr">async</span> <span class="nx">_verifyConversionAccuracy</span><span class="p">(</span><span class="nx">original</span><span class="p">,</span> <span class="nx">converted</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">56</span><span class="cl">    <span class="c1">// 檢查語意完整性
</span></span></span><span class="line"><span class="ln">57</span><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="nx">original</span><span class="p">.</span><span class="nx">code</span> <span class="o">!==</span> <span class="nx">converted</span><span class="p">.</span><span class="nx">getOriginalCode</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">58</span><span class="cl">      <span class="k">throw</span> <span class="k">new</span> <span class="nx">ConversionError</span><span class="p">(</span><span class="s1">&#39;Code mapping失敗&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">59</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">60</span><span class="cl">
</span></span><span class="line"><span class="ln">61</span><span class="cl">    <span class="c1">// 檢查資料完整性
</span></span></span><span class="line"><span class="ln">62</span><span class="cl"><span class="c1"></span>    <span class="kr">const</span> <span class="nx">roundTripResult</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">_convertBack</span><span class="p">(</span><span class="nx">converted</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">63</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">_isDataEquivalent</span><span class="p">(</span><span class="nx">original</span><span class="p">,</span> <span class="nx">roundTripResult</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">64</span><span class="cl">      <span class="k">throw</span> <span class="k">new</span> <span class="nx">ConversionError</span><span class="p">(</span><span class="s1">&#39;Round-trip 驗證失敗&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">65</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">66</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">67</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h4 id="危險模式3效能懸崖-performance-cliff">危險模式3：效能懸崖 (Performance Cliff)</h4>
<ul>
<li>
<p><strong>風險</strong>：相容性層導致效能急劇下降</p>
</li>
<li>
<p><strong>症狀</strong>：記憶體洩漏、CPU爆炸、回應時間暴增</p>
</li>
<li>
<p><strong>預防策略</strong>：適配器模式 + 效能監控</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><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">npm run migration:performance-test --baseline</span></span></code></pre></div></li>
</ul>
<h4 id="實作範例適配器模式-adapter-pattern">實作範例：適配器模式 (Adapter Pattern)</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// src/core/errors/UC01ErrorAdapter.js (Sample Code)
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="kr">class</span> <span class="nx">UC01ErrorAdapter</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="c1">// 錯誤映射表：確保精確轉換
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"></span>  <span class="kr">static</span> <span class="nx">getErrorMapping</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">_errorMapping</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">      <span class="k">this</span><span class="p">.</span><span class="nx">_errorMapping</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">        <span class="c1">// DOM_ERROR 類型映射
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span>        <span class="s1">&#39;PAGE_DETECTION_FAILED&#39;</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">          <span class="nx">newType</span><span class="o">:</span> <span class="s1">&#39;DOM_ERROR&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">          <span class="nx">newCode</span><span class="o">:</span> <span class="s1">&#39;DOM_001&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">          <span class="nx">riskLevel</span><span class="o">:</span> <span class="s1">&#39;HIGH&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">          <span class="nx">conversionComplexity</span><span class="o">:</span> <span class="s1">&#39;SIMPLE&#39;</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">        <span class="s1">&#39;ELEMENT_EXTRACTION_FAILED&#39;</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">          <span class="nx">newType</span><span class="o">:</span> <span class="s1">&#39;DOM_ERROR&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">          <span class="nx">newCode</span><span class="o">:</span> <span class="s1">&#39;DOM_002&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">          <span class="nx">riskLevel</span><span class="o">:</span> <span class="s1">&#39;MEDIUM&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">          <span class="nx">conversionComplexity</span><span class="o">:</span> <span class="s1">&#39;SIMPLE&#39;</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl">        <span class="c1">// NETWORK_ERROR 類型映射
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="c1"></span>        <span class="s1">&#39;CONNECTION_TIMEOUT&#39;</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">          <span class="nx">newType</span><span class="o">:</span> <span class="s1">&#39;NETWORK_ERROR&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl">          <span class="nx">newCode</span><span class="o">:</span> <span class="s1">&#39;NET_001&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">          <span class="nx">riskLevel</span><span class="o">:</span> <span class="s1">&#39;HIGH&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">          <span class="nx">conversionComplexity</span><span class="o">:</span> <span class="s1">&#39;MEDIUM&#39;</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">
</span></span><span class="line"><span class="ln">29</span><span class="cl">        <span class="c1">// SYSTEM_ERROR 類型映射
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="c1"></span>        <span class="s1">&#39;STORAGE_QUOTA_EXCEEDED&#39;</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">          <span class="nx">newType</span><span class="o">:</span> <span class="s1">&#39;SYSTEM_ERROR&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">          <span class="nx">newCode</span><span class="o">:</span> <span class="s1">&#39;SYS_001&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">          <span class="nx">riskLevel</span><span class="o">:</span> <span class="s1">&#39;CRITICAL&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">34</span><span class="cl">          <span class="nx">conversionComplexity</span><span class="o">:</span> <span class="s1">&#39;COMPLEX&#39;</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">        <span class="c1">// ... 更多映射定義
</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="c1"></span>      <span class="p">}</span>
</span></span><span class="line"><span class="ln">38</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">    <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">_errorMapping</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">
</span></span><span class="line"><span class="ln">42</span><span class="cl">  <span class="c1">// 高效能轉換：&lt;1ms 目標
</span></span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="c1"></span>  <span class="kr">static</span> <span class="nx">convertError</span><span class="p">(</span><span class="nx">standardError</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">44</span><span class="cl">    <span class="kr">const</span> <span class="nx">startTime</span> <span class="o">=</span> <span class="nx">performance</span><span class="p">.</span><span class="nx">now</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl">
</span></span><span class="line"><span class="ln">46</span><span class="cl">    <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">47</span><span class="cl">      <span class="c1">// 快速查找：使用預建索引
</span></span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="c1"></span>      <span class="kr">const</span> <span class="nx">mapping</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">getErrorMapping</span><span class="p">()[</span><span class="nx">standardError</span><span class="p">.</span><span class="nx">code</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">mapping</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">50</span><span class="cl">        <span class="k">throw</span> <span class="k">new</span> <span class="nx">AdapterError</span><span class="p">(</span><span class="sb">`無法映射錯誤代碼: </span><span class="si">${</span><span class="nx">standardError</span><span class="p">.</span><span class="nx">code</span><span class="si">}</span><span class="sb">`</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">51</span><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="ln">52</span><span class="cl">
</span></span><span class="line"><span class="ln">53</span><span class="cl">      <span class="c1">// 安全轉換：保持所有原始資訊
</span></span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="c1"></span>      <span class="kr">const</span> <span class="nx">errorCodesInstance</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">ErrorCodes</span><span class="p">[</span><span class="nx">mapping</span><span class="p">.</span><span class="nx">newType</span><span class="p">](</span>
</span></span><span class="line"><span class="ln">55</span><span class="cl">        <span class="nx">mapping</span><span class="p">.</span><span class="nx">newCode</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">56</span><span class="cl">        <span class="nx">standardError</span><span class="p">.</span><span class="nx">message</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">57</span><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="ln">58</span><span class="cl">          <span class="p">...</span><span class="nx">standardError</span><span class="p">.</span><span class="nx">details</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">59</span><span class="cl">          <span class="c1">// 可追蹤性：保留轉換資訊
</span></span></span><span class="line"><span class="ln">60</span><span class="cl"><span class="c1"></span>          <span class="nx">migrationInfo</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">61</span><span class="cl">            <span class="nx">originalCode</span><span class="o">:</span> <span class="nx">standardError</span><span class="p">.</span><span class="nx">code</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">62</span><span class="cl">            <span class="nx">convertedAt</span><span class="o">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">().</span><span class="nx">toISOString</span><span class="p">(),</span>
</span></span><span class="line"><span class="ln">63</span><span class="cl">            <span class="nx">adapterVersion</span><span class="o">:</span> <span class="s1">&#39;UC01-v1.0&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">64</span><span class="cl">            <span class="nx">riskLevel</span><span class="o">:</span> <span class="nx">mapping</span><span class="p">.</span><span class="nx">riskLevel</span>
</span></span><span class="line"><span class="ln">65</span><span class="cl">          <span class="p">}</span>
</span></span><span class="line"><span class="ln">66</span><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="ln">67</span><span class="cl">      <span class="p">)</span>
</span></span><span class="line"><span class="ln">68</span><span class="cl">
</span></span><span class="line"><span class="ln">69</span><span class="cl">      <span class="c1">// 效能監控：確保符合 &lt;1ms 目標
</span></span></span><span class="line"><span class="ln">70</span><span class="cl"><span class="c1"></span>      <span class="kr">const</span> <span class="nx">conversionTime</span> <span class="o">=</span> <span class="nx">performance</span><span class="p">.</span><span class="nx">now</span><span class="p">()</span> <span class="o">-</span> <span class="nx">startTime</span>
</span></span><span class="line"><span class="ln">71</span><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="nx">conversionTime</span> <span class="o">&gt;</span> <span class="mf">1.0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">72</span><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">warn</span><span class="p">(</span><span class="sb">`轉換效能警告: </span><span class="si">${</span><span class="nx">conversionTime</span><span class="si">}</span><span class="sb">ms 超過 1ms 目標`</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">73</span><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="ln">74</span><span class="cl">
</span></span><span class="line"><span class="ln">75</span><span class="cl">      <span class="k">return</span> <span class="nx">errorCodesInstance</span>
</span></span><span class="line"><span class="ln">76</span><span class="cl">    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">77</span><span class="cl">      <span class="c1">// 失敗處理：提供降級機制
</span></span></span><span class="line"><span class="ln">78</span><span class="cl"><span class="c1"></span>      <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">_createFallbackError</span><span class="p">(</span><span class="nx">standardError</span><span class="p">,</span> <span class="nx">error</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">79</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">80</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">81</span><span class="cl">
</span></span><span class="line"><span class="ln">82</span><span class="cl">  <span class="c1">// 安全機制：轉換失敗時的降級處理
</span></span></span><span class="line"><span class="ln">83</span><span class="cl"><span class="c1"></span>  <span class="kr">static</span> <span class="nx">_createFallbackError</span><span class="p">(</span><span class="nx">originalError</span><span class="p">,</span> <span class="nx">conversionError</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">84</span><span class="cl">    <span class="k">return</span> <span class="k">new</span> <span class="nx">ErrorCodes</span><span class="p">.</span><span class="nx">SYSTEM_ERROR</span><span class="p">(</span><span class="s1">&#39;SYS_999&#39;</span><span class="p">,</span> <span class="s1">&#39;錯誤轉換失敗&#39;</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">85</span><span class="cl">      <span class="nx">originalError</span><span class="o">:</span> <span class="nx">originalError</span><span class="p">.</span><span class="nx">toString</span><span class="p">(),</span>
</span></span><span class="line"><span class="ln">86</span><span class="cl">      <span class="nx">conversionError</span><span class="o">:</span> <span class="nx">conversionError</span><span class="p">.</span><span class="nx">message</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">87</span><span class="cl">      <span class="nx">fallbackMode</span><span class="o">:</span> <span class="kc">true</span>
</span></span><span class="line"><span class="ln">88</span><span class="cl">    <span class="p">})</span>
</span></span><span class="line"><span class="ln">89</span><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="ln">90</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h3 id="支柱四監控與早期預警機制---避免問題擴散">支柱四：監控與早期預警機制 - 避免問題擴散</h3>
<p><strong>核心問題：</strong> 「如何在小問題變成大災難之前攔截它們？」</p>
<h4 id="監控盲點的危險模式與預警設計">監控盲點的危險模式與預警設計</h4>
<h4 id="盲點1無聲故障-silent-failure">盲點1：無聲故障 (Silent Failure)</h4>
<ul>
<li>
<p><strong>風險</strong>：錯誤被掩蓋，問題累積到臨界點才爆發</p>
</li>
<li>
<p><strong>危險信號</strong>：測試通過但業務邏輯錯誤</p>
</li>
<li>
<p><strong>預警機制</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><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">npm run migration:monitor --silent-failure-detection</span></span></code></pre></div></li>
</ul>
<h4 id="盲點2級聯故障延遲">盲點2：級聯故障延遲</h4>
<ul>
<li>
<p><strong>風險</strong>：依賴鏈中的問題延遲暴露</p>
</li>
<li>
<p><strong>危險信號</strong>：單一檔案修改後，相關檔案在數小時後才出錯</p>
</li>
<li>
<p><strong>預警機制</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><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">npm run migration:track-cascade --dependency-chain</span></span></code></pre></div></li>
</ul>
<h4 id="盲點3效能劣化累積">盲點3：效能劣化累積</h4>
<ul>
<li>
<p><strong>風險</strong>：小幅效能下降累積成系統瓶頸</p>
</li>
<li>
<p><strong>危險信號</strong>：個別測試通過，但整體效能下降</p>
</li>
<li>
<p><strong>預警機制</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><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">npm run migration:health-check --performance-regression</span></span></code></pre></div></li>
</ul>
<h3 id="支柱五知識管理與錯誤預防---避免重複出問題">支柱五：知識管理與錯誤預防 - 避免重複出問題</h3>
<p><strong>核心問題：</strong> 「如何防止團隊重複犯相同的錯誤？」</p>
<h4 id="知識遺失的危險模式與防護機制">知識遺失的危險模式與防護機制</h4>
<h4 id="危險模式1隱式決策-implicit-decision">危險模式1：隱式決策 (Implicit Decision)</h4>
<ul>
<li>
<p><strong>風險</strong>：關鍵決策沒有記錄，後人重複試錯</p>
</li>
<li>
<p><strong>症狀</strong>：「為什麼當初這樣做？」無人知曉</p>
</li>
<li>
<p><strong>防護機制</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><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">npm run migration:decision-log --why<span class="o">=</span><span class="s2">&#34;reason&#34;</span> --what<span class="o">=</span><span class="s2">&#34;change&#34;</span></span></span></code></pre></div></li>
</ul>
<h4 id="危險模式2錯誤重現-error-repetition">危險模式2：錯誤重現 (Error Repetition)</h4>
<ul>
<li>
<p><strong>風險</strong>：相同的錯誤在不同時間、不同人員間重複發生</p>
</li>
<li>
<p><strong>症狀</strong>：類似的問題反覆出現，解決方案被遺忘</p>
</li>
<li>
<p><strong>防護機制</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><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">npm run migration:check-known-issues --pattern-match</span></span></code></pre></div></li>
</ul>
<h4 id="危險模式3解決方案腐化-solution-decay">危險模式3：解決方案腐化 (Solution Decay)</h4>
<ul>
<li>
<p><strong>風險</strong>：過時的解決方案被盲目應用到新情境</p>
</li>
<li>
<p><strong>症狀</strong>：舊方法在新環境中失效或造成新問題</p>
</li>
<li>
<p><strong>防護機制</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><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">npm run migration:validate-solution --context-check</span></span></code></pre></div></li>
</ul>
<hr>
<h2 id="-方法論詳解思考流程與決策邏輯">📐 方法論詳解：思考流程與決策邏輯</h2>
<h3 id="phase-1-發現與評估---知己知彼">Phase 1: 發現與評估 - 「知己知彼」</h3>
<h4 id="11-實際問題本質挖掘---連接概念卡片a">1.1 實際問題本質挖掘 - 連接【概念卡片A】</h4>
<h5 id="基於實際專案經驗的問題識別流程">基於實際專案經驗的問題識別流程</h5>
<p>如【概念卡片A】所述，我們遭遇的核心問題是<strong>過度工程化危機</strong>。實際的問題挖掘過程：</p>
<h5 id="第一層表面症狀觀察">第一層：表面症狀觀察</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// 實際發現的問題模式 (Sample Code)
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1">// 症狀1：開發者困惑
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">handleErrorA</span><span class="p">()</span> <span class="p">{</span> <span class="k">throw</span> <span class="s1">&#39;STRING_ERROR&#39;</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="kd">function</span> <span class="nx">handleErrorB</span><span class="p">()</span> <span class="p">{</span> <span class="k">throw</span> <span class="k">new</span> <span class="nx">CustomError</span><span class="p">(...)</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="kd">function</span> <span class="nx">handleErrorC</span><span class="p">()</span> <span class="p">{</span> <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(...)</span> <span class="p">}</span>
</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 class="c1">// 症狀2：測試複雜化
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span><span class="nx">expect</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="nx">funcA</span><span class="p">()).</span><span class="nx">toThrow</span><span class="p">(</span><span class="s1">&#39;STRING_ERROR&#39;</span><span class="p">)</span>      <span class="c1">// 字串比對
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"></span><span class="nx">expect</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="nx">funcB</span><span class="p">()).</span><span class="nx">toThrow</span><span class="p">(</span><span class="nx">CustomError</span><span class="p">)</span>         <span class="c1">// 類型檢查
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"></span><span class="nx">expect</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="nx">funcC</span><span class="p">()).</span><span class="nx">toThrow</span><span class="p">(</span><span class="sr">/error message/</span><span class="p">)</span>     <span class="c1">// 正則匹配
</span></span></span></code></pre></div><h5 id="第二層根本原因分析">第二層：根本原因分析</h5>
<ul>
<li><strong>技術債務累積</strong>：30+ 錯誤代碼，每個模組自定義規範</li>
<li><strong>架構分裂</strong>：7 個功能模組使用不同錯誤處理模式</li>
<li><strong>維護成本指數增長</strong>：新增一個錯誤需要修改多個地方</li>
</ul>
<h5 id="第三層量化問題影響">第三層：量化問題影響</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 實際測量結果 (Sample Code)</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">npm run migration:analyze --metrics
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"># 輸出：</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># - 錯誤處理模式：7 種不同方式</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"># - 維護工時：每月 40+ 小時處理錯誤處理相關問題</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1"># - 新人學習成本：需要 2 週理解全部錯誤處理規範</span></span></span></code></pre></div><h4 id="12-系統現狀深度解析---連接概念卡片b">1.2 系統現狀深度解析 - 連接【概念卡片B】</h4>
<h5 id="實際分散系統混亂狀態分析">實際分散系統混亂狀態分析</h5>
<p>如【概念卡片B】所述的分散系統問題，實際解析流程：</p>
<h5 id="模組錯誤處理現狀盤點">模組錯誤處理現狀盤點</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// 實際發現的系統狀態 (Sample Code)
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">systemErrorMappings</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="s1">&#39;UC-01&#39;</span><span class="o">:</span> <span class="s1">&#39;string-based errors&#39;</span><span class="p">,</span>      <span class="c1">// 10 個字串錯誤
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"></span>  <span class="s1">&#39;UC-02&#39;</span><span class="o">:</span> <span class="s1">&#39;StandardError class&#39;</span><span class="p">,</span>      <span class="c1">// 15 個 StandardError
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"></span>  <span class="s1">&#39;UC-03&#39;</span><span class="o">:</span> <span class="s1">&#39;native Error objects&#39;</span><span class="p">,</span>     <span class="c1">// 12 個原生 Error
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span>  <span class="s1">&#39;UC-04&#39;</span><span class="o">:</span> <span class="s1">&#39;mixed approaches&#39;</span><span class="p">,</span>         <span class="c1">// 混合模式
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"></span>  <span class="s1">&#39;UC-05&#39;</span><span class="o">:</span> <span class="s1">&#39;custom error classes&#39;</span><span class="p">,</span>     <span class="c1">// 自定義錯誤類
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span>  <span class="s1">&#39;UC-06&#39;</span><span class="o">:</span> <span class="s1">&#39;result objects&#39;</span><span class="p">,</span>           <span class="c1">// 結果物件模式
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"></span>  <span class="s1">&#39;UC-07&#39;</span><span class="o">:</span> <span class="s1">&#39;exception throwing&#39;</span>        <span class="c1">// 異常拋出模式
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><h5 id="跨平台一致性缺失分析">跨平台一致性缺失分析</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// Chrome Extension 環境 (Sample Code)
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="kr">class</span> <span class="nx">ChromeErrorHandler</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="nx">serialize</span><span class="p">(</span><span class="nx">error</span><span class="p">)</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="k">typeof</span> <span class="nx">error</span> <span class="o">===</span> <span class="s1">&#39;string&#39;</span><span class="p">)</span> <span class="k">return</span> <span class="p">{</span><span class="nx">type</span><span class="o">:</span> <span class="s1">&#39;string&#39;</span><span class="p">,</span> <span class="nx">value</span><span class="o">:</span> <span class="nx">error</span><span class="p">}</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">error</span> <span class="k">instanceof</span> <span class="nx">CustomError</span><span class="p">)</span> <span class="k">return</span> <span class="nx">error</span><span class="p">.</span><span class="nx">toJSON</span><span class="p">()</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="c1">// 每種錯誤類型需要特殊處理...
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1">// Flutter 環境 (Sample Code)
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"></span><span class="kr">class</span> <span class="nx">FlutterErrorHandler</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="nx">handleError</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">error</span> <span class="nx">is</span> <span class="nb">String</span><span class="p">)</span> <span class="k">return</span> <span class="nx">StringError</span><span class="p">(</span><span class="nx">error</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">error</span> <span class="nx">is</span> <span class="nx">CustomException</span><span class="p">)</span> <span class="k">return</span> <span class="nx">error</span><span class="p">.</span><span class="nx">toDart</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="c1">// 完全不同的處理邏輯...
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="c1"></span>  <span class="p">}</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h5 id="技術債務量化評估">技術債務量化評估</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 實際執行的分析指令 (Sample Code)</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">npm run migration:debt-analysis
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"># 結果：</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># - 重複代碼：14 個相似的錯誤處理模式</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"># - 測試複雜度：每個模組需要不同的測試策略</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1"># - 序列化問題：無法統一 JSON 格式</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># - 文檔維護：7 套不同的錯誤處理說明文檔</span></span></span></code></pre></div><h5 id="影響範圍實際測量">影響範圍實際測量</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1">// 變更影響分析工具結果 (Sample Code)
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">impactAnalysis</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">  <span class="nx">affectedModules</span><span class="o">:</span> <span class="mi">7</span><span class="p">,</span>                    <span class="c1">// 7 個功能模組
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"></span>  <span class="nx">testFilesRequiringUpdate</span><span class="o">:</span> <span class="mi">45</span><span class="p">,</span>          <span class="c1">// 45 個測試檔案
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"></span>  <span class="nx">documentationPages</span><span class="o">:</span> <span class="mi">12</span><span class="p">,</span>                <span class="c1">// 12 頁文檔
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1"></span>  <span class="nx">developersAffected</span><span class="o">:</span> <span class="mi">4</span><span class="p">,</span>                 <span class="c1">// 4 位開發者
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"></span>  <span class="nx">estimatedMigrationTime</span><span class="o">:</span> <span class="s1">&#39;3-6 months&#39;</span>   <span class="c1">// 預估遷移時間
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div><h4 id="13-可行性多維度評估">1.3 可行性多維度評估</h4>
<h5 id="評估框架">評估框架</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">技術可行性 × 資源可行性 × 時間可行性 × <span class="nv">風險可接受性</span> <span class="o">=</span> 整體可行性</span></span></code></pre></div><h5 id="具體評估方法">具體評估方法</h5>
<ol>
<li>
<p><strong>技術可行性</strong>：</p>
<ul>
<li>是否有成熟的遷移路徑？</li>
<li>是否有相似的成功案例？</li>
<li>是否有必要的工具支援？</li>
<li>團隊是否具備相關技能？</li>
</ul>
</li>
<li>
<p><strong>資源可行性</strong>：</p>
<ul>
<li>人力資源：開發人員、測試人員、運營人員</li>
<li>工具資源：開發工具、測試環境、監控系統</li>
<li>預算資源：軟體授權、硬體設備、外部諮詢</li>
</ul>
</li>
<li>
<p><strong>時間可行性</strong>：</p>
<ul>
<li>業務窗口：是否有合適的時間點？</li>
<li>開發週期：預估工作量是否合理？</li>
<li>學習時間：團隊是否有足夠時間適應？</li>
</ul>
</li>
<li>
<p><strong>風險可接受性</strong>：</p>
<ul>
<li>業務風險：對核心業務的影響</li>
<li>技術風險：系統穩定性的影響</li>
<li>組織風險：對團隊效率的影響</li>
</ul>
</li>
</ol>
<h3 id="phase-2-策略設計">Phase 2: 策略設計</h3>
<h4 id="21-遷移策略選擇的決策樹">2.1 遷移策略選擇的決策樹</h4>
<p><strong>核心問題：</strong> 選擇什麼樣的遷移策略？</p>
<h5 id="決策維度">決策維度</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">影響範圍 + 複雜度 + 時間壓力 + <span class="nv">團隊能力</span> <span class="o">=</span> 策略選擇</span></span></code></pre></div><h5 id="策略類型與適用情境">策略類型與適用情境</h5>
<ol>
<li>
<p><strong>大爆炸式遷移 (Big Bang)</strong>：</p>
<ul>
<li><strong>適用條件</strong>：影響範圍小（&lt;50個檔案）、複雜度低、團隊經驗豐富</li>
<li><strong>優勢</strong>：快速完成、狀態單純</li>
<li><strong>風險</strong>：失敗代價高、回滾困難</li>
<li><strong>決策考量</strong>：是否有充分的測試覆蓋？是否有完整的回滾計畫？</li>
</ul>
</li>
<li>
<p><strong>分批次遷移 (Batch Migration)</strong>：</p>
<ul>
<li><strong>適用條件</strong>：中等規模（50-200個檔案）、模組間耦合度低</li>
<li><strong>優勢</strong>：風險可控、可以階段性驗證</li>
<li><strong>風險</strong>：批次間可能有依賴問題</li>
<li><strong>決策考量</strong>：如何劃分批次？如何處理批次間依賴？</li>
</ul>
</li>
<li>
<p><strong>漸進式遷移 (Incremental Migration)</strong>：</p>
<ul>
<li><strong>適用條件</strong>：大規模（&gt;200個檔案）、高複雜度、長期專案</li>
<li><strong>優勢</strong>：風險最低、可以持續優化</li>
<li><strong>風險</strong>：長期維護成本、新舊系統並存複雜度</li>
<li><strong>決策考量</strong>：如何設計相容性？如何管理過渡期？</li>
</ul>
</li>
</ol>
<h4 id="22-風險分級的科學化方法">2.2 風險分級的科學化方法</h4>
<h5 id="風險評估公式設計思考">風險評估公式設計思考</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="nv">風險等級</span> <span class="o">=</span> f<span class="o">(</span>技術複雜度, 業務影響度, 使用頻率, 依賴關係<span class="o">)</span></span></span></code></pre></div><h5 id="多維度風險評估模型">多維度風險評估模型</h5>
<ol>
<li>
<p><strong>技術複雜度評估</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">簡單：語法替換、參數調整
</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></code></pre></div></li>
<li>
<p><strong>業務影響度評估</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">低影響：輔助功能、開發工具
</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></code></pre></div></li>
<li>
<p><strong>使用頻率評估</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">低頻率：偶爾使用、邊緣情境
</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></code></pre></div></li>
<li>
<p><strong>依賴關係評估</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">獨立：無依賴或依賴穩定
</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></code></pre></div></li>
</ol>
<h4 id="23-相容性策略的設計原則">2.3 相容性策略的設計原則</h4>
<p><strong>核心問題：</strong> 如何讓新舊系統和諧共存？</p>
<h5 id="設計原則">設計原則</h5>
<ol>
<li>
<p><strong>最小驚訝原則</strong>：</p>
<ul>
<li>對使用者：介面儘量保持不變</li>
<li>對開發者：學習成本儘量最小</li>
<li>對系統：行為儘量保持一致</li>
</ul>
</li>
<li>
<p><strong>漸進披露原則</strong>：</p>
<ul>
<li>先暴露基本功能，再暴露進階功能</li>
<li>先支援常用情境，再支援邊緣情境</li>
<li>先確保正確性，再優化效能</li>
</ul>
</li>
<li>
<p><strong>優雅降級原則</strong>：</p>
<ul>
<li>新功能不可用時，能回退到舊功能</li>
<li>部分失敗不影響整體功能</li>
<li>錯誤能被清楚地識別和處理</li>
</ul>
</li>
</ol>
<h5 id="相容性模式選擇邏輯">相容性模式選擇邏輯</h5>





<pre tabindex="0"><code class="language-mermaid" data-lang="mermaid">graph TD
    A[需要相容性嗎？] --&gt;|是| B[並行期多長？]
    A --&gt;|否| C[直接替換]

    B --&gt;|短期&lt;3個月| D[包裝器模式]
    B --&gt;|中期3-12個月| E[橋接模式]
    B --&gt;|長期&gt;12個月| F[適配器模式]

    D --&gt; G[重點：保持介面穩定]
    E --&gt; H[重點：雙向轉換精確]
    F --&gt; I[重點：功能完整對等]</code></pre><h3 id="phase-3-實施執行---步步為營">Phase 3: 實施執行 - 「步步為營」</h3>
<h4 id="31-執行階段的設計哲學">3.1 執行階段的設計哲學</h4>
<p><strong>核心問題：</strong> 如何在確保安全的前提下高效執行遷移？</p>
<h5 id="設計哲學">設計哲學</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">安全第一 → 效率第二 → 完美第三</span></span></code></pre></div><h5 id="執行原則">執行原則</h5>
<ol>
<li><strong>小步快跑</strong>：每次變更儘量小，但頻率儘量高</li>
<li><strong>快速回饋</strong>：每個變更都有即時的驗證機制</li>
<li><strong>持續監控</strong>：全程監控系統健康狀況</li>
<li><strong>即時調整</strong>：根據回饋快速調整策略</li>
</ol>
<h4 id="32-自動化工具鏈設計">3.2 自動化工具鏈設計</h4>
<h5 id="工具鏈架構思考">工具鏈架構思考</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">分析工具 → 轉換工具 → 驗證工具 → 監控工具</span></span></code></pre></div><h5 id="跨語言通用的工具設計模式">跨語言通用的工具設計模式</h5>
<ol>
<li>
<p><strong>程式碼分析器 (Code Analyzer)</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">輸入：源程式碼檔案
</span></span><span class="line"><span class="ln">2</span><span class="cl">處理：AST解析 → 模式匹配 → 影響分析
</span></span><span class="line"><span class="ln">3</span><span class="cl">輸出：遷移候選清單 + 風險評估</span></span></code></pre></div><p><strong>設計考量</strong>：</p>
<ul>
<li>如何處理不同語言的語法差異？</li>
<li>如何識別語言特定的慣用模式？</li>
<li>如何處理宏、模板、動態特性？</li>
</ul>
</li>
<li>
<p><strong>程式碼轉換器 (Code Transformer)</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">輸入：遷移候選 + 轉換規則
</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></code></pre></div><p><strong>設計考量</strong>：</p>
<ul>
<li>如何確保轉換的正確性？</li>
<li>如何保持原有的程式碼風格？</li>
<li>如何處理複雜的邏輯轉換？</li>
</ul>
</li>
<li>
<p><strong>驗證器 (Validator)</strong>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">輸入：轉換前後程式碼
</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></code></pre></div><p><strong>設計考量</strong>：</p>
<ul>
<li>如何設計全面的驗證標準？</li>
<li>如何平衡驗證嚴格度和執行效率？</li>
<li>如何處理驗證失敗的情況？</li>
</ul>
</li>
</ol>
<h4 id="33-監控與早期預警">3.3 監控與早期預警</h4>
<h5 id="監控體系設計">監控體系設計</h5>
<ol>
<li>
<p><strong>技術指標監控</strong>：</p>
<ul>
<li>編譯成功率、測試通過率</li>
<li>效能指標、錯誤率</li>
<li>程式碼品質指標</li>
</ul>
</li>
<li>
<p><strong>進度指標監控</strong>：</p>
<ul>
<li>遷移完成度、預計完成時間</li>
<li>阻礙問題、風險變化</li>
<li>資源使用情況</li>
</ul>
</li>
<li>
<p><strong>業務指標監控</strong>：</p>
<ul>
<li>系統可用性、使用者滿意度</li>
<li>功能正確性、資料完整性</li>
<li>效能表現、擴展性</li>
</ul>
</li>
</ol>
<h3 id="phase-4-驗證與優化---精益求精">Phase 4: 驗證與優化 - 「精益求精」</h3>
<h4 id="41-多層次驗證策略">4.1 多層次驗證策略</h4>
<h5 id="驗證金字塔">驗證金字塔</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">業務驗證 <span class="o">(</span>頂層<span class="o">)</span>
</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 class="o">(</span>中層<span class="o">)</span>
</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">└── 單元驗證 <span class="o">(</span>底層<span class="o">)</span></span></span></code></pre></div><h5 id="各層驗證重點">各層驗證重點</h5>
<ol>
<li>
<p><strong>單元驗證</strong>：</p>
<ul>
<li>功能正確性：每個函數的輸入輸出是否正確</li>
<li>邊界條件：異常情況是否正確處理</li>
<li>效能要求：是否滿足效能標準</li>
</ul>
</li>
<li>
<p><strong>整合驗證</strong>：</p>
<ul>
<li>介面相容：模組間互動是否正常</li>
<li>資料一致：資料傳遞是否完整</li>
<li>流程完整：業務流程是否正確</li>
</ul>
</li>
<li>
<p><strong>業務驗證</strong>：</p>
<ul>
<li>使用者體驗：是否影響使用者操作</li>
<li>業務邏輯：是否保持業務語意</li>
<li>非功能需求：效能、安全性、可用性</li>
</ul>
</li>
</ol>
<h4 id="42-效能最佳化策略">4.2 效能最佳化策略</h4>
<h5 id="最佳化原則">最佳化原則</h5>





<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><h5 id="跨語言通用的最佳化方向">跨語言通用的最佳化方向</h5>
<ol>
<li>
<p><strong>記憶體最佳化</strong>：</p>
<ul>
<li>減少不必要的物件創建</li>
<li>優化資料結構選擇</li>
<li>改善垃圾回收效率</li>
</ul>
</li>
<li>
<p><strong>CPU最佳化</strong>：</p>
<ul>
<li>減少複雜運算</li>
<li>優化演算法選擇</li>
<li>利用語言特定的最佳化</li>
</ul>
</li>
<li>
<p><strong>IO最佳化</strong>：</p>
<ul>
<li>減少檔案存取次數</li>
<li>優化網路請求策略</li>
<li>改善資料庫查詢效率</li>
</ul>
</li>
</ol>
<h4 id="43-持續改進機制">4.3 持續改進機制</h4>
<h5 id="改進循環">改進循環</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">問題發現 → 根因分析 → 解決方案 → 效果評估 → 知識沉澱</span></span></code></pre></div><h5 id="持續改進的關鍵">持續改進的關鍵</h5>
<ol>
<li>
<p><strong>問題追蹤</strong>：</p>
<ul>
<li>建立問題回報機制</li>
<li>分類問題嚴重程度</li>
<li>追蹤解決進度</li>
</ul>
</li>
<li>
<p><strong>知識管理</strong>：</p>
<ul>
<li>記錄解決方案</li>
<li>建立最佳實踐庫</li>
<li>分享經驗教訓</li>
</ul>
</li>
<li>
<p><strong>工具進化</strong>：</p>
<ul>
<li>根據實際使用情況改進工具</li>
<li>增加新的功能特性</li>
<li>優化使用者體驗</li>
</ul>
</li>
</ol>
<hr>
<h2 id="跨語言適應性指南">跨語言適應性指南</h2>
<h3 id="語言特性考量矩陣">語言特性考量矩陣</h3>
<table>
  <thead>
      <tr>
          <th>語言特性</th>
          <th>JavaScript</th>
          <th>Dart</th>
          <th>PHP</th>
          <th>Go</th>
          <th>Laravel</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>類型系統</strong></td>
          <td>動態類型</td>
          <td>靜態類型</td>
          <td>動態類型</td>
          <td>靜態類型</td>
          <td>動態類型(PHP)</td>
      </tr>
      <tr>
          <td><strong>編譯方式</strong></td>
          <td>解釋執行</td>
          <td>AOT/JIT</td>
          <td>解釋執行</td>
          <td>編譯</td>
          <td>解釋執行</td>
      </tr>
      <tr>
          <td><strong>錯誤處理</strong></td>
          <td>Exception</td>
          <td>Exception</td>
          <td>Exception/Return</td>
          <td>Error值</td>
          <td>Exception</td>
      </tr>
      <tr>
          <td><strong>並發模型</strong></td>
          <td>Event Loop</td>
          <td>Isolate</td>
          <td>多執行緒</td>
          <td>Goroutine</td>
          <td>多執行緒</td>
      </tr>
      <tr>
          <td><strong>生態系統</strong></td>
          <td>NPM</td>
          <td>Pub</td>
          <td>Composer</td>
          <td>Module</td>
          <td>Composer</td>
      </tr>
  </tbody>
</table>
<h3 id="語言特定的遷移考量">語言特定的遷移考量</h3>
<h4 id="javascript-生態系統">JavaScript 生態系統</h4>
<h5 id="javascript-特殊考量">JavaScript 特殊考量</h5>
<ul>
<li><strong>原型鏈影響</strong>：物件屬性的動態性</li>
<li><strong>非同步模式</strong>：Promise/async-await的錯誤傳播</li>
<li><strong>模組系統</strong>：CommonJS vs ES Module的差異</li>
<li><strong>執行環境</strong>：Node.js vs Browser的API差異</li>
</ul>
<h4 id="javascript-遷移策略調整">JavaScript 遷移策略調整</h4>
<ul>
<li>重點關注執行時錯誤檢測</li>
<li>加強型別檢查（使用TypeScript或Flow）</li>
<li>特別注意非同步錯誤處理</li>
</ul>
<h4 id="dart-生態系統">Dart 生態系統</h4>
<h5 id="dart-特殊考量">Dart 特殊考量</h5>
<ul>
<li><strong>Null Safety</strong>：空值安全的型別系統</li>
<li><strong>Future/Stream</strong>：異步程式設計模式</li>
<li><strong>Flutter相依性</strong>：UI框架的特殊需求</li>
<li><strong>AOT編譯</strong>：編譯時最佳化考量</li>
</ul>
<h4 id="dart-遷移策略調整">Dart 遷移策略調整</h4>
<ul>
<li>利用靜態型別檢查提早發現問題</li>
<li>重點關注Null Safety的遷移路徑</li>
<li>考慮Flutter widget的生命週期影響</li>
</ul>
<h4 id="php-生態系統">PHP 生態系統</h4>
<h5 id="php-特殊考量">PHP 特殊考量</h5>
<ul>
<li><strong>弱型別轉換</strong>：隱式型別轉換的陷阱</li>
<li><strong>全域狀態</strong>：全域變數和函數的影響</li>
<li><strong>Include/Require</strong>：檔案載入的依賴關係</li>
<li><strong>版本相容性</strong>：PHP版本間的差異</li>
</ul>
<h4 id="php-遷移策略調整">PHP 遷移策略調整</h4>
<ul>
<li>加強執行時驗證</li>
<li>特別關注型別相關的錯誤</li>
<li>考慮使用靜態分析工具（如PHPStan）</li>
</ul>
<h4 id="go-生態系統">Go 生態系統</h4>
<h5 id="go-特殊考量">Go 特殊考量</h5>
<ul>
<li><strong>錯誤值返回</strong>：與exception機制的根本差異</li>
<li><strong>介面導向</strong>：duck typing的設計哲學</li>
<li><strong>Goroutine</strong>：並發安全考量</li>
<li><strong>模組系統</strong>：Go modules的依賴管理</li>
</ul>
<h4 id="go-遷移策略調整">Go 遷移策略調整</h4>
<ul>
<li>重新設計錯誤處理流程</li>
<li>重點關注並發安全性</li>
<li>利用靜態型別和編譯檢查</li>
</ul>
<h4 id="laravel-框架">Laravel 框架</h4>
<h5 id="laravel-特殊考量">Laravel 特殊考量</h5>
<ul>
<li><strong>Facade模式</strong>：靜態呼叫的動態解析</li>
<li><strong>服務容器</strong>：依賴注入的複雜性</li>
<li><strong>Eloquent ORM</strong>：資料存取層的抽象</li>
<li><strong>中間件機制</strong>：請求處理流水線</li>
</ul>
<h4 id="laravel-遷移策略調整">Laravel 遷移策略調整</h4>
<ul>
<li>特別關注框架層的錯誤處理</li>
<li>考慮Facade背後的服務實例</li>
<li>重點驗證資料庫互動的正確性</li>
</ul>
<h3 id="通用適應原則">通用適應原則</h3>
<h4 id="1-語言無關的抽象層設計">1. 語言無關的抽象層設計</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">概念層：錯誤類型、處理流程、驗證規則
</span></span><span class="line"><span class="ln">2</span><span class="cl">實現層：語言特定的語法、API、慣例</span></span></code></pre></div><h4 id="2-可配置的規則引擎">2. 可配置的規則引擎</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">規則定義 → 語言適配 → 程式碼生成</span></span></code></pre></div><h4 id="3-分階段適配策略">3. 分階段適配策略</h4>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">Phase 1：基本語法適配
</span></span><span class="line"><span class="ln">2</span><span class="cl">Phase 2：語言特性適配
</span></span><span class="line"><span class="ln">3</span><span class="cl">Phase 3：生態系統適配
</span></span><span class="line"><span class="ln">4</span><span class="cl">Phase 4：效能最佳化適配</span></span></code></pre></div><hr>
<h2 id="成功評估標準與度量指標">成功評估標準與度量指標</h2>
<h3 id="定量指標體系">定量指標體系</h3>
<h5 id="技術指標">技術指標</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="nv">程式碼品質改善率</span> <span class="o">=</span> <span class="o">(</span>遷移後品質分數 - 遷移前品質分數<span class="o">)</span> / 遷移前品質分數 × 100%
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nv">遷移覆蓋率</span> <span class="o">=</span> 已遷移項目數 / 總項目數 × 100%
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="nv">自動化率</span> <span class="o">=</span> 自動處理項目數 / 總項目數 × 100%
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="nv">錯誤修復效率</span> <span class="o">=</span> 平均修復時間<span class="o">(</span>遷移後<span class="o">)</span> / 平均修復時間<span class="o">(</span>遷移前<span class="o">)</span></span></span></code></pre></div><h5 id="效率指標">效率指標</h5>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nv">遷移速度</span> <span class="o">=</span> 每週完成的遷移項目數
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nv">問題解決速度</span> <span class="o">=</span> 平均問題解決時間
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nv">工具效率</span> <span class="o">=</span> 工具節省的人工時間 / 工具開發時間
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nv">學習效率</span> <span class="o">=</span> 團隊熟練度提升速度
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="sb">```</span>text
</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 class="c1">##### 風險指標</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="sb">```</span>bash
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="nv">風險實現率</span> <span class="o">=</span> 實際發生的風險數 / 預計風險數 × 100%
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nv">影響範圍控制</span> <span class="o">=</span> 實際影響範圍 / 預估影響範圍
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="nv">回滾次數</span> <span class="o">=</span> 總回滾次數（越少越好）
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nv">嚴重事故率</span> <span class="o">=</span> 嚴重事故次數 / 遷移週期數</span></span></code></pre></div><h3 id="定性評估框架">定性評估框架</h3>
<h5 id="團隊滿意度">團隊滿意度</h5>
<ul>
<li><strong>開發體驗</strong>：工具易用性、文件完整性、支援及時性</li>
<li><strong>學習成本</strong>：新技術掌握難度、培訓效果、適應時間</li>
<li><strong>工作效率</strong>：日常開發效率、除錯便利性、部署簡化度</li>
</ul>
<h5 id="業務價值">業務價值</h5>
<ul>
<li><strong>功能改善</strong>：新功能可用性、效能提升、穩定性改善</li>
<li><strong>維護成本</strong>：長期維護難度、技術債務減少、擴展性提升</li>
<li><strong>競爭優勢</strong>：技術領先度、創新能力、市場響應速度</li>
</ul>
<hr>
<h2 id="最佳實踐與經驗總結">最佳實踐與經驗總結</h2>
<h3 id="成功關鍵因素">成功關鍵因素</h3>
<h5 id="1-充分的前期準備">1. 充分的前期準備</h5>
<ul>
<li><strong>深度調研</strong>：充分了解現狀和目標狀態</li>
<li><strong>風險評估</strong>：識別所有可能的風險點</li>
<li><strong>資源規劃</strong>：確保有足夠的時間和人力</li>
</ul>
<h5 id="2-強有力的工具支援">2. 強有力的工具支援</h5>
<ul>
<li><strong>自動化優先</strong>：能自動化的絕不手工</li>
<li><strong>驗證完整</strong>：多層次、全方位的驗證</li>
<li><strong>監控及時</strong>：即時發現問題並快速響應</li>
</ul>
<h5 id="3-有效的團隊協作">3. 有效的團隊協作</h5>
<ul>
<li><strong>責任明確</strong>：每個人都知道自己的職責</li>
<li><strong>溝通順暢</strong>：問題能快速上報和解決</li>
<li><strong>知識共享</strong>：經驗和教訓能及時分享</li>
</ul>
<h3 id="常見陷阱與避免方法">常見陷阱與避免方法</h3>
<h5 id="1-低估複雜度">1. 低估複雜度</h5>
<p><strong>陷阱：</strong> 認為簡單的語法替換就能完成遷移</p>
<p><strong>避免：</strong> 充分的現狀分析，考慮語意變化和邊界情況</p>
<h5 id="2-忽視相容性">2. 忽視相容性</h5>
<p><strong>陷阱：</strong> 急於求成，忽視向後相容性需求</p>
<p><strong>避免：</strong> 設計完整的相容性策略，考慮過渡期需求</p>
<h5 id="3-工具過度依賴">3. 工具過度依賴</h5>
<p><strong>陷阱：</strong> 期望工具能解決所有問題</p>
<p><strong>避免：</strong> 正確認識工具的能力邊界，準備人工處理方案</p>
<h5 id="4-缺乏回滾計畫">4. 缺乏回滾計畫</h5>
<p><strong>陷阱：</strong> 只考慮成功情況，沒有失敗應對方案</p>
<p><strong>避免：</strong> 每個階段都準備回滾方案，並定期演練</p>]]></content:encoded></item><item><title>在文章中加入圖片的語法</title><link>https://tarrragon.github.io/blog/posts/%E5%9C%A8%E6%96%87%E7%AB%A0%E4%B8%AD%E5%8A%A0%E5%85%A5%E5%9C%96%E7%89%87%E7%9A%84%E8%AA%9E%E6%B3%95/</link><pubDate>Wed, 17 Sep 2025 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/posts/%E5%9C%A8%E6%96%87%E7%AB%A0%E4%B8%AD%E5%8A%A0%E5%85%A5%E5%9C%96%E7%89%87%E7%9A%84%E8%AA%9E%E6%B3%95/</guid><description>&lt;h2 id="在文章中引用assets的圖片">在文章中引用assets的圖片&lt;/h2>
&lt;p>我現在的做法是assets第一層資料夾是大分類，第二層資料夾一個文章一個資料夾，然後每個文章使用的圖片集中那個資料夾&lt;/p></description><content:encoded><![CDATA[<h2 id="在文章中引用assets的圖片">在文章中引用assets的圖片</h2>
<p>我現在的做法是assets第一層資料夾是大分類，第二層資料夾一個文章一個資料夾，然後每個文章使用的圖片集中那個資料夾</p>
<h2 id="語法">語法</h2>
<p>1.使用 Hugo 的圖片處理功能</p>





<pre tabindex="0"><code class="language-makdown" data-lang="makdown"><figure><img src="/blog/work-log/flutter_toggle_button/ToggleButtons.png"
    alt="ToggleButtons 樣式">
</figure>
</code></pre><p>2.使用標準 Markdown 語法</p>





<pre tabindex="0"><code class="language-makdown" data-lang="makdown">![ToggleButtons 樣式](/work-log/flutter_toggle_button/ToggleButtons.png)</code></pre><p>3.使用 Hugo 的圖片 shortcode</p>





<pre tabindex="0"><code class="language-makdown" data-lang="makdown"><figure><img src="/blog/work-log/flutter_toggle_button/ToggleButtons.png"
    alt="ToggleButtons 樣式" width="600"><figcaption>
      <p>Flutter ToggleButtons 元件樣式展示</p>
    </figcaption>
</figure>
</code></pre><h2 id="重要注意事項">重要注意事項</h2>
<p>1.圖片路徑：在 Hugo 中，assets 資料夾的內容會被處理並放在網站根目錄下，所以路徑是 /work-log/flutter_toggle_button/ToggleButtons.png</p>
<p>2.圖片優化：Hugo 會自動處理圖片優化，但你可以透過 shortcode 參數來控制大小和品質</p>
<p>3.響應式設計：使用 <figure><img src="">
</figure>
 shortcode 可以確保圖片在不同裝置上都能正確顯示</p>]]></content:encoded></item></channel></rss>