我們用 SMART——Specific(具體)、Measurable(可衡量)、Achievable(可達成)、Relevant(具關聯)、Time-bound(有時限)——把每個故事拆成五天,作為每次動手造輪子前的五個檢查問題。
本篇是故事一「客戶要加個按鈕切換 LED 呼吸燈,一個月後 RD 還在談 CMSIS 與 Clean Code」的 Specific 篇:現在真正要解決的是什麼問題?
本篇定位:故事一的開場,還原一個小需求如何在被看懂之前,先被升級成大型架構議題。
客戶的需求只有一句話:在既有設備上加一顆按鈕,讓 LED 能在常亮、熄滅與呼吸燈三種效果之間切換。設備已經量產出貨,硬體上留有預備的按鍵位置,聽起來就是一個「加個功能」等級的修改。
我當時是剛結束實習、轉正沒多久的菜鳥,第一次全程參與這種需求討論。會議前十分鐘大家還在談按鈕,十分鐘後白板上已經是另一個世界:這份韌體(firmware)沒有依 CMSIS 的慣例分層、驅動和應用邏輯攪在一起、命名新舊混雜、缺一套像樣的狀態機(state machine)框架。每一句都是事實,我也跟著點頭——那份程式碼確實不漂亮。於是結論很自然地變成:「既然要動,不如趁這次整理乾淨。」
一個月後,分層圖畫了好幾版,愈畫愈完整;命名規則訂出來了,資料夾結構重排了,但按鈕還是不能正常切換 LED。回頭看,最讓我印象深刻的不是進度延誤,而是整個過程沒有人偷懶。每個人都很認真,加班討論、查規範、畫圖——認真地解一個客戶從頭到尾沒有提出的問題。
那場會議真正的問題,是把三種東西攪在同一鍋:客戶的功能需求(按鈕能切換 LED)、確實存在的技術債(程式結構混亂)、以及工程師的技術理想(想留下一份漂亮的韌體)。三者都是真的,但只有第一種有交付期限、有人付錢;我們卻默默讓後兩種插隊,還讓它們掛在同一個時程上。
讓範圍失守的關鍵字是「順便」。加按鈕得碰 GPIO,碰 GPIO 就看見驅動寫得亂,看見亂就想分層,想分層就談到 CMSIS 相容性——每一步單獨看都合理,串起來卻讓一個開關需求長成重構專案。「順便」最危險的地方在於它不需要任何人批准:沒有人決議要重構,重構就已經開始了。範圍一旦沒有邊界,「完成」就沒有定義,時程自然也失去意義。
更諷刺的是,這顆按鈕並非毫無技術細節。防彈跳(debounce)要不要做、短按與長按如何區分、重開機後狀態要不要保留——這些會直接改變產品行為、真正需要跟客戶確認的問題,那一個月裡幾乎沒人討論。我們有力氣爭論命名規則,卻沒有人回頭問客戶一個問題。小功能之所以容易變成重構入口,不是因為它技術上困難,而是因為它太小了,小到大家覺得「反正很快」,於是值得順便夾帶所有想做很久的事。
Specific 這一步要做的事很不起眼:在動任何程式之前,把需求改寫成一句可以唸給客戶聽、客戶也能點頭的使用情境。例如:「操作人員短按面板上這顆按鈕時,旁邊的狀態 LED 依序在常亮、呼吸、熄滅之間循環切換。」哪一顆燈、哪一種操作、產生什麼變化,一句話說完。說不完,代表需求還沒看懂;說得完,這句話就是之後所有取捨的量尺,也輪不到架構先出場。
有了這句話,桌上的工作就能分成三堆。第一堆是這次非做不可的:按鍵輸入、LED 控制、狀態切換,加上前面那些未釐清的行為細節,逐項找客戶確認。第二堆是會直接擋住這次修改的技術債——真的擋路才算,例如中斷處理亂到無法安全加入按鍵事件。第三堆是可以另開工單的改善:CMSIS 對齊、全面分層、命名統一。把它們放進第三堆不是否定其價值——CMSIS 在跨晶片移植、新專案起步時是合理投資——而是承認它們和這顆按鈕分屬兩張工單、兩條時程、兩種驗收方式。分堆的結果應該留下文字紀錄,哪怕只是一頁工單描述或簡短的 ADR(Architecture Decision Record),讓「這次不做什麼」和「要做什麼」一樣明確。
小功能常被當成重構的入場券,多半不是惡意,也不是能力問題,而是熟悉感作祟:面對一份陌生又混亂的程式,「先整理成我熟悉的樣子」比「在混亂中做最小修改」更有控制感。但客戶付錢買的是那顆按鈕的行為改變,不是我們的安全感。把需求還原成一句使用情境,就是強迫自己先回答「這次到底要改變什麼」,再決定值不值得為它打開整棟房子的牆。
不過,一句使用情境只回答了「要做什麼」,還沒回答「怎樣才算做完」——LED 有亮起來,就代表按鈕能用了嗎?連按、長按、彈跳、斷電重開之後呢?這是明天要處理的問題。