iT邦幫忙

2026 iThome 鐵人賽

DAY 11
0

程式碼會忠實記得你做了什麼,但它一個字也不會記得你為什麼這樣做。


一段很怪的程式碼

昨天說到,「怎樣算完成」的標準藏在資深工程師腦裡。今天再往下挖一層:連「當初為什麼這樣設計」,也在。

案例照舊經過去識別化與合併改寫。

某個系統的新需求,要動到一個五年前的模組。年輕工程師接下 Ticket,trace 進去,看到一段讓他皺眉的程式碼:金額在輸出之前,先被轉成字串、左邊補零、切成兩段、再重新組回來——明明一行格式化就能解決的事,繞了三層。

他做了任何一個有上進心的工程師都會做的事:開一張重構的 Ticket,附上整理乾淨的版本。Code Review 上大家看了都點頭:「原本那段的確很怪。」測試跑完,全綠。

準備合併的那個下午,資深工程師剛好從座位後面經過,瞄了一眼螢幕,停下腳步。

「不要動那段。那是為了某個客戶的對帳格式特別繞的。他們系統吃固定位數,動了,月底對帳會炸。」

辦公室安靜了幾秒——跟 Day 01 會議室裡那種安靜,是同一款。

全組回頭去查。commit 訊息:「fix format」。設計文件:沒有這段。程式碼註解:沒有。Wiki:最後更新在兩年前,而且沒提這件事。

也就是說,「這段程式碼不能動」這個判斷,整個組織裡沒有任何一個地方寫著。它這次能被攔下來,靠的是兩個條件同時成立:原作者剛好還在,而且剛好路過。


當時團隊怎麼理解這件事

事後團隊的結論是:「還好有他在。」——這句話在這個系列出現第幾次了?

當時大家的理解大概是這樣:

怪程式碼就是技術債,看到就該清,這叫工程素養;設計文件太重了,我們敏捷,程式碼本身就是文件;真有疑問,原作者就坐在那裡,問他比查文件快;而且他又還沒要離職,急什麼。

每句單獨看都有道理。合在一起,就變成一個結論:

「為什麼這樣設計」不需要寫下來,因為活的答案就坐在辦公室裡。

問題在於,活的答案有兩個特性:他會請假,而他這次攔截事故的方式,叫做「剛好路過」。


缺席的不是設計文件,是決策的脈絡

先把話說清楚:那段程式碼不是爛 code。它是一個正確的工程決策——為了遷就一個不能改的外部對帳系統,刻意選擇的繞法。

爛掉的不是決策,是決策的脈絡(Decision Context)沒有跟著存活下來。所謂脈絡,最少包含四件事:

當初面對什麼問題
→ 對方系統吃固定位數,這邊位數不足要補零

為什麼選這個做法
→ 對方系統不能改,只好我們遷就

付出了什麼代價
→ 程式碼變醜,每個新人看到都會想重構它

什麼情況下可以翻案
→ 哪天那個對帳系統退役,這段就可以刪

程式碼只保存了第一件事的「結果」,其他三件全部蒸發。所以:

程式碼是決策的結果,不是決策的紀錄。

這個問題,Waterfall 跟 Agile 各有自己的解法。瀑布把它放進 Design Specification 與 Design Review——「為什麼這樣設計」理論上要在動工前寫下來、被審過(實務上常退化成頁數競賽,Day 02 演過了)。敏捷則把它變輕,變成 ADR(Architecture Decision Record):幾行字、跟著 repo 走、在做決定的當下順手寫。

形式差很多,責任是同一個:「為什麼」必須存在於人腦之外。

Day 01 說過,Artifact 可以變輕,責任不能消失。ADR 大概是全系列最便宜的例子:它可以只有四行。而這個團隊連四行都沒有。


大神腦袋裡藏了什麼

把那句「不要動那段」拆開來看,它其實就是一份完整的 ADR,只是儲存位置錯了:

背景   → 某客戶的對帳系統吃固定格式,他記得
決定   → 當年選擇由輸出端遷就對方,他記得
代價   → 這段從此一直被誤認成爛 code,他記得
翻案條件 → 對方系統一天不換,這段一天不能動,他記得

