<?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>State-Matrix on Tarragon</title><link>https://tarrragon.github.io/blog/tags/state-matrix/</link><description>Recent content in State-Matrix on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Fri, 19 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/state-matrix/index.xml" rel="self" type="application/rss+xml"/><item><title>畫面狀態矩陣的定義與填寫方法</title><link>https://tarrragon.github.io/blog/ux-design/01-screen-state-machine/state-matrix-definition/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ux-design/01-screen-state-machine/state-matrix-definition/</guid><description>&lt;p>&lt;a href="https://tarrragon.github.io/blog/ux-design/knowledge-cards/screen-state-matrix/" data-link-title="畫面狀態矩陣" data-link-desc="說明用四欄表格（顯示/可用操作/進入條件/退出路徑）系統性地暴露畫面導航缺口的設計工具">畫面狀態矩陣&lt;/a>是一張表格，每行代表一個畫面的一個狀態，每列描述該狀態的四個面向：顯示了什麼、使用者能做什麼操作、什麼條件進入這個狀態、怎麼離開。這張表格的目的是在實作前暴露導航缺口 — 退出路徑欄位為空代表使用者一旦進入就出不去。&lt;/p>
&lt;h2 id="四欄定義">四欄定義&lt;/h2>
&lt;h3 id="顯示">顯示&lt;/h3>
&lt;p>使用者看到的畫面元素。進度指示器、錯誤訊息、表單欄位、資料列表 — 任何視覺上呈現的內容。&lt;/p>
&lt;h3 id="可用操作">可用操作&lt;/h3>
&lt;p>使用者在這個狀態下能執行的動作。按鈕、手勢、鍵盤輸入、下拉選單選擇。重點是「能做什麼」，不是「看到什麼」。&lt;/p>
&lt;p>「顯示」和「可用操作」的區別在於互動性。顯示一段錯誤訊息和顯示一個重試按鈕都是「顯示」；按下重試按鈕觸發重新連線是「可用操作」。&lt;/p>
&lt;h3 id="進入條件">進入條件&lt;/h3>
&lt;p>什麼事件或動作讓畫面進入這個狀態。使用者操作（點擊按鈕）、系統事件（連線成功）、外部事件（推播通知）。&lt;/p>
&lt;h3 id="退出路徑">退出路徑&lt;/h3>
&lt;p>使用者如何離開這個狀態。返回上一頁、導航到另一個畫面、取消當前操作、完成流程後自動轉場。&lt;/p>
&lt;p>退出路徑是這張表格中最容易遺漏的欄位。開發者設計畫面時，注意力集中在「進來後看到什麼、能做什麼」，容易忽略「怎麼離開」。&lt;/p>
&lt;h2 id="填寫步驟">填寫步驟&lt;/h2>
&lt;h3 id="第一步列出畫面的所有狀態">第一步：列出畫面的所有狀態&lt;/h3>
&lt;p>從程式碼中的狀態管理機制取得狀態清單。Flutter 的 enum、React 的 state、Vue 的 reactive data — 任何控制畫面呈現的狀態變數。&lt;/p>
&lt;p>如果沒有明確的狀態 enum，從畫面的視覺變化反推：loading 時長什麼樣、成功時長什麼樣、失敗時長什麼樣。每種不同的視覺呈現就是一個狀態。&lt;/p>
&lt;h3 id="第二步每個狀態填四欄">第二步：每個狀態填四欄&lt;/h3>
&lt;p>逐一填寫每個狀態的顯示、可用操作、進入條件、退出路徑。填不出來的欄位先留空，留空本身就是發現。&lt;/p>
&lt;h3 id="第三步檢查退出路徑欄">第三步：檢查退出路徑欄&lt;/h3>
&lt;p>退出路徑為空的狀態是 UX 死胡同。使用者進入後無法靠自己的操作離開，只能殺掉 app 或等系統逾時。&lt;/p>
&lt;p>app_tunnel 的 Terminal 畫面有五個 enum 狀態（idle / connecting / connected / error / disconnected），每個狀態的退出路徑都是空的。使用者從首頁點 Connect Terminal 進入後，無論處於哪個狀態都無法返回首頁（&lt;a href="https://tarrragon.github.io/blog/ux-design/cases/five-states-zero-exits/" data-link-title="U.C1 Terminal 畫面五個狀態零個退出路徑" data-link-desc="Flutter app 的 Terminal 畫面有 idle/connecting/connected/error/disconnected 五個 enum 狀態，每個狀態都沒有 back 或 disconnect 按鈕 — 使用者一旦進入就出不去">U.C1&lt;/a>）。10 分鐘填完這張表格就能發現全部五個缺口（本章合成，UF-3 Derive）。&lt;/p>
&lt;h3 id="第四步檢查操作欄">第四步：檢查操作欄&lt;/h3>
&lt;p>操作欄為空的狀態可能合理（loading 時使用者等待），也可能代表缺少互動設計。loading 狀態通常應該有「取消」操作，error 狀態通常應該有「重試」和「返回」。&lt;/p>
&lt;h2 id="填寫範例">填寫範例&lt;/h2>
&lt;p>以 app_tunnel Terminal 畫面為例，修復前的矩陣如下：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>狀態&lt;/th>
 &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>idle&lt;/td>
 &lt;td>空白（自動連線）&lt;/td>
 &lt;td>無&lt;/td>
 &lt;td>進入畫面&lt;/td>
 &lt;td>無&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>connecting&lt;/td>
 &lt;td>進度指示&lt;/td>
 &lt;td>無&lt;/td>
 &lt;td>idle 自動觸發&lt;/td>
 &lt;td>無&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>connected&lt;/td>
 &lt;td>終端機 + 工具列&lt;/td>
 &lt;td>打字、特殊鍵&lt;/td>
 &lt;td>WebSocket 連線成功&lt;/td>
 &lt;td>無&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>error&lt;/td>
 &lt;td>錯誤訊息 + 重連按鈕&lt;/td>
 &lt;td>重新連線&lt;/td>
 &lt;td>連線失敗&lt;/td>
 &lt;td>無&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>disconnected&lt;/td>
 &lt;td>「連線中斷」+ 重連按鈕&lt;/td>
 &lt;td>重新連線&lt;/td>
 &lt;td>連線斷開&lt;/td>
 &lt;td>無&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>退出路徑欄全空 — 五個 UX 死胡同。修復後的矩陣應該每個狀態都有至少一條退出路徑：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>狀態&lt;/th>
 &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>idle&lt;/td>
 &lt;td>空白（自動連線）&lt;/td>
 &lt;td>取消&lt;/td>
 &lt;td>進入畫面&lt;/td>
 &lt;td>取消 → 返回首頁&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>connecting&lt;/td>
 &lt;td>進度指示 + back 按鈕&lt;/td>
 &lt;td>取消連線&lt;/td>
 &lt;td>idle 自動觸發&lt;/td>
 &lt;td>back → 返回首頁&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>connected&lt;/td>
 &lt;td>終端機 + 工具列 + back&lt;/td>
 &lt;td>打字、特殊鍵、中斷連線&lt;/td>
 &lt;td>WebSocket 連線成功&lt;/td>
 &lt;td>back / disconnect → 首頁&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>error&lt;/td>
 &lt;td>錯誤訊息 + 重連 + back&lt;/td>
 &lt;td>重新連線、返回&lt;/td>
 &lt;td>連線失敗&lt;/td>
 &lt;td>back → 返回首頁&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>disconnected&lt;/td>
 &lt;td>中斷訊息 + 重連 + back&lt;/td>
 &lt;td>重新連線、返回&lt;/td>
 &lt;td>連線斷開&lt;/td>
 &lt;td>back → 返回首頁&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="每個狀態至少一條退出路徑">每個狀態至少一條退出路徑&lt;/h2>
