<?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>Opinionated-Software on Tarragon</title><link>https://tarrragon.github.io/blog/tags/opinionated-software/</link><description>Recent content in Opinionated-Software on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Thu, 25 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/opinionated-software/index.xml" rel="self" type="application/rss+xml"/><item><title>工具的預設行為決定使用者習慣 — 從版本錯置看工具設計的 opinion 責任</title><link>https://tarrragon.github.io/blog/work-log/%E5%B7%A5%E5%85%B7%E7%9A%84%E9%A0%90%E8%A8%AD%E8%A1%8C%E7%82%BA%E6%B1%BA%E5%AE%9A%E4%BD%BF%E7%94%A8%E8%80%85%E7%BF%92%E6%85%A3-%E5%BE%9E%E7%89%88%E6%9C%AC%E9%8C%AF%E7%BD%AE%E7%9C%8B%E5%B7%A5%E5%85%B7%E8%A8%AD%E8%A8%88%E7%9A%84-opinion-%E8%B2%AC%E4%BB%BB/</link><pubDate>Thu, 25 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/work-log/%E5%B7%A5%E5%85%B7%E7%9A%84%E9%A0%90%E8%A8%AD%E8%A1%8C%E7%82%BA%E6%B1%BA%E5%AE%9A%E4%BD%BF%E7%94%A8%E8%80%85%E7%BF%92%E6%85%A3-%E5%BE%9E%E7%89%88%E6%9C%AC%E9%8C%AF%E7%BD%AE%E7%9C%8B%E5%B7%A5%E5%85%B7%E8%A8%AD%E8%A8%88%E7%9A%84-opinion-%E8%B2%AC%E4%BB%BB/</guid><description>&lt;p>這篇從一個版本錯置的經驗出發，討論工具設計中一個容易忽略的面向：工具接受自由輸入時，預設路徑如何影響使用者的決策。適用於 CLI、API、表單、自動化流程——任何需要使用者做選擇的介面。&lt;/p>
&lt;hr>
&lt;h2 id="背景我們怎麼管理版本和工作項目">背景：我們怎麼管理版本和工作項目&lt;/h2>
&lt;p>我們的專案用 semver（語意化版本）管理發布節奏。每個版本（如 v0.3.0）有明確的功能範圍，由數個提案定義——每個提案描述一組要交付的功能和邊界。版本內部再拆成多個工作項目（ticket），按批次排序執行（類似 Sprint，但以依賴順序而非時間框分批）。&lt;/p>
&lt;p>版本的生命週期很單純：&lt;code>planned → active → completed&lt;/code>。一個版本的所有 ticket 完成後，跑發布流程、打 tag、標記 completed。&lt;/p>
&lt;p>圍繞這個流程，我們自建了兩個 CLI 工具：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>工具&lt;/th>
 &lt;th>用途&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>ticket create&lt;/code>&lt;/td>
 &lt;td>建立工作項目，指定歸屬版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>version-release&lt;/code>&lt;/td>
 &lt;td>版本發布（pre-flight 檢查、文件更新、打 tag）&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>這兩個工具在設計時，都選擇了「彈性優先」——接受任何合法輸入，不對使用者的選擇做判斷。&lt;/p>
&lt;p>這個選擇在後來被證明是錯的。&lt;/p>
&lt;h2 id="版本語意大版本和小版本的分工">版本語意：大版本和小版本的分工&lt;/h2>
&lt;p>semver 的 &lt;code>MAJOR.MINOR.PATCH&lt;/code> 有明確的語意分工：&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>MAJOR（0.x → 1.0）&lt;/td>
 &lt;td>不相容的 API 變更&lt;/td>
 &lt;td>破壞既有介面&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>MINOR（0.3 → 0.4）&lt;/td>
 &lt;td>新功能&lt;/td>
 &lt;td>新增向後相容功能&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>PATCH（0.3.0 → 0.3.1）&lt;/td>
 &lt;td>修復和改善&lt;/td>
 &lt;td>bug fix（我們擴充涵蓋重構和流程改善）&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>版本號不只是標記——它決定了&lt;strong>工作項目應該放在哪裡&lt;/strong>。一個 bug fix 放進 MINOR 版本，語意上等於說「這個 bug fix 和下一批新功能綁定發布」——多數情況下這不是你想要的。&lt;/p>
