「他們兩個很熟」翻譯成工程語言是:這條介面的唯一一份規格書,存放在兩個人的交情裡——不可備份,也不可轉讓。
昨天結尾問了一個問題:需求沒寫清楚,可以靠一個人腦補;那兩個團隊之間的介面呢?今天就來看介面。案例照舊經過去識別化與合併改寫。
某個系統整合案裡有兩個子系統,一個管訂單,一個管帳務,分屬兩個團隊。每次改版,兩邊都要對介接的資料格式做調整。
照理說,這叫跨團隊介面變更,該有一場會、一頁定義、一次雙方確認。
實際上,它是一段十秒鐘的茶水間對話。訂單那邊的資深工程師,遇到帳務那邊的資深工程師——兩人是老戰友,從上上個案子一路打仗到現在:
「這次欄位跟上次一樣?」
「一樣,多一個 status。」
「行。」
會議結束。沒有文件,沒有範例,沒有錯誤格式,沒有時區、重送、空值的討論。
兩週後聯調,一次就通。PM 在週報上寫:「整合順利。」
年底,帳務那邊的資深工程師調往其他部門。
下一次改版,接手的工程師問了一句:「我們跟訂單那邊的介面文件在哪?」得到的答案是:「沒有欸,以前都是他們兩個講一講。」
於是他做了這個處境下唯一能做的事:翻程式碼,照上一版的樣子推測。訂單那邊也一樣,照著「上次的印象」改。
兩邊各改各的。聯調當天,資料對不上。
status 這邊當成三種值寫死了對應,那邊上一版已經悄悄加了第四種;時間欄位一邊帶時區、一邊不帶;對方重送的時候要不要擋重複,兩邊都以為是對方的事。
聯調炸了三天。兩邊都確定自己沒改壞,所以都確定是對方改壞的。
第三天,有人想起那句「以前都是他們兩個講一講」,打了通電話,把調走的那位請回來。他站在白板前講了二十分鐘,把當年談定的約定重講一遍——兩邊這才發現,自己手上的「印象」各缺了不一樣的角。
當天傍晚,介面通了。事後檢討,結論是:「交接不確實。」
注意這兩次的記帳方式。十秒對話換來一次就通,週報上叫「整合順利」;三天聯調換來一片狼藉,檢討會上叫「交接不確實」。兩個結論都沒提到同一件事實:
這條介面,從頭到尾沒有被定義過。
當時兩個團隊的共識大概是:他們兩個那麼熟,開介面會議是浪費彼此時間;介面文件寫了也會過期,要看就看 code,code 不會騙人;我們敏捷,重視的是人與人的互動,不是流程與文件;而且事實勝於雄辯——一次就通,默契就是最好的介面管理。
每一句單獨看都有幾分道理。合在一起,它們把一份工程契約,換成了一段私人交情。
出事之後的解讀也很一致:「新人還不熟。」你可能發現了,這跟昨天那句「還好有他在」是同一種句型——把結構問題,讀成個人問題。
先替那段十秒對話平反:它的資訊量其實非常大。
「跟上次一樣」五個字,引用的是兩人多年合作累積出來的完整介面規格——每個欄位、每個狀態值的意義、出錯時的行為、誰該擋重複。這五個字是一個指標,指向只存在兩顆腦袋裡的共享記憶。要解開這個指標,兩個人都得在場。
所以那不是「沒有介面定義」。定義一直都在,而且相當精確。問題出在儲存位置。
換句話說,他們不是不寫契約。他們是用交情把契約內化了,內化到看起來不需要契約。
這就是為什麼「熟人合作不用契約」是一個危險的觀察:它是真的,但它推不出「契約沒有用」。介面契約(Interface Contract)本來就不是寫給熟人看的。
契約的意義,是讓不熟的人也能正確合作。
Waterfall 對這件事的處理很直白:介面定義是設計階段的產物,兩個團隊動工之前,交界處長什麼樣要先寫下來、凍結,要改就走 Change——因為 Predictive 的承諾是估算出來的,而交界沒定義,兩邊的估算就是兩份互相矛盾的猜測。
Agile 把它變輕,但沒有把它豁免:一頁 schema、一組雙方都跑得動的範例、一份跟著版本走的變更紀錄,形式隨你選。Individuals and interactions 的意思是「用對話把契約談出來」,不是「用交情把契約收進腦袋」。
兩套方法沒有任何一套主張:跨團隊的交界,可以只存在兩個人的關係裡。
因為交情這種儲存介質,有三個工程上的致命屬性:不可備份、不可轉讓,而且會隨人事異動自動刪除。
那條介面能順跑好幾年,靠的是兩顆腦袋裡各存了一份規格:
欄位與型別
→ 他記得每個欄位當年為什麼長那樣,
包括那個永遠是空的保留欄位
語意
→ status 前三種值的意思,是某次事故後兩人在白板前談定的;
白板當天就擦掉了
錯誤格式
→ 他知道對方收到壞資料會回什麼,
因為那段防禦寫法是他當年建議的
時序與重試
→ 「對方會重送,所以我這邊要擋重複」——
這條規則只存在他的程式碼裡,而且沒有註解
版本與變更方式
→ 所謂變更管理,就是改之前打給對方
對照一下等一下的 Artifact 就會發現:最小介面契約需要的五行,一行都沒缺。全部存在,全部精確,全部不在紙上。
於是每次十秒對話,其實是兩份腦內規格在做差異同步——只傳 diff,因為全量版本雙方都有。旁人看到的是默契,實際上是一套高度壓縮、只有兩個人持有解碼器的通訊協定。
年底人事命令一發,解碼器少了一半。剩下那半份規格並沒有錯,只是再也沒有人能完整讀出它。
老規矩。這條介面多年沒出事,改版照跑、時程照走,那「跨團隊介面沒有定義」的成本,記在哪一格?
Scope □ 介接範圍一項沒少
Time □ 時程表上從來沒有「介面定義」這項;炸掉的三天記在「聯調」名下
Cost □ 沒有人為此追加預算
Quality □ 這次剛好炸在聯調——算走運,再晚一步就炸在帳務資料上
Risk □ 兩個人的交情是全案最大的單點故障,但風險清單上沒有這一條
人 ■ 前期靠兩人的交情撐,後期靠三天聯調加班收 ← 又是這格
值得多看一眼的是「前期」那半句。這一格在爆炸之前就已經是 ■ 了——每一次十秒對話,消耗的都是那兩個人多年累積的共享記憶與信任。那是一種資產,一種從來沒出現在任何報表上的資產。組織用了它很多年,不用付費,也就從來不知道自己在用。
直到資產跟著人事命令一起離開,帳單才第一次寄到。
不是一份五十頁的介面規格書。是任何兩個團隊的交界處,至少有一頁東西,讓不熟的人拿著它就能正確介接。
判斷標準只有一條:
一個跟兩邊都不熟的工程師,只看這一頁,能不能不打電話就把介面接對?
能,這條介面才算被定義過。不能,那你們擁有的不是介面定義,是兩個人的交情——而交情的問題,我們剛剛才看過。
把那一頁收成五行。任何跨團隊交界動工之前,兩邊一起填:
□ 欄位與型別:收/送哪些欄位、什麼型別、可否為空____
□ 語意:每個關鍵欄位(尤其是狀態值)代表什麼、由哪邊改變它____
□ 錯誤格式:出錯時對方會看到什麼(代碼、格式、一個實際範例)____
□ 時序與重試:誰先誰後、會不會重送、重複收到怎麼辦____
□ 版本與變更方式:現在第幾版、要改先告訴誰、記在哪____
寫在 wiki、repo 裡的一頁 markdown、甚至聯調前貼在群組的一則訊息,都可以。重點不是格式,是這五行從此有了人腦以外的副本。
順帶一提,第五行填「改之前打給某某」也行——至少你誠實面對了現況:這條介面的變更管理,目前建立在一個人的電話號碼上。
默契是契約的一種儲存格式:讀取速度極快,但不可備份、不可轉讓,而且會跟著人事異動一起消失。
介面的洞,這次也被交情補完了。但第二部的盤點才過兩天,介面之外,還有一個更常被腦補的東西——一個功能做到什麼程度,算做完?
明天:沒有 Acceptance Criteria,資深工程師知道怎樣算完成。