四行,一行都不在 repo 裡,全部在他腦裡。

而且注意這次救援的觸發方式。Day 08 的大神在接需求時補洞,Day 10 的大神在交付前補洞——至少都發生在流程的固定位置。今天這位是路過。救援的觸發條件,是隨機的。

把「剛好路過」拿掉,平行世界長這樣:重構合併、上線、一切正常——直到月底對帳那天。客戶窗口打電話進來,全組查一整晚,最後在 diff 裡找到那次「把程式碼變乾淨」的合併。而檢討會的結論,大概率不會是「決策沒有留下來」,而是:

「舊程式碼不要亂動。」

這是比炸掉更貴的結局。從此每一段怪 code 都被當成「可能有理由」的怪 code,沒有人分得出哪些怪是有苦衷、哪些怪只是當年趕時間。重構停擺,技術債只進不出,codebase 變成一片要先問過原作者才能踩的地雷區。

而原作者,變成全組的人肉考古服務——他的資深,有一部分被拿來每天回答五年前的自己為什麼那樣寫。


這次到底誰在吸收代價?

老規矩。這次炸彈沒有爆,Scope、Time、Cost 表面上全都無事:

Scope       □   需求照做
Time        □   沒有延誤——因為有人剛好路過
Cost        □
Quality     □   炸彈沒拆,只是繼續埋著
Risk        ■   所有「沒被路過救到」的下一次   ← 沒有人在管理這個機率
人          ■   原作者成為終身考古服務   ← 還是這格

「人」這格,這次勾得跟前幾天不太一樣。不是加班,是一種永久的 on-call:只要決策還存在他腦裡,他寫過的每一段怪 code,就都是一張只有他能理賠的保單。他不能離職、不能久休,最好連座位都不要換——換了,就路過不到了。

而 Risk 這格,是今天真正的重點:這次被攔下來,純粹是機率站在團隊這邊。同一個團隊、同一套做法,「沒被路過救到」的版本每天都在別的地方上演。沒有 ADR 的組織不是沒有風險,是沒有在管理這個風險——它把風險外包給「剛好」。

靠大神救回來的每一次,都在替下一次沒被救到累積本金。


如果沒有大神,應該留下什麼

不是回頭補一份完整的架構文件。五年前的模組要全面補件,成本高到不會有人真的去做,這種提案的下場通常是變成另一張永遠 pending 的 Ticket。

要改的是往後的習慣:每次做出「未來會有人想翻案」的決定時,當場花五分鐘寫四行。判斷要不要寫的標準很簡單:

如果半年後有人看到這段會問「為什麼」,這個決定就值四行。

寫給誰看?寫給三種人:接手的新人、原作者離開之後的團隊,還有五年後那個早就忘記理由的原作者本人。


今日 Artifact|最小 ADR

四行,填空即可,放在 repo 裡跟程式碼一起活著:

□ 背景:當時面對的問題是____
□ 決定:我們選擇____,而不是____
□ 代價:這個選擇讓我們付出____
□ 翻案條件:當____發生時,這個決定應該重新考慮

拿今天的故事來填:背景,客戶對帳系統吃固定位數;決定,由輸出端補零遷就,而不是要求對方改格式;代價,這段程式碼會一直長得像爛 code;翻案條件,對方系統退役。

四行寫完,那句「不要動那段」就不再需要有人剛好路過,才說得出口。


今日一句

程式碼記得你做了什麼;「為什麼這樣做」只存在還沒離職的人腦裡——除非你肯在當下花五分鐘,寫四行。

設計的「為什麼」,靠原作者剛好還在。那需求呢?改了一輪又一輪之後,「現在到底承諾到哪一版」靠誰記得?

明天來看一場精彩的記憶力表演:需求一直改也沒關係,PM 記得誰說過什麼。


上一篇
Day 10|沒有 Acceptance Criteria,資深工程師知道怎樣算完成
系列文
你以為自己很敏捷,其實連瀑布都沒做好——30 天從錯誤承諾、大神救火,到沒有大神也跑得動的開發方法11
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言