<?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>Technical-Debt on Tarragon</title><link>https://tarrragon.github.io/blog/tags/technical-debt/</link><description>Recent content in Technical-Debt on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Fri, 17 Jul 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/technical-debt/index.xml" rel="self" type="application/rss+xml"/><item><title>U.C20 管理模式操作全是佔位 — dev toast 讓未接線看起來有反應</title><link>https://tarrragon.github.io/blog/ux-design/cases/management-actions-placeholder-only/</link><pubDate>Fri, 17 Jul 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/ux-design/cases/management-actions-placeholder-only/</guid><description>&lt;p>書庫管理 app 的管理模式有完整的批次操作 domain 服務、畫面上也有一整排批次操作按鈕 — 兩者之間的接線全數是佔位。佔位 handler 的風險是雙重的：對使用者，可點的假按鈕比沒有按鈕更糟（按鈕的存在承諾功能存在）；對開發者，接了 dev toast / log 的佔位在自測時「有反應」，讓未接線在驗收前不可見。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>驗收回報「右下按鈕按了沒反應」；管理模式批次操作 UI 的盤點結果：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>UI 元件&lt;/th>
 &lt;th>onPressed 實際行為&lt;/th>
 &lt;th>位置&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>右下浮動按鈕 FAB（「批次操作」）&lt;/td>
 &lt;td>只彈開發用的點擊事件測試 toast&lt;/td>
 &lt;td>&lt;code>library_display_extensions.dart:62-66&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>底部欄「編輯」「分享」「刪除」&lt;/td>
 &lt;td>只寫 log「&amp;hellip;尚未實作」&lt;/td>
 &lt;td>&lt;code>library_display_page.dart:129,144,159&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>AppBar「更多選項」&lt;/td>
 &lt;td>只寫 log「尚未實作」&lt;/td>
 &lt;td>&lt;code>library_display_page.dart:63-67&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>對照規格（SPEC-006 FR-9 / FR-10）：批次操作的 domain 層已完整實作 — &lt;code>LibraryManagementService.performBatchOperation&lt;/code>（刪除 / 改來源 / 改重要度 / 增刪標籤）與 Command 模式的編輯服務（undo / redo）都在 — &lt;strong>是 UI 到 service 的最後一段接線沒做&lt;/strong>。團隊已有「佔位實作」的追蹤票（佔位掃描盲區改善），本案是同類技術債在管理模式的集中呈現。&lt;/p>