&lt;p>退出路徑是預設要求。即使是 connecting 這種過渡狀態，使用者也應該能取消 — 連線卡住時使用者需要能離開。iOS HIG 和 Material Design 對 modal 畫面都要求 dismiss 機制；畫面狀態矩陣的退出路徑欄是這個要求的具體檢查方式。&lt;/p></description><content:encoded><![CDATA[<p><a href="/blog/ux-design/knowledge-cards/screen-state-matrix/" data-link-title="畫面狀態矩陣" data-link-desc="說明用四欄表格（顯示/可用操作/進入條件/退出路徑）系統性地暴露畫面導航缺口的設計工具">畫面狀態矩陣</a>是一張表格，每行代表一個畫面的一個狀態，每列描述該狀態的四個面向：顯示了什麼、使用者能做什麼操作、什麼條件進入這個狀態、怎麼離開。這張表格的目的是在實作前暴露導航缺口 — 退出路徑欄位為空代表使用者一旦進入就出不去。</p>
<h2 id="四欄定義">四欄定義</h2>
<h3 id="顯示">顯示</h3>
<p>使用者看到的畫面元素。進度指示器、錯誤訊息、表單欄位、資料列表 — 任何視覺上呈現的內容。</p>
<h3 id="可用操作">可用操作</h3>
<p>使用者在這個狀態下能執行的動作。按鈕、手勢、鍵盤輸入、下拉選單選擇。重點是「能做什麼」，不是「看到什麼」。</p>
<p>「顯示」和「可用操作」的區別在於互動性。顯示一段錯誤訊息和顯示一個重試按鈕都是「顯示」；按下重試按鈕觸發重新連線是「可用操作」。</p>
<h3 id="進入條件">進入條件</h3>
<p>什麼事件或動作讓畫面進入這個狀態。使用者操作（點擊按鈕）、系統事件（連線成功）、外部事件（推播通知）。</p>
<h3 id="退出路徑">退出路徑</h3>
<p>使用者如何離開這個狀態。返回上一頁、導航到另一個畫面、取消當前操作、完成流程後自動轉場。</p>
<p>退出路徑是這張表格中最容易遺漏的欄位。開發者設計畫面時，注意力集中在「進來後看到什麼、能做什麼」，容易忽略「怎麼離開」。</p>
<h2 id="填寫步驟">填寫步驟</h2>
<h3 id="第一步列出畫面的所有狀態">第一步：列出畫面的所有狀態</h3>
<p>從程式碼中的狀態管理機制取得狀態清單。Flutter 的 enum、React 的 state、Vue 的 reactive data — 任何控制畫面呈現的狀態變數。</p>
<p>如果沒有明確的狀態 enum，從畫面的視覺變化反推：loading 時長什麼樣、成功時長什麼樣、失敗時長什麼樣。每種不同的視覺呈現就是一個狀態。</p>
<h3 id="第二步每個狀態填四欄">第二步：每個狀態填四欄</h3>
<p>逐一填寫每個狀態的顯示、可用操作、進入條件、退出路徑。填不出來的欄位先留空，留空本身就是發現。</p>
<h3 id="第三步檢查退出路徑欄">第三步：檢查退出路徑欄</h3>
<p>退出路徑為空的狀態是 UX 死胡同。使用者進入後無法靠自己的操作離開，只能殺掉 app 或等系統逾時。</p>
<p>app_tunnel 的 Terminal 畫面有五個 enum 狀態（idle / connecting / connected / error / disconnected），每個狀態的退出路徑都是空的。使用者從首頁點 Connect Terminal 進入後，無論處於哪個狀態都無法返回首頁（<a href="/blog/ux-design/cases/five-states-zero-exits/" data-link-title="U.C1 Terminal 畫面五個狀態零個退出路徑" data-link-desc="Flutter app 的 Terminal 畫面有 idle/connecting/connected/error/disconnected 五個 enum 狀態，每個狀態都沒有 back 或 disconnect 按鈕 — 使用者一旦進入就出不去">U.C1</a>）。10 分鐘填完這張表格就能發現全部五個缺口（本章合成，UF-3 Derive）。</p>
<h3 id="第四步檢查操作欄">第四步：檢查操作欄</h3>
<p>操作欄為空的狀態可能合理（loading 時使用者等待），也可能代表缺少互動設計。loading 狀態通常應該有「取消」操作，error 狀態通常應該有「重試」和「返回」。</p>
<h2 id="填寫範例">填寫範例</h2>
<p>以 app_tunnel Terminal 畫面為例，修復前的矩陣如下：</p>
<table>
  <thead>
      <tr>
          <th>狀態</th>
          <th>顯示</th>
          <th>可用操作</th>
          <th>進入條件</th>
          <th>退出路徑</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>idle</td>
          <td>空白（自動連線）</td>
          <td>無</td>
          <td>進入畫面</td>
          <td>無</td>
      </tr>
      <tr>
          <td>connecting</td>
          <td>進度指示</td>
          <td>無</td>
          <td>idle 自動觸發</td>
          <td>無</td>
      </tr>
      <tr>
          <td>connected</td>
          <td>終端機 + 工具列</td>
          <td>打字、特殊鍵</td>
          <td>WebSocket 連線成功</td>
          <td>無</td>
      </tr>
      <tr>
          <td>error</td>
          <td>錯誤訊息 + 重連按鈕</td>
          <td>重新連線</td>
          <td>連線失敗</td>
          <td>無</td>
      </tr>
      <tr>
          <td>disconnected</td>
          <td>「連線中斷」+ 重連按鈕</td>
          <td>重新連線</td>
          <td>連線斷開</td>
          <td>無</td>
      </tr>
  </tbody>
</table>
<p>退出路徑欄全空 — 五個 UX 死胡同。修復後的矩陣應該每個狀態都有至少一條退出路徑：</p>
<table>
  <thead>
      <tr>
          <th>狀態</th>
          <th>顯示</th>
          <th>可用操作</th>
          <th>進入條件</th>
          <th>退出路徑</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>idle</td>
          <td>空白（自動連線）</td>
          <td>取消</td>
          <td>進入畫面</td>
          <td>取消 → 返回首頁</td>
      </tr>
      <tr>
          <td>connecting</td>
          <td>進度指示 + back 按鈕</td>
          <td>取消連線</td>
          <td>idle 自動觸發</td>
          <td>back → 返回首頁</td>
      </tr>
      <tr>
          <td>connected</td>
          <td>終端機 + 工具列 + back</td>
          <td>打字、特殊鍵、中斷連線</td>
          <td>WebSocket 連線成功</td>
          <td>back / disconnect → 首頁</td>
      </tr>
      <tr>
          <td>error</td>
          <td>錯誤訊息 + 重連 + back</td>
          <td>重新連線、返回</td>
          <td>連線失敗</td>
          <td>back → 返回首頁</td>
      </tr>
      <tr>
          <td>disconnected</td>
          <td>中斷訊息 + 重連 + back</td>
          <td>重新連線、返回</td>
          <td>連線斷開</td>
          <td>back → 返回首頁</td>
      </tr>
  </tbody>
</table>
<h2 id="每個狀態至少一條退出路徑">每個狀態至少一條退出路徑</h2>
<p>退出路徑是預設要求。即使是 connecting 這種過渡狀態，使用者也應該能取消 — 連線卡住時使用者需要能離開。iOS HIG 和 Material Design 對 modal 畫面都要求 dismiss 機制；畫面狀態矩陣的退出路徑欄是這個要求的具體檢查方式。</p>
<p>退出路徑為空只在一種情況下合理：該狀態持續時間極短（&lt; 1 秒）且有保證的自動轉場。即使如此，仍建議保留取消操作 — 因為「極短」是在正常情況下的預期，異常情況（網路中斷、服務當機）可能讓這個狀態停留很久。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>從 BDD 操作盤點展開到狀態矩陣 → <a href="/blog/ux-design/01-screen-state-machine/bdd-to-state-matrix/" data-link-title="從 BDD 操作盤點展開到狀態矩陣" data-link-desc="五步驟把 BDD 操作盤點的「前端引導」展開成完整的畫面狀態矩陣 — 補上操作和退出這兩個容易遺漏的面向">從 BDD 操作盤點展開到狀態矩陣</a></li>
<li>路由可達性檢查 → <a href="/blog/ux-design/01-screen-state-machine/route-reachability/" data-link-title="路由可達性檢查" data-link-desc="Router 定義的路由 vs UI 實際可達的路由 — 路由存在但 UI 不可達等於死程式碼的 UX 版本">路由可達性檢查</a></li>
<li>狀態矩陣轉 widget test case → <a href="/blog/testing/04-ui-automation/" data-link-title="模組四：自動化 UI 驗證" data-link-desc="Widget test 的狀態覆蓋策略、Playwright 驗證流程、螢幕狀態 coverage">testing 模組四 UI 自動化</a></li>
<li>狀態矩陣加「可觀測性」欄位 → <a href="/blog/testing/02-client-observability/" data-link-title="模組二：客戶端可觀測性" data-link-desc="連線生命週期 log、protocol 訊息 log、使用者行為 log — log 設計是功能規格的一部分">testing 模組二 客戶端可觀測性</a></li>
<li>狀態轉換事件作為 funnel 分析原料 → <a href="/blog/monitoring/08-business-analytics/" data-link-title="模組八：行為資料的商業利用" data-link-desc="Funnel / Cohort / Attribution / A/B test / 推薦系統 / RFM — 從 debug 工具到商業資產的翻轉">monitoring 模組八 行為資料的商業利用</a></li>
</ul>
]]></content:encoded></item><item><title>從 BDD 操作盤點展開到狀態矩陣</title><link>https://tarrragon.github.io/blog/ux-design/01-screen-state-machine/bdd-to-state-matrix/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ux-design/01-screen-state-machine/bdd-to-state-matrix/</guid><description>&lt;p>BDD 操作盤點描述使用者操作的情境和預期結果，但操作盤點的格式（Given / When / Then）聚焦在「什麼情境下做什麼得到什麼」，容易漏掉畫面層級的兩個面向：每個狀態下使用者能執行哪些操作，以及如何離開當前狀態。&lt;a href="https://tarrragon.github.io/blog/ux-design/knowledge-cards/screen-state-matrix/" data-link-title="畫面狀態矩陣" data-link-desc="說明用四欄表格（顯示/可用操作/進入條件/退出路徑）系統性地暴露畫面導航缺口的設計工具">畫面狀態矩陣&lt;/a>補上這兩個面向，讓導航缺口在實作前浮現。&lt;/p>
&lt;h2 id="操作盤點的覆蓋範圍">操作盤點的覆蓋範圍&lt;/h2>
&lt;p>BDD 操作盤點通常包含：&lt;/p>
&lt;ul>
&lt;li>使用者操作（When）：「使用者點擊連線按鈕」&lt;/li>
&lt;li>前端引導（Then）：「顯示連線進度指示」&lt;/li>
&lt;li>後端回應：「WebSocket 連線建立」&lt;/li>
&lt;/ul>
&lt;p>「前端引導」描述的是畫面的顯示內容 — 對應狀態矩陣的「顯示」欄。但操作盤點通常不會展開：連線中的畫面除了顯示進度指示，使用者能做什麼？如果連線失敗，使用者怎麼離開失敗畫面？&lt;/p>
&lt;p>app_tunnel 的操作盤點在「前端引導」欄寫了「連線失敗顯示無法連線」，覆蓋了 error 狀態的顯示。但是「顯示無法連線之後使用者能做什麼」和「使用者怎麼離開這個畫面」都沒有描述。實作出來的 error 狀態有重連按鈕但沒有 back 按鈕 — 重連失敗時使用者被困在 error → retry → error 循環裡（&lt;a href="https://tarrragon.github.io/blog/ux-design/cases/five-states-zero-exits/" data-link-title="U.C1 Terminal 畫面五個狀態零個退出路徑" data-link-desc="Flutter app 的 Terminal 畫面有 idle/connecting/connected/error/disconnected 五個 enum 狀態，每個狀態都沒有 back 或 disconnect 按鈕 — 使用者一旦進入就出不去">U.C1&lt;/a>）。&lt;/p>
&lt;h2 id="展開步驟">展開步驟&lt;/h2>
&lt;h3 id="步驟一從操作盤點抽出所有畫面">步驟一：從操作盤點抽出所有畫面&lt;/h3>
&lt;p>每個 BDD 情境至少涉及一個畫面。列出所有情境提到的畫面名稱，去重後得到畫面清單。&lt;/p>
&lt;p>例：app_tunnel 的操作盤點涉及三個畫面 — 首頁、配對畫面、終端機畫面。&lt;/p>
&lt;h3 id="步驟二每個畫面列出狀態">步驟二：每個畫面列出狀態&lt;/h3>
&lt;p>從操作盤點的 Given / When / Then 條件中抽出狀態。「Given 連線已建立」暗示有 connected 狀態；「Then 顯示無法連線」暗示有 error 狀態。&lt;/p>
&lt;p>同時檢查程式碼中的狀態 enum — 操作盤點可能遺漏了某些狀態（如 idle、disconnected），程式碼裡有但操作盤點沒提到的狀態同樣需要設計 UI。&lt;/p>
&lt;h3 id="步驟三每個狀態填顯示欄">步驟三：每個狀態填「顯示」欄&lt;/h3>
&lt;p>從操作盤點的「前端引導」直接填入。這一步通常不缺資料，因為操作盤點的強項就是描述顯示內容。&lt;/p>
&lt;h3 id="步驟四每個狀態填可用操作和退出路徑欄">步驟四：每個狀態填「可用操作」和「退出路徑」欄&lt;/h3>
&lt;p>這一步是關鍵 — 操作盤點通常不提供這些資訊，需要主動補上。&lt;/p>
&lt;p>對每個狀態問兩個問題：&lt;/p>
&lt;ul>
&lt;li>使用者在這個狀態下想做什麼？（可用操作）&lt;/li>
&lt;li>使用者怎麼離開這個狀態？（退出路徑）&lt;/li>
&lt;/ul>
&lt;p>開發者容易假設 connected 狀態下使用者只想打字，不會想返回首頁。但使用者可能想切換到配對畫面重新配對、想暫時離開做其他事、想結束當前操作。把這些可能性列出來，判斷哪些需要提供操作按鈕。&lt;/p>
&lt;h3 id="步驟五檢查矩陣的空白格">步驟五：檢查矩陣的空白格&lt;/h3>
&lt;p>退出路徑欄為空的狀態是 UX 死胡同，需要補上退出路徑。可用操作欄為空的狀態需要判斷是否合理 — loading 狀態操作欄為空可能合理，但建議至少提供取消操作。&lt;/p>
&lt;h2 id="操作盤點的描述顯示偏差">操作盤點的「描述顯示」偏差&lt;/h2>
&lt;p>操作盤點的「前端引導」傾向描述顯示（What the user sees）而非描述互動（What the user can do）。這個偏差的根源在 BDD 的 Then 語法 — Then 通常描述可觀察的結果，而「畫面顯示 X」比「使用者可以做 Y」更容易寫成可觀察的斷言。&lt;/p>
&lt;p>app_tunnel 的操作盤點就是這個模式。四個操作情境的「前端引導」都寫了顯示內容（「顯示終端機畫面」「顯示連線中」「顯示無法連線」），沒有一個寫了操作（「使用者可以取消」「使用者可以返回」）或退出路徑（「使用者可以回到首頁」）（&lt;a href="https://tarrragon.github.io/blog/ux-design/cases/five-states-zero-exits/" data-link-title="U.C1 Terminal 畫面五個狀態零個退出路徑" data-link-desc="Flutter app 的 Terminal 畫面有 idle/connecting/connected/error/disconnected 五個 enum 狀態，每個狀態都沒有 back 或 disconnect 按鈕 — 使用者一旦進入就出不去">U.C1&lt;/a>）。&lt;/p>
&lt;p>畫面狀態矩陣的四欄結構強制補上這兩個面向。從 BDD 操作盤點到畫面狀態矩陣的展開步驟，就是把「只描述顯示」擴展成「顯示 + 操作 + 退出」的過程。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>畫面狀態矩陣的完整定義 → &lt;a href="https://tarrragon.github.io/blog/ux-design/01-screen-state-machine/state-matrix-definition/" data-link-title="畫面狀態矩陣的定義與填寫方法" data-link-desc="四欄矩陣（顯示 / 可用操作 / 進入條件 / 退出路徑）的定義、填寫步驟和檢查規則 — 退出路徑為空 = UX 死胡同">畫面狀態矩陣的定義與填寫方法&lt;/a>&lt;/li>
&lt;li>路由可達性檢查 → &lt;a href="https://tarrragon.github.io/blog/ux-design/01-screen-state-machine/route-reachability/" data-link-title="路由可達性檢查" data-link-desc="Router 定義的路由 vs UI 實際可達的路由 — 路由存在但 UI 不可達等於死程式碼的 UX 版本">路由可達性檢查&lt;/a>&lt;/li>
&lt;li>想知道什麼是「假設只走 happy path」的反模式 → &lt;a href="https://tarrragon.github.io/blog/ux-design/01-screen-state-machine/anti-pattern-happy-path-only/" data-link-title="反模式：假設使用者只走 happy path" data-link-desc="為什麼開發者容易只設計 happy path 的 UI、使用者在非 happy path 狀態下被困住的機制分析、以及用狀態矩陣系統性地防止這個問題">反模式：假設使用者只走 happy path&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>BDD 操作盤點描述使用者操作的情境和預期結果，但操作盤點的格式（Given / When / Then）聚焦在「什麼情境下做什麼得到什麼」，容易漏掉畫面層級的兩個面向：每個狀態下使用者能執行哪些操作，以及如何離開當前狀態。<a href="/blog/ux-design/knowledge-cards/screen-state-matrix/" data-link-title="畫面狀態矩陣" data-link-desc="說明用四欄表格（顯示/可用操作/進入條件/退出路徑）系統性地暴露畫面導航缺口的設計工具">畫面狀態矩陣</a>補上這兩個面向，讓導航缺口在實作前浮現。</p>
<h2 id="操作盤點的覆蓋範圍">操作盤點的覆蓋範圍</h2>
<p>BDD 操作盤點通常包含：</p>
<ul>
<li>使用者操作（When）：「使用者點擊連線按鈕」</li>
<li>前端引導（Then）：「顯示連線進度指示」</li>
<li>後端回應：「WebSocket 連線建立」</li>
</ul>
<p>「前端引導」描述的是畫面的顯示內容 — 對應狀態矩陣的「顯示」欄。但操作盤點通常不會展開：連線中的畫面除了顯示進度指示，使用者能做什麼？如果連線失敗，使用者怎麼離開失敗畫面？</p>
<p>app_tunnel 的操作盤點在「前端引導」欄寫了「連線失敗顯示無法連線」，覆蓋了 error 狀態的顯示。但是「顯示無法連線之後使用者能做什麼」和「使用者怎麼離開這個畫面」都沒有描述。實作出來的 error 狀態有重連按鈕但沒有 back 按鈕 — 重連失敗時使用者被困在 error → retry → error 循環裡（<a href="/blog/ux-design/cases/five-states-zero-exits/" data-link-title="U.C1 Terminal 畫面五個狀態零個退出路徑" data-link-desc="Flutter app 的 Terminal 畫面有 idle/connecting/connected/error/disconnected 五個 enum 狀態，每個狀態都沒有 back 或 disconnect 按鈕 — 使用者一旦進入就出不去">U.C1</a>）。</p>
<h2 id="展開步驟">展開步驟</h2>
<h3 id="步驟一從操作盤點抽出所有畫面">步驟一：從操作盤點抽出所有畫面</h3>
<p>每個 BDD 情境至少涉及一個畫面。列出所有情境提到的畫面名稱，去重後得到畫面清單。</p>
<p>例：app_tunnel 的操作盤點涉及三個畫面 — 首頁、配對畫面、終端機畫面。</p>
<h3 id="步驟二每個畫面列出狀態">步驟二：每個畫面列出狀態</h3>
<p>從操作盤點的 Given / When / Then 條件中抽出狀態。「Given 連線已建立」暗示有 connected 狀態；「Then 顯示無法連線」暗示有 error 狀態。</p>
<p>同時檢查程式碼中的狀態 enum — 操作盤點可能遺漏了某些狀態（如 idle、disconnected），程式碼裡有但操作盤點沒提到的狀態同樣需要設計 UI。</p>
<h3 id="步驟三每個狀態填顯示欄">步驟三：每個狀態填「顯示」欄</h3>
<p>從操作盤點的「前端引導」直接填入。這一步通常不缺資料，因為操作盤點的強項就是描述顯示內容。</p>
<h3 id="步驟四每個狀態填可用操作和退出路徑欄">步驟四：每個狀態填「可用操作」和「退出路徑」欄</h3>
<p>這一步是關鍵 — 操作盤點通常不提供這些資訊，需要主動補上。</p>
<p>對每個狀態問兩個問題：</p>
<ul>
<li>使用者在這個狀態下想做什麼？（可用操作）</li>
<li>使用者怎麼離開這個狀態？（退出路徑）</li>
</ul>
<p>開發者容易假設 connected 狀態下使用者只想打字，不會想返回首頁。但使用者可能想切換到配對畫面重新配對、想暫時離開做其他事、想結束當前操作。把這些可能性列出來，判斷哪些需要提供操作按鈕。</p>
<h3 id="步驟五檢查矩陣的空白格">步驟五：檢查矩陣的空白格</h3>
<p>退出路徑欄為空的狀態是 UX 死胡同，需要補上退出路徑。可用操作欄為空的狀態需要判斷是否合理 — loading 狀態操作欄為空可能合理，但建議至少提供取消操作。</p>
<h2 id="操作盤點的描述顯示偏差">操作盤點的「描述顯示」偏差</h2>
<p>操作盤點的「前端引導」傾向描述顯示（What the user sees）而非描述互動（What the user can do）。這個偏差的根源在 BDD 的 Then 語法 — Then 通常描述可觀察的結果，而「畫面顯示 X」比「使用者可以做 Y」更容易寫成可觀察的斷言。</p>
<p>app_tunnel 的操作盤點就是這個模式。四個操作情境的「前端引導」都寫了顯示內容（「顯示終端機畫面」「顯示連線中」「顯示無法連線」），沒有一個寫了操作（「使用者可以取消」「使用者可以返回」）或退出路徑（「使用者可以回到首頁」）（<a href="/blog/ux-design/cases/five-states-zero-exits/" data-link-title="U.C1 Terminal 畫面五個狀態零個退出路徑" data-link-desc="Flutter app 的 Terminal 畫面有 idle/connecting/connected/error/disconnected 五個 enum 狀態，每個狀態都沒有 back 或 disconnect 按鈕 — 使用者一旦進入就出不去">U.C1</a>）。</p>
<p>畫面狀態矩陣的四欄結構強制補上這兩個面向。從 BDD 操作盤點到畫面狀態矩陣的展開步驟，就是把「只描述顯示」擴展成「顯示 + 操作 + 退出」的過程。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>畫面狀態矩陣的完整定義 → <a href="/blog/ux-design/01-screen-state-machine/state-matrix-definition/" data-link-title="畫面狀態矩陣的定義與填寫方法" data-link-desc="四欄矩陣（顯示 / 可用操作 / 進入條件 / 退出路徑）的定義、填寫步驟和檢查規則 — 退出路徑為空 = UX 死胡同">畫面狀態矩陣的定義與填寫方法</a></li>
<li>路由可達性檢查 → <a href="/blog/ux-design/01-screen-state-machine/route-reachability/" data-link-title="路由可達性檢查" data-link-desc="Router 定義的路由 vs UI 實際可達的路由 — 路由存在但 UI 不可達等於死程式碼的 UX 版本">路由可達性檢查</a></li>
<li>想知道什麼是「假設只走 happy path」的反模式 → <a href="/blog/ux-design/01-screen-state-machine/anti-pattern-happy-path-only/" data-link-title="反模式：假設使用者只走 happy path" data-link-desc="為什麼開發者容易只設計 happy path 的 UI、使用者在非 happy path 狀態下被困住的機制分析、以及用狀態矩陣系統性地防止這個問題">反模式：假設使用者只走 happy path</a></li>
</ul>
]]></content:encoded></item></channel></rss>