iT邦幫忙

2026 iThome 鐵人賽

DAY 24
0

今天講一個很小的設計,小到只有一行檢查。但我認為它是整套工廠裡最聰明的一個。

它處理的問題是:規格和程式,會慢慢對不上。

漂移是怎麼開始的

工廠建好之後,理想的流程是:先寫規格 → 跑流程 → 產出程式和測試。

但真實世界不會這麼乾淨。

會有一些東西是不走規格的:一支啟動腳本、一個資料庫遷移檔、一層框架的組裝設定。這些不是業務規則,寫規格反而累贅。也會有一些臨時的:先做個東西驗證想法,想說之後再補規格。

然後——

「之後」不會來。

那個薪資工廠的紀錄裡就有這樣的痕跡。有幾次執行的備註寫著「基礎設施非 spec」:

持久化層(資料庫接線)        ~12 min    基礎設施非 spec
框架組裝層(依賴注入設定)    ~10 min    基礎設施非 spec
操作入口(命令列包裝)        ~20 min    基礎設施非 spec

這些標記是誠實的、也是合理的。那些東西確實不該有業務規格。

但問題來了:當「不走規格」變成一個可以宣稱的選項,界線就會慢慢滑動。 今天是資料庫接線不走規格,明天是「這個功能很簡單,先做再補」,後天是「反正那份規格沒人看」。半年後你有三十個業務流程,二十份規格。而沒有人知道少的是哪十個。

那一行閘門

薪資工廠某一次執行的備註,記了這樣一段:

後補 use-case 規格(覆蓋率 5/5)
+ 結構檢查新增「每個 use case 必有 spec」的勾稽閘門
消除「有 use case 無 spec」的漂移

拆開來看,這裡發生了三件事:

一、發現漂移。 有一個業務流程做出來了,但沒有對應的規格。

二、補上。 把那份規格補寫,而且跑了覆蓋率驗證。

三——這才是關鍵——把「不會再發生」寫成機器檢查。

第三步就是那一行閘門:掃描業務流程目錄,每一個都必須在規格目錄找到對應的檔案。找不到就擋。真的就是幾行:

#!/bin/bash
# 每個 use case 必有 spec
fail=0
[ -d .dev/use-cases ] || { echo "⛔ .dev/use-cases 不存在——這支檢查等於沒跑" >&2; exit 2; }
for uc in .dev/use-cases/*.md; do
  [ -e "$uc" ] || continue
  name=$(basename "$uc" .md)
  [ -f ".dev/specs/${name}.json" ] || { echo "❌ 有 use case 無 spec:$name" >&2; fail=1; }
done
exit $fail

第二行那個 -d 檢查不能省。 目錄不存在的時候,for 迴圈一圈都不跑、fail 還是 0、腳本回報通過。那正是我在算帳那篇自承的「指向不存在的檔案,整段從不執行,連 PASS 都不印」。同一個坑,我在寫這支的時候又踩了一次。

注意它是單向的。 這一版只檢查「有 UC 沒 spec」,反過來的「有 spec 沒 UC」它抓不到。那是另一個方向的漂移,要另外寫一圈。我第一版只寫了一邊,而且沒發現。

https://ithelp.ithome.com.tw/upload/images/20260922/201782628MN55cEKob.png

為什麼這個設計聰明

因為它處理的是單向的失效。

一般的檢查是驗「這個東西對不對」。這個閘門驗的是「這個東西存不存在」。

而「不存在」是最難被發現的一類問題:

  • 程式碼寫錯了 → 測試會紅
  • 程式碼放錯目錄 → 結構檢查會擋
  • 規格沒寫 → 什麼都不會發生

Day 06 講過 UI 的沉默約束,Day 08 講過開發規範的沉默約束。這是第三種:缺漏的沉默。

而且它會隨時間累積。每多一個沒補規格的流程,工廠的覆蓋面就少一塊,而那個缺口不會自己浮出來。用一個閘門把它變成每次都會被檢查的事,成本只有一行 grep。

這件事的一般形式

我後來把這個 pattern 抽象成一句話:

凡是「A 存在就必須有對應的 B」的關係,都應該有一支腳本去掃。

同一個 pattern 可以套在很多地方:

關係 檢查
每個 use case 必有規格 掃目錄對應
每條規格的後置條件必有測試斷言 就是 Day 22 那支覆蓋率腳本
每條鐵律必有決策紀錄編號 掃編號是否存在
規範文件引用的路徑必須存在 Day 22 的文件一致性檢查
每個決策紀錄編號只能被用一次 掃重號
每個修完的 bug 必有回歸測試 Day 18 講的那條

這六條全部是同一個形狀:對應關係的完整性。

而它們全部可以用幾行腳本檢查,全部屬於第 3 層。

我建議你現在就可以拿這個 pattern 去掃自己的專案:**列出所有「A 必須有 B」的關係,然後看有幾條是靠人記得的。**我第一次做這個練習的時候,列出七條,其中六條沒有任何機器檢查。

決策紀錄的那條特別值得講

上面那張表裡有一條是「每個決策紀錄編號只能被用一次」。這條規則看起來瑣碎到有點好笑。不就是編號嗎,會撞到又怎樣?我看過一個真實的坑:兩個人在不同分支上各自新增決策紀錄,都取了下一個流水號。合併之後,兩份不同的決策用同一個編號。聽起來是小麻煩,但後果比想像中大。因為決策紀錄是被引用的東西:

  • 第 0 層的鐵律要附決策編號
  • 審查清單的「依據」欄位會指向決策
  • 程式碼註解裡會寫「見 ADR-021」

當編號指向兩個東西,這些引用全部失去意義。 而且失去意義的方式是安靜的。沒有人會收到錯誤訊息。解法很簡單:流水號只在一個索引檔登記,要開新的先去取號。而「有沒有重號」「有沒有孤兒(有編號沒檔案、有檔案沒登記)」,一支腳本掃。

這又是「規則單一來源」,只是這次的對象是編號。

一個提醒:閘門要放在對的時間點

最後講一個實務細節。

這類「完整性檢查」放在什麼時候跑,差別很大:

  • 放在寫檔之後(PostToolUse):太早。你剛寫完業務流程、還沒開始寫規格,它就紅了
  • 放在工作結束時(Stop):剛好。一段工作做完,該補的都補了才檢查
  • 放在 CI:也可以,但回饋太慢

薪資工廠那支結構檢查是掛在寫檔之後的,但這條「每個 use case 必有規格」的勾稽被放在結構檢查的第六節。也就是說它跟著結構檢查跑,但那支腳本本身是在工作告一段落時執行完整版。

檢查的內容決定它該在哪個時機跑。 便宜又即時的(命名、目錄)放前面,需要「一段工作完成」才有意義的(完整性、一致性)放後面。放錯時機的檢查會一直誤報,而誤報三次之後就沒有人看了。

小結

  • 「不走規格」一旦變成可以宣稱的選項,界線就會滑動
  • 那個工廠的做法:發現漂移 → 補上 → 把「不會再發生」寫成機器檢查
  • 「缺漏」是最難發現的失效,因為什麼都不會發生
  • 一般形式:凡是「A 存在就必須有對應的 B」,都該有腳本掃
  • 檢查的內容決定它該在哪個時機跑,放錯時機會誤報,誤報三次就沒人看了

上一篇
Day 23 - 規格格式:寫可驗證的行為,不要寫實作步驟
下一篇
Day 25 - 43 次執行:36 次一次過,還是證明不了 AI 比人強
系列文
綠燈不等於做對:AI 開發的驗收工程 ——從兩個失敗的案子,到驗收交付 共 29 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言