&lt;p>附帶：底部欄只在「已選取至少一本」時才出現 — 驗收者若沒先勾書、連佔位的編輯 / 分享 / 刪除鍵都看不到。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>可點的假按鈕是負資產&lt;/strong>。按鈕存在 = 系統承諾功能存在，點了沒反應被讀成「壞掉」（體感同 U.C5 零回饋）— 對產品信任的傷害大於「功能還沒有」。未完成的功能在 UI 上的正確形態是隱藏、或 disabled + 說明（「即將推出」），不是可點的佔位。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>dev toast 佔位掩蓋未接線&lt;/strong>。接了 toast 的按鈕在開發自測時「有反應」— 開發回饋（toast 彈了）與使用者回饋（操作確實執行）被混為一談，佔位從此不在任何人的視野裡，直到驗收。log-only 佔位更隱形：畫面上完全無反應。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>domain 完成 + UI 佔位 = 完成度斷層&lt;/strong>。規格對照表寫「批次管理操作：已實作」指的是 service 層 — 進度報告以 domain 層為準時，UI 未接線的斷層不會出現在任何狀態欄位上，驗收是唯一暴露點。完成度要分層記帳（domain / UI 接線 / 驗收通過）。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="策略">策略&lt;/h2>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>佔位 handler 用統一可掃描的標記&lt;/strong>（固定的 dev-placeholder helper、或 &lt;code>// PLACEHOLDER:&lt;/code> 類統一註解），release 前用 grep 掃描（本案的現成過渡掃法：&lt;code>rg &amp;quot;尚未實作|DevToast&amp;quot;&lt;/code>）清零或轉成 disabled + 說明。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>未接線功能的 UI 三選一&lt;/strong>：隱藏（功能不該被發現）、disabled + 原因（讓使用者知道存在但未開放）、接線（完成它）— 可點無反應不在選項裡。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>完成度分層記帳&lt;/strong>：domain 完成、UI 接線、驗收通過是三個獨立狀態 — 「已實作」要標明層級，避免 UI 斷層藏在 domain 的完成宣告後面。&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>零回饋按鈕的原型案例 → &lt;a href="https://tarrragon.github.io/blog/ux-design/cases/export-button-zero-feedback/" data-link-title="U.C5 匯出按鈕按下零回饋 — 狀態機完備但 UI 沒接線" data-link-desc="Flutter app 匯出設定頁的確認匯出按鈕 onPressed 是空 callback，按下畫面毫無變化 — 使用者無法分辨匯出成功、進行中、還是功能根本沒做。ViewModel 的 idle/inProgress/completed/failed 狀態機早已完備，缺的只是頁面接線與三層回饋">U.C5 匯出按鈕零回饋&lt;/a>&lt;/li>
&lt;li>回饋鏈路的其他斷點 → &lt;a href="https://tarrragon.github.io/blog/ux-design/cases/async-listener-false-failure/" data-link-title="U.C9 提取成功卻誤報失敗 — 結果通知鏈路被搶通道" data-link-desc="操作實際成功、資料已寫入，UI 卻顯示失敗時使用。多 context 的訊息通道語意（誰負責回應）是結果通知鏈路的一部分，async listener 搶通道會把 undefined 當成回應送回">U.C9 提取成功卻誤報失敗&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ux-design/cases/selection-count-layout-starvation/" data-link-title="U.C19 「已選擇」計數被版面擠壓成省略號 — 回饋死在 layout" data-link-desc="狀態文字顯示成「...」、資料層查起來卻一切正常 — 回饋鏈的最後一哩是版面，flex 寬度競爭把關鍵計數整串壓成省略號，state 正確、使用者拿到零資訊">U.C19 計數被版面擠壓&lt;/a>&lt;/li>
&lt;li>三層回饋的完整要求 → &lt;a href="https://tarrragon.github.io/blog/ux-design/06-interaction-feedback/feedback-three-layers/" data-link-title="互動回饋三層模型：點擊確認、等待指示、結果通知" data-link-desc="使用者操作後的回饋依時間分層，缺層的症狀是重複提交與重複導航 — 診斷「按了沒反應」與多步驟流程卡狀態問題的檢查框架，涵蓋按鈕級與畫面級兩個尺度。">互動回饋三層模型&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>書庫管理 app 的管理模式有完整的批次操作 domain 服務、畫面上也有一整排批次操作按鈕 — 兩者之間的接線全數是佔位。佔位 handler 的風險是雙重的：對使用者，可點的假按鈕比沒有按鈕更糟（按鈕的存在承諾功能存在）；對開發者，接了 dev toast / log 的佔位在自測時「有反應」，讓未接線在驗收前不可見。</p>
<h2 id="觀察">觀察</h2>
<p>驗收回報「右下按鈕按了沒反應」；管理模式批次操作 UI 的盤點結果：</p>
<table>
  <thead>
      <tr>
          <th>UI 元件</th>
          <th>onPressed 實際行為</th>
          <th>位置</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>右下浮動按鈕 FAB（「批次操作」）</td>
          <td>只彈開發用的點擊事件測試 toast</td>
          <td><code>library_display_extensions.dart:62-66</code></td>
      </tr>
      <tr>
          <td>底部欄「編輯」「分享」「刪除」</td>
          <td>只寫 log「&hellip;尚未實作」</td>
          <td><code>library_display_page.dart:129,144,159</code></td>
      </tr>
      <tr>
          <td>AppBar「更多選項」</td>
          <td>只寫 log「尚未實作」</td>
          <td><code>library_display_page.dart:63-67</code></td>
      </tr>
  </tbody>
