星期二下午,一組 Automation Case 跑完後,第二組開始出現很奇怪的結果。
同一個設定值,在第一組 Case 裡已經改成:
Mode: Manual
第二組 Case 啟動時,Log 卻又寫著:
Mode: Auto
負責人第一個反應是:
「哪一份值才是真的?」
大家開始找。
啟動環境裡有一份。
跨 Case 共用的 Context 裡有一份。
某個正在執行的 Object 裡也有一份。
三個地方都叫 Mode。
三個地方也都不是亂存的。
問題變得更怪了。
如果只能有一個 Source of Truth,那另外兩份到底為什麼存在?
工程師先把流程畫出來。
Launch Environment
↓
Shared Context
↓
Case Object
啟動時,系統先從 Environment 讀取設定。
接著放進 Shared Context,讓後面的 Case 可以共用。
每一個 Case 建立自己的 Object 時,又會把需要的值放進 Local State。
看起來很合理。
如果每次都回頭讀最上層,程式會變得很難用。
所以 local copy 本身並不是問題。
真正的裂縫出現在第一個 Case 執行到一半時。
它因為自己的條件,把 Local State 改成了:
Mode: Manual
這個修改只對當前 Case 有效。
Shared Context 沒有變。
下一個 Case 建立時,又從 Shared Context 讀到:
Mode: Auto
從程式的角度來看,兩邊都沒有錯。
但從人的角度看,就會出現一句很危險的話:
「我剛剛明明已經把 Mode 改成 Manual 了。」
這句話少了一個東西。
在哪裡改?
很多系統出現狀態不一致時,第一個處理方式是找唯一的 Source of Truth。
這個方向在很多情況下有用。
但不是所有 State 都只能有一個 Authority。
這個案例裡,三份值其實分別回答三個不同問題。
Environment
= 這次 Runtime 啟動時的初始設定是什麼?
Shared Context
= 後續 Case 共用的目前設定是什麼?
Local State
= 這一個 Case 此刻實際使用的是什麼?
它們的 Scope 不一樣。
Lifecycle 也不一樣。
所以三份資料可以同時正確。
問題不是「系統裡有三個真相」。
而是有人把只對一個 Local Object 成立的值,拿去代表整個 Runtime。
或者反過來。
拿 Shared Context 的值,去宣稱某個正在執行的 Case 此刻一定也是同一個狀態。
這時候副本才真正變成問題。
不是因為它是副本。
而是因為它被拿來回答了超出自己 Scope 的問題。
這跟前面談文件來源時其實很像。
SOP 可以證明預期流程。
Log 可以證明這次實際發生了什麼。
兩份資料都重要,但不能互相冒充。
Runtime State 也是一樣。
一個值要不要相信,不只看它是不是最新。
還要看:
Scope
它代表誰?
Owner
誰有資格改它?
Lifecycle
它的有效時間有多長?
如果這三件事沒有分清楚,最後很容易出現另一種修法:
看到 Local State 更新,就把 Shared Context 一起改掉。
看起來所有地方終於一致了。
但這可能反而把原本只應該影響單一 Case 的變更,擴散到後面的所有 Case。
數值一致了。
工作語意卻錯了。
有人原本提議:
「不然所有地方都只留一份 Mode,大家都去讀它。」
這個方案最後沒有採用。
因為三個 Scope 原本就真的有不同用途。
他們只做了一個很小的調整。
在那個最常讓人搞混的 State 旁邊,補了三欄:
State: Mode
Scope:
Case Local
Owner:
Current Case
Lifecycle:
Until Case Ends
Shared Context 那份則寫成另一組。
沒有建立新的 State Framework。
也沒有把所有 Variable 都重新設計。
只是讓看到這個值的人知道:
這份資料到底有資格代表哪一段工作。
幾天後,另一個人又在 Log 裡看到:
Shared Mode: Auto
Local Mode: Manual
他沒有立刻開 Bug。
也沒有先問哪一個才是真的。
他往旁邊看了一眼 Scope。
一個屬於 Runtime Shared State。
一個只屬於目前這個 Case。
白板上原本畫著一個大框:
Source of Truth
後來被擦掉了。
換成三個比較小的框。
每一個旁邊,都多寫了自己的 Scope。