同方向反覆失敗的轉折點
核心原則
第 2 次同方向失敗、停下來回報「假設可能錯了、要不要換思路」。 失敗 ≥ 2 次大多是底層假設有問題、不是執行細節有問題。繼續沿同一方向加碼(換更複雜的 selector、加 !important、再寫一層 polyfill)只會放大原本的問題。
為什麼第 2 次是轉折點
商業邏輯
第 1 次失敗常是執行細節(typo、特定 syntax、瀏覽器 cache)— 修正後可能就過。
第 2 次失敗、用同樣的方法但更小心、還是失敗 — 這個訊號的重量遠大於兩次失敗的相加。它說的是「我以為的問題不在這層、根本問題在別處」。
第 3 次以上的失敗、加上「再試一次更小心」的努力、產生的副作用會超過解決的問題:
| 嘗試次數 | 心理狀態 | 行動模式 | 可能副作用 |
|---|---|---|---|
| 1 | 信心足 | 直接做 | 無 |
| 2 | 信心動搖 | 加碼(更複雜的 selector / important) | 可控 |
| 3 | 焦慮 | 全面反擊(layers + important + polyfill) | 大 — 改動範圍擴張 |
| 4+ | 沉沒成本綁住 | 不肯放棄已寫的 | 嚴重 — 為前面的錯買單 |
第 2 次是還能優雅切換方向的最後機會。
這次任務的實際情境
觀察
要把 search scope UI 放在「搜尋框與結果之間」。我嘗試了:
| 嘗試 | 方向 | 結果 |
|---|---|---|
| 1 | Display: contents 串接 + grid-row 排序 | 失敗 — scope 跑到頁尾 |
| 2 | 加 !important 強化 grid-row | 失敗 — 沒改善 |
| 3 | Specificity 雙寫(.x.x) | 失敗 — 沒改善 |
| 4 | 加更多 display: contents 層 | 失敗 — 同樣結果 |
| 5(被使用者制止) | 「思路錯了、換方向」 | 改用 absolute 定位、一次成功 |
四次失敗都基於同一假設:「drawer 是 .pagefind-ui 的直接子節點」。實際 drawer 在 form 內。
判讀
第 2 次失敗時就應該停下來檢查假設、不該再往同方向加碼。
正確流程:第 1 次失敗修細節;第 2 次失敗用 playwright 量 DOM 確認假設;發現假設錯就立刻換方向、不要為前面的努力買單。
執行:失敗計數與行動
| 失敗次數 | 行動 |
|---|---|
| 第 1 次 | 修細節(typo、cache、syntax) |
| 第 2 次 | 停下來 — 用工具驗證底層假設(DOM tree、computed style、framework 行為) |
| 第 2 次驗證後 | 假設對 → 繼續修;假設錯 → 換方向、不為前面買單 |
關鍵是第 2 次的「停」 — 把行動從「執行更努力」切換到「驗證假設」。
內在屬性比較:四種失敗應對
| 應對 | 適用次數 | 風險 |
|---|---|---|
| 修細節再試 | 1 次 | 低 — 假設沒問題的話通常成功 |
| 停下來驗證假設 | 2 次 | 低 — 確認方向是否正確 |
| 加碼(important / 雙寫 / polyfill) | 不適用 | 高 — 假設錯時放大問題 |
| 換方向(重新設計實作) | 2 次後驗證假設錯 | 中 — 一次性成本、後續穩定 |
選擇規則:第 1 次修細節、第 2 次驗證、第 2 次後驗證假設決定繼續或換方向。不該有第 3 次同方向加碼。
假設驗證的具體方法
1. 用工具讀真實狀態
| 假設類型 | 驗證工具 |
|---|---|
| DOM 結構 | playwright browser_evaluate 讀 ancestor chain |
| Computed style | playwright getComputedStyle |
| 元素位置 | playwright getBoundingClientRect |
| Framework 行為 | 讀框架 source、看 reconcile 條件 |
2. 反問「如果假設錯了會怎樣」
| 假設 | 如果錯了 |
|---|---|
| Drawer 是 form 的 sibling | 那 grid-row 完全無效(drawer 跟 form 共用 cell) |
| Specificity 30 是上限 | 那 layers 才是解、不是雙寫 |
「如果錯了會怎樣」的答案是「跟我看到的失敗一致」 → 假設可能錯。
3. 對外回報
1我嘗試了兩次 [方向 X]、結果都 [現象 Y]。
2我的假設是 [假設 Z]、但驗證 [假設 Z] 似乎不成立。
3要不要換 [方向 W]、或是有什麼資訊我沒看到?對外回報 = 把問題放到使用者視野、避免繼續單方面加碼。
設計取捨:失敗應對的策略
四種做法、各自機會成本不同。這個專案選 A(第 2 次失敗驗證假設)當預設、其他做法在特定情境合理。
本篇是 #42 2 次門檻 抽象原則在「同方向失敗」這個面向的應用。
A:第 2 次失敗停下驗證假設(這個專案的預設)
- 機制:第 1 次修細節再試;第 2 次失敗 → 用工具驗證底層假設(DOM tree、computed style、framework 行為);驗證錯就換方向
- 選 A 的理由:早一點切換、雙方時間都省;2 次失敗的證據量足以判斷「路徑問題」
- 適合:所有除錯情境
- 代價:第 2 次後的「停下」需要心理紀律(克服繼續加碼的衝動)
B:第 4-5 次才停(沉沒成本綁住)
- 機制:繼續加碼直到使用者制止
- 跟 A 的取捨:B 給更多嘗試空間、A 早決;B 在沉沒成本累積後更難切換
- B 是反模式:沉沒成本是認知偏誤、不是合理應對 — 「再試一次更小心」的衝動是訊號、不是解法
C:第 1 次失敗就換方向(過度反應)
- 機制:每次失敗都假設方向錯、立即換
- 跟 A 的取捨:C 太敏感、A 適度;C 在「修細節就能過」的場景過度切換
- C 才合理的情境:嘗試成本極高(每次失敗 = 半天工作)— 即使單次失敗、也值得停下重新評估
D:永不換方向
- 機制:認定方向對、無限加碼
- D 是反模式:方向錯時無法收斂、最後產生脆弱的 patchwork
- 看起來吸引人的原因:心理上不想承認方向錯、繼續加碼比放棄好受
- 實際發生的代價:失敗訊號被忽略、產生脆弱的 patchwork、修復成本指數放大
判讀徵兆
| 訊號 | 該觸發的行動 | 第一個該做的事 |
|---|---|---|
| 第 2 次同方向失敗 | 停下來驗證假設 | 用 playwright / DevTools 量真實狀態 |
加 !important 解 specificity | 停 — 切換到 layers 思路 | 評估用 CSS Layers |
| 加第 2 條 polyfill 補跨瀏覽器 | 停 — 評估值不值得繼續 | 報告成本、問使用者意願 |
| 用 imperative JS 補宣告式 layout | 停 — 切換到 CSS-first 思路 | 評估能否用 grid / flex 解決 |
| 內心 OS:「再試一次更小心」 | 停 — 這是沉沒成本綁住的訊號 | 對外回報、邀請換方向 |
核心原則:第 2 次失敗的最佳行動是「驗證假設」、不是「再試一次」。早一點切換方向、節省的是雙方時間。
「再試一次」是當下便利的選項(不需要重新分析)、「驗證假設換方向」是對齊正確性的選項 — 這個反相關見 #67 寫作便利度跟意圖對齊反相關。