</table>
<p>對照規格（SPEC-006 FR-9 / FR-10）：批次操作的 domain 層已完整實作 — <code>LibraryManagementService.performBatchOperation</code>（刪除 / 改來源 / 改重要度 / 增刪標籤）與 Command 模式的編輯服務（undo / redo）都在 — <strong>是 UI 到 service 的最後一段接線沒做</strong>。團隊已有「佔位實作」的追蹤票（佔位掃描盲區改善），本案是同類技術債在管理模式的集中呈現。</p>
<p>附帶：底部欄只在「已選取至少一本」時才出現 — 驗收者若沒先勾書、連佔位的編輯 / 分享 / 刪除鍵都看不到。</p>
<h2 id="判讀">判讀</h2>
<ol>
<li>
<p><strong>可點的假按鈕是負資產</strong>。按鈕存在 = 系統承諾功能存在，點了沒反應被讀成「壞掉」（體感同 U.C5 零回饋）— 對產品信任的傷害大於「功能還沒有」。未完成的功能在 UI 上的正確形態是隱藏、或 disabled + 說明（「即將推出」），不是可點的佔位。</p>
</li>
<li>
<p><strong>dev toast 佔位掩蓋未接線</strong>。接了 toast 的按鈕在開發自測時「有反應」— 開發回饋（toast 彈了）與使用者回饋（操作確實執行）被混為一談，佔位從此不在任何人的視野裡，直到驗收。log-only 佔位更隱形：畫面上完全無反應。</p>
</li>
<li>
<p><strong>domain 完成 + UI 佔位 = 完成度斷層</strong>。規格對照表寫「批次管理操作：已實作」指的是 service 層 — 進度報告以 domain 層為準時，UI 未接線的斷層不會出現在任何狀態欄位上，驗收是唯一暴露點。完成度要分層記帳（domain / UI 接線 / 驗收通過）。</p>
</li>
</ol>
<h2 id="策略">策略</h2>
<ol>
<li>
<p><strong>佔位 handler 用統一可掃描的標記</strong>（固定的 dev-placeholder helper、或 <code>// PLACEHOLDER:</code> 類統一註解），release 前用 grep 掃描（本案的現成過渡掃法：<code>rg &quot;尚未實作|DevToast&quot;</code>）清零或轉成 disabled + 說明。</p>
</li>
<li>
<p><strong>未接線功能的 UI 三選一</strong>：隱藏（功能不該被發現）、disabled + 原因（讓使用者知道存在但未開放）、接線（完成它）— 可點無反應不在選項裡。</p>
</li>
<li>
<p><strong>完成度分層記帳</strong>：domain 完成、UI 接線、驗收通過是三個獨立狀態 — 「已實作」要標明層級，避免 UI 斷層藏在 domain 的完成宣告後面。</p>
</li>
</ol>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>零回饋按鈕的原型案例 → <a href="/blog/ux-design/cases/export-button-zero-feedback/" data-link-title="U.C5 匯出按鈕按下零回饋 — 狀態機完備但 UI 沒接線" data-link-desc="Flutter app 匯出設定頁的確認匯出按鈕 onPressed 是空 callback，按下畫面毫無變化 — 使用者無法分辨匯出成功、進行中、還是功能根本沒做。ViewModel 的 idle/inProgress/completed/failed 狀態機早已完備，缺的只是頁面接線與三層回饋">U.C5 匯出按鈕零回饋</a></li>
<li>回饋鏈路的其他斷點 → <a href="/blog/ux-design/cases/async-listener-false-failure/" data-link-title="U.C9 提取成功卻誤報失敗 — 結果通知鏈路被搶通道" data-link-desc="操作實際成功、資料已寫入，UI 卻顯示失敗時使用。多 context 的訊息通道語意（誰負責回應）是結果通知鏈路的一部分，async listener 搶通道會把 undefined 當成回應送回">U.C9 提取成功卻誤報失敗</a>、<a href="/blog/ux-design/cases/selection-count-layout-starvation/" data-link-title="U.C19 「已選擇」計數被版面擠壓成省略號 — 回饋死在 layout" data-link-desc="狀態文字顯示成「...」、資料層查起來卻一切正常 — 回饋鏈的最後一哩是版面，flex 寬度競爭把關鍵計數整串壓成省略號，state 正確、使用者拿到零資訊">U.C19 計數被版面擠壓</a></li>
<li>三層回饋的完整要求 → <a href="/blog/ux-design/06-interaction-feedback/feedback-three-layers/" data-link-title="互動回饋三層模型：點擊確認、等待指示、結果通知" data-link-desc="使用者操作後的回饋依時間分層，缺層的症狀是重複提交與重複導航 — 診斷「按了沒反應」與多步驟流程卡狀態問題的檢查框架，涵蓋按鈕級與畫面級兩個尺度。">互動回饋三層模型</a></li>
</ul>
]]></content:encoded></item></channel></rss>