&lt;p>版本管理只是其中一個場景——任何接受自由輸入的內部工具，只要輸入涉及分類或歸屬判斷，都可能有同樣的問題。我們的工具沒有表達這個語意，接下來的兩個事件是後果。&lt;/p>
&lt;h2 id="事件一改善類工作放進了新功能版本">事件一：改善類工作放進了新功能版本&lt;/h2>
&lt;p>v0.3.0 發布了三個新功能。發布後的版本檢討發現了一個測試隔離問題，v0.3.1 做了 hotfix。&lt;/p>
&lt;p>接下來要做根因分析和系統性防護。建立工作項目時，順手指定了 &lt;code>--version 0.4.0&lt;/code>——v0.3.0 和 v0.3.1 都已發布，v0.4.0 是下一個功能版本，看起來是合理的選擇。&lt;/p>
&lt;p>CLI 接受了這個輸入，沒有任何提示。&lt;/p>
&lt;p>三張改善類的工作項目（根因分析、重構、規則文件）就這樣和 PostgreSQL Storage Backend（v0.4.0 的核心功能）混在一起。直到使用者檢視版本看板時才發現不對——改善類工作和新功能綁在同一個發布週期，語意混亂。&lt;/p>
&lt;p>修正方式：建立 v0.3.2、遷移三張 ticket、重新發布。額外花了一輪操作成本。&lt;/p>
&lt;h2 id="事件二已完成版本的幽靈">事件二：已完成版本的幽靈&lt;/h2>
&lt;p>版本看板的異常不止一處。同一次檢視中，看板顯示 v0.2.0 有未完成任務。&lt;/p>
&lt;p>查證後發現 v0.2.0（38 張 ticket 全部完成）、v0.2.1（7 張全完成）、v0.2.2（1 張已結案）三個版本在版本清單中仍標記為 &lt;code>active&lt;/code>。它們在數個月前就該標為 &lt;code>completed&lt;/code>，但沒有。&lt;/p>
&lt;p>原因是版本發布工具的 pre-flight 檢查只看「當前版本的 ticket 是否完成」，不掃描「更早的版本是否有 active 殘留」。早期版本可能是手動發布的，跳過了狀態同步步驟。工具沒有補救機制，殘留就一直留著。&lt;/p>
&lt;p>看板靜默地把這些版本顯示為「有未完成工作」，產生誤導。&lt;/p>
&lt;h2 id="為什麼會這樣工具沒有-opinion">為什麼會這樣：工具沒有 opinion&lt;/h2>
&lt;p>兩個事件的共通根因：&lt;strong>工具在應該有立場的地方選擇了沉默。&lt;/strong>&lt;/p>
&lt;h3 id="建立工作項目時">建立工作項目時&lt;/h3>
&lt;p>&lt;code>ticket create --version 0.4.0 --type ANA --action &amp;quot;分析&amp;quot;&lt;/code> — 工具知道這是一張分析類的 ticket，也知道 v0.4.0 的 scope 是 PostgreSQL Storage。但它不認為自己有責任判斷「分析類 ticket 放在新功能版本是否合理」。它只做格式驗證：版本號存在嗎？通過就建立。&lt;/p>
&lt;h3 id="發布版本時">發布版本時&lt;/h3>
&lt;p>發布工具的盲區更隱蔽。每次發布時，工具會檢查「這個版本的所有工作項目都完成了嗎？」——如果答案是「是」，就繼續打 tag、更新文件、推送。但它從不回頭看更早的版本：有沒有哪個舊版本的工作項目早已全部完成，卻一直沒被標記為「已完成」？這種殘留不影響當前發布，但會讓看板持續顯示「舊版本有未完成工作」，誤導每一個後續查看看板的人。&lt;/p>
&lt;p>兩者都是「工具做了它被要求做的事，但沒做它應該做的事」。&lt;/p>
&lt;h2 id="工具什麼時候應該有-opinion">工具什麼時候應該有 opinion？&lt;/h2>
&lt;p>不是所有情境都需要工具有立場。有一個簡單的判斷標準：&lt;/p>
&lt;blockquote>
&lt;p>當存在一個「多數情況下正確的預設行為」時，工具應該把它表達出來。使用者可以覆蓋，但預設路徑應該引導正確做法。&lt;/p>&lt;/blockquote>
&lt;p>這裡的 opinion 是&lt;strong>建議而非阻擋&lt;/strong>——工具提示預設路徑，使用者可以覆蓋。這個區分很重要：阻擋式的 opinion（必須額外操作才能繞過）適合風險高的操作（如 force push to main、刪除生產資料）；建議式的 opinion 適合歸屬判斷。錯誤成本不對稱決定了形式：建議錯了，使用者覆蓋一次，幾秒鐘；沉默錯了，事後修正，幾小時。只要建議的正確率不是極低，建議就比沉默划算。&lt;/p>
&lt;p>這個邏輯不限於 CLI。API 的預設參數、表單的預選值、自動化流程的預設路由——任何使用者需要做選擇的介面，都有機會用預設行為表達 opinion。&lt;/p>
&lt;p>改善類 ticket 放 patch 版本，在多數情況下是正確的。「多數情況下對」已經足夠讓工具表達立場：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="err">$&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ticket&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">create&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">--type IMP --action &amp;#34;修復&amp;#34; --target &amp;#34;retry test&amp;#34;
&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="p">[&lt;/span>&lt;span class="err">建議&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="err">此&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ticket&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="err">為修復類，建議放&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">v0&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="err">（&lt;/span>&lt;span class="n">patch&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">bump&lt;/span>&lt;span class="err">）&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="err">而非&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">v0&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="err">（下一個功能版本）&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="err">使用&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c1">--version 覆蓋此建議&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>前版本 status 掃描也是。已完成版本仍為 active 在所有情況下都是異常——工具不需要猜，只需要報告：&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">$ version-release check
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">[WARN] v0.2.0：38 張 ticket 全部完成但 status 仍為 active&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="為什麼使用者是-ai-agent-時問題更嚴重">為什麼使用者是 AI agent 時問題更嚴重&lt;/h2>
&lt;p>這個 pattern 在人類使用者身上已經存在——人類也會走阻力最小的路徑。但人類有跨次記憶：「上次放錯版本被糾正過，這次注意一下。」&lt;/p></description><content:encoded><![CDATA[<p>這篇從一個版本錯置的經驗出發，討論工具設計中一個容易忽略的面向：工具接受自由輸入時，預設路徑如何影響使用者的決策。適用於 CLI、API、表單、自動化流程——任何需要使用者做選擇的介面。</p>
<hr>
<h2 id="背景我們怎麼管理版本和工作項目">背景：我們怎麼管理版本和工作項目</h2>
<p>我們的專案用 semver（語意化版本）管理發布節奏。每個版本（如 v0.3.0）有明確的功能範圍，由數個提案定義——每個提案描述一組要交付的功能和邊界。版本內部再拆成多個工作項目（ticket），按批次排序執行（類似 Sprint，但以依賴順序而非時間框分批）。</p>
<p>版本的生命週期很單純：<code>planned → active → completed</code>。一個版本的所有 ticket 完成後，跑發布流程、打 tag、標記 completed。</p>
<p>圍繞這個流程，我們自建了兩個 CLI 工具：</p>
<table>
  <thead>
      <tr>
          <th>工具</th>
          <th>用途</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>ticket create</code></td>
          <td>建立工作項目，指定歸屬版本</td>
      </tr>
      <tr>
          <td><code>version-release</code></td>
          <td>版本發布（pre-flight 檢查、文件更新、打 tag）</td>
      </tr>
  </tbody>
</table>
<p>這兩個工具在設計時，都選擇了「彈性優先」——接受任何合法輸入，不對使用者的選擇做判斷。</p>
<p>這個選擇在後來被證明是錯的。</p>
<h2 id="版本語意大版本和小版本的分工">版本語意：大版本和小版本的分工</h2>
<p>semver 的 <code>MAJOR.MINOR.PATCH</code> 有明確的語意分工：</p>
<table>
  <thead>
      <tr>
          <th>層級</th>
          <th>語意</th>
          <th>觸發條件</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>MAJOR（0.x → 1.0）</td>
          <td>不相容的 API 變更</td>
          <td>破壞既有介面</td>
      </tr>
      <tr>
          <td>MINOR（0.3 → 0.4）</td>
          <td>新功能</td>
          <td>新增向後相容功能</td>
      </tr>
      <tr>
          <td>PATCH（0.3.0 → 0.3.1）</td>
          <td>修復和改善</td>
          <td>bug fix（我們擴充涵蓋重構和流程改善）</td>
      </tr>
  </tbody>
</table>
<p>版本號不只是標記——它決定了<strong>工作項目應該放在哪裡</strong>。一個 bug fix 放進 MINOR 版本，語意上等於說「這個 bug fix 和下一批新功能綁定發布」——多數情況下這不是你想要的。</p>
<p>版本管理只是其中一個場景——任何接受自由輸入的內部工具，只要輸入涉及分類或歸屬判斷，都可能有同樣的問題。我們的工具沒有表達這個語意，接下來的兩個事件是後果。</p>
<h2 id="事件一改善類工作放進了新功能版本">事件一：改善類工作放進了新功能版本</h2>
<p>v0.3.0 發布了三個新功能。發布後的版本檢討發現了一個測試隔離問題，v0.3.1 做了 hotfix。</p>
<p>接下來要做根因分析和系統性防護。建立工作項目時，順手指定了 <code>--version 0.4.0</code>——v0.3.0 和 v0.3.1 都已發布，v0.4.0 是下一個功能版本，看起來是合理的選擇。</p>
<p>CLI 接受了這個輸入，沒有任何提示。</p>
<p>三張改善類的工作項目（根因分析、重構、規則文件）就這樣和 PostgreSQL Storage Backend（v0.4.0 的核心功能）混在一起。直到使用者檢視版本看板時才發現不對——改善類工作和新功能綁在同一個發布週期，語意混亂。</p>
<p>修正方式：建立 v0.3.2、遷移三張 ticket、重新發布。額外花了一輪操作成本。</p>
<h2 id="事件二已完成版本的幽靈">事件二：已完成版本的幽靈</h2>
<p>版本看板的異常不止一處。同一次檢視中，看板顯示 v0.2.0 有未完成任務。</p>
<p>查證後發現 v0.2.0（38 張 ticket 全部完成）、v0.2.1（7 張全完成）、v0.2.2（1 張已結案）三個版本在版本清單中仍標記為 <code>active</code>。它們在數個月前就該標為 <code>completed</code>，但沒有。</p>
<p>原因是版本發布工具的 pre-flight 檢查只看「當前版本的 ticket 是否完成」，不掃描「更早的版本是否有 active 殘留」。早期版本可能是手動發布的，跳過了狀態同步步驟。工具沒有補救機制，殘留就一直留著。</p>
<p>看板靜默地把這些版本顯示為「有未完成工作」，產生誤導。</p>
<h2 id="為什麼會這樣工具沒有-opinion">為什麼會這樣：工具沒有 opinion</h2>
<p>兩個事件的共通根因：<strong>工具在應該有立場的地方選擇了沉默。</strong></p>
<h3 id="建立工作項目時">建立工作項目時</h3>
<p><code>ticket create --version 0.4.0 --type ANA --action &quot;分析&quot;</code> — 工具知道這是一張分析類的 ticket，也知道 v0.4.0 的 scope 是 PostgreSQL Storage。但它不認為自己有責任判斷「分析類 ticket 放在新功能版本是否合理」。它只做格式驗證：版本號存在嗎？通過就建立。</p>
<h3 id="發布版本時">發布版本時</h3>
<p>發布工具的盲區更隱蔽。每次發布時，工具會檢查「這個版本的所有工作項目都完成了嗎？」——如果答案是「是」，就繼續打 tag、更新文件、推送。但它從不回頭看更早的版本：有沒有哪個舊版本的工作項目早已全部完成，卻一直沒被標記為「已完成」？這種殘留不影響當前發布，但會讓看板持續顯示「舊版本有未完成工作」，誤導每一個後續查看看板的人。</p>
<p>兩者都是「工具做了它被要求做的事，但沒做它應該做的事」。</p>
<h2 id="工具什麼時候應該有-opinion">工具什麼時候應該有 opinion？</h2>
<p>不是所有情境都需要工具有立場。有一個簡單的判斷標準：</p>
<blockquote>
<p>當存在一個「多數情況下正確的預設行為」時，工具應該把它表達出來。使用者可以覆蓋，但預設路徑應該引導正確做法。</p></blockquote>
<p>這裡的 opinion 是<strong>建議而非阻擋</strong>——工具提示預設路徑，使用者可以覆蓋。這個區分很重要：阻擋式的 opinion（必須額外操作才能繞過）適合風險高的操作（如 force push to main、刪除生產資料）；建議式的 opinion 適合歸屬判斷。錯誤成本不對稱決定了形式：建議錯了，使用者覆蓋一次，幾秒鐘；沉默錯了，事後修正，幾小時。只要建議的正確率不是極低，建議就比沉默划算。</p>
<p>這個邏輯不限於 CLI。API 的預設參數、表單的預選值、自動化流程的預設路由——任何使用者需要做選擇的介面，都有機會用預設行為表達 opinion。</p>
<p>改善類 ticket 放 patch 版本，在多數情況下是正確的。「多數情況下對」已經足夠讓工具表達立場：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="ln">1</span><span class="cl"><span class="err">$</span><span class="w"> </span><span class="n">ticket</span><span class="w"> </span><span class="k">create</span><span class="w"> </span><span class="c1">--type IMP --action &#34;修復&#34; --target &#34;retry test&#34;
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"></span><span class="p">[</span><span class="err">建議</span><span class="p">]</span><span class="w"> </span><span class="err">此</span><span class="w"> </span><span class="n">ticket</span><span class="w"> </span><span class="err">為修復類，建議放</span><span class="w"> </span><span class="n">v0</span><span class="p">.</span><span class="mi">3</span><span class="p">.</span><span class="mi">2</span><span class="err">（</span><span class="n">patch</span><span class="w"> </span><span class="n">bump</span><span class="err">）</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">       </span><span class="err">而非</span><span class="w"> </span><span class="n">v0</span><span class="p">.</span><span class="mi">4</span><span class="p">.</span><span class="mi">0</span><span class="err">（下一個功能版本）</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">       </span><span class="err">使用</span><span class="w"> </span><span class="c1">--version 覆蓋此建議</span></span></span></code></pre></div><p>前版本 status 掃描也是。已完成版本仍為 active 在所有情況下都是異常——工具不需要猜，只需要報告：</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">$ version-release check
</span></span><span class="line"><span class="ln">2</span><span class="cl">[WARN] v0.2.0：38 張 ticket 全部完成但 status 仍為 active</span></span></code></pre></div><h2 id="為什麼使用者是-ai-agent-時問題更嚴重">為什麼使用者是 AI agent 時問題更嚴重</h2>
<p>這個 pattern 在人類使用者身上已經存在——人類也會走阻力最小的路徑。但人類有跨次記憶：「上次放錯版本被糾正過，這次注意一下。」</p>
<p>AI agent 沒有這個。</p>
<p>每個 session 是一個全新的 agent，它讀到的是：版本清單中 v0.4.0 是 active、CLI 接受 <code>--version 0.4.0</code>、沒有警告。於是它每次都會用最直覺的選擇——當前 active 的最大版本。</p>
<p>上次的教訓不會自動傳遞到下次。除非教訓被固化成工具行為。</p>
<p>這把「工具應該有 opinion」從「建議做法」升級為「必要條件」：</p>
<ul>
<li><strong>人類使用者</strong>：opinion 是提醒，有助於減少錯誤</li>
<li><strong>AI agent 使用者</strong>：opinion 是最可靠的防線，因為工具在操作當下的即時引導是離決策點最近的攔截</li>
</ul>
<h2 id="工具的預設行為就是團隊的實際流程">工具的預設行為，就是團隊的實際流程</h2>
<blockquote>
<p>工具的預設行為，就是團隊的實際流程。</p></blockquote>
<p>文件上寫「改善類工作放 patch 版本」沒有用——如果工具不引導，使用者會走工具預設的路徑。人類和 AI 都是。文件說的和工具做的不一致時，工具會贏。</p>
<p>但文件不是敵人。文件定義「應該是什麼樣」，傳遞設計理由和架構決策；工具實現「實際是什麼樣」。兩者不一致時，優先修工具。</p>
<blockquote>
<p>如果你希望使用者做 X，不要寫文件說「請做 X」——把工具的預設行為設成 X。</p></blockquote>
<p>這個原則適用於所有內部工具設計，不限於版本管理：</p>
<table>
  <thead>
      <tr>
          <th>場景</th>
          <th>寫文件的做法</th>
          <th>改工具的做法</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>commit 前跑測試</td>
          <td>README 寫「請先跑測試」</td>
          <td>pre-commit hook 自動跑</td>
      </tr>
      <tr>
          <td>PR 描述格式</td>
          <td>貢獻指南寫範本</td>
          <td>PR template 預填結構</td>
      </tr>
      <tr>
          <td>改善放 patch 版本</td>
          <td>版本策略文件寫規則</td>
          <td>CLI 根據 ticket type 建議版本</td>
      </tr>
      <tr>
          <td>API 環境參數</td>
          <td>文件寫「production 需額外確認」</td>
          <td>API 預設 staging，production 需顯式指定</td>
      </tr>
      <tr>
          <td>表單必填欄位</td>
          <td>說明文字寫「建議填寫」</td>
          <td>欄位預設值 + 必填驗證</td>
      </tr>
  </tbody>
</table>
<p>每一個「寫文件提醒使用者遵守操作規範」都是一個信號——工具的預設行為還有空間改善。看到這個信號時，優先評估能否把提醒轉化為工具的預設行為。</p>
<p>Rails 的「Convention over Configuration」是同一個觀念的先驅表達：框架用約定引導開發者走正確路徑，省去不必要的配置決策。有 opinion 的工具在必要決策時引導方向。兩者共通的是把判斷成本從「每次使用時」前移到「設計工具時」——一次判斷，永久生效。</p>
<h2 id="回去檢查你的工具">回去檢查你的工具</h2>
<ol>
<li>列出你的工具中所有使用者需要做選擇的地方——CLI 參數、API 欄位、表單選項、流程分支</li>
<li>對每個問：有沒有「多數情況下正確」的預設值或建議值？</li>
<li>有的話，加建議式 opinion（提示預設 + 允許覆蓋）</li>
<li>檢查工具的清理路徑：有沒有前一次操作應該同步但沒有同步的狀態？</li>
<li>如果你的工具會被 AI agent 或自動化流程呼叫，上述每一項的優先級加倍——自動化沒有判斷力，它只走預設路徑</li>
</ol>
]]></content:encoded></item></channel></rss>