可維護性——六個月後你還改得動嗎?
一句話總結:可維護性是好產品的「隱藏維度」——使用者看不到,PM 不會特別關注,但它直接決定了產品能活多久。
結論先講:一個無法維護的產品,就像一棟沒有維修通道的大樓——剛蓋好時很漂亮,幾年後就是危樓。程式碼品質的重點不是「寫得漂亮」,是「別人能不能快速理解和安全修改」。
程式碼品質:給六個月後的自己一條活路
程式碼品質不是美學問題,是生存問題。你現在為了趕 deadline 寫的「暫時性」程式碼,六個月後就是另一個工程師(很可能是你自己)的噩夢。
// 六個月後你看到這段程式碼,你會想什麼?
function proc(d) {
const r = [];
for (let i = 0; i < d.length; i++) {
if (d[i].s === 1 && d[i].a > 18) {
r.push(d[i]);
}
}
return r;
}
// 這段呢?
function getActiveAdultUsers(users) {
return users.filter(user =>
user.status === UserStatus.ACTIVE && user.age > MINIMUM_ADULT_AGE
);
}第一段你需要花五分鐘去猜 d 是什麼、s 是什麼、a 是什麼、為什麼是 1、為什麼是 18。第二段一秒就懂。差距不是一倍兩倍的開發速度,而是在你需要緊急修 bug 的凌晨三點,你是能快速定位問題還是在那邊猜變數名的意思。
測試覆蓋率——最容易被誤解的指標。80% 覆蓋所有核心業務邏輯和關鍵路徑,遠比 95% 覆蓋一堆 getter/setter 有價值。重點不是數字,是你覆蓋了什麼。
問自己:核心業務邏輯有測試嗎?錯誤處理路徑有測試嗎?邊界條件有測試嗎?如果改了行為但測試不會失敗,那測試沒有在保護你——它只是讓你的覆蓋率數字好看而已。
文件:不寫就等著被問到煩
文件這東西,寫的時候覺得浪費時間,不寫的時候被同事問到想離職。
| 文件類型 | 什麼時候最需要它 | 更新頻率 |
|---|---|---|
| README | 新人第一天 | 重大架構變更 |
| API Docs | 前後端吵架的時候 | 每次 API 變更 |
| ADR | 有人問「當初為什麼選這個」的時候 | 每次重大技術決策 |
| Runbook | 系統掛掉的時候 | 每次 incident 後 |
| Onboarding Guide | 新人入職的時候 | 每季 |
ADR(Architecture Decision Records) 是我認為最被低估的文件類型。三個月後沒人記得為什麼選了 PostgreSQL 而不是 MongoDB,為什麼用了 Redis 而不是 Memcached。ADR 記錄的是「為什麼」,不只是「是什麼」。
# ADR-007: 選擇 PostgreSQL 作為主要資料庫
## 背景
需要支援 ACID、JSON 查詢、全文搜尋。候選:PostgreSQL、MySQL、MongoDB。
## 決策
選擇 PostgreSQL。
## 理由
- 原生 JSONB,同時滿足結構化和半結構化需求
- 內建全文搜尋,中等規模不需要 Elasticsearch
- 團隊 3 人有 PG 經驗,1 人有 MongoDB 經驗
- 開源授權,沒有商業授權風險
## 後果
- 需要學 JSONB 查詢語法
- 全文搜尋超過 PG 能力時需要引入 ES沒有 ADR 的團隊,同樣的議題會反覆被討論。「我們為什麼不用 MongoDB?」每三個月問一次。有 ADR 的團隊?「去看 ADR-007。」完。
技術債:借錢不是罪,不還才是
技術債不是壞事。它就像財務上的借貸——有時候借錢是合理的決策。問題在於你是否有意識地承擔,以及是否有計畫地償還。
技術債有四種:
| 有意識的 | 無意識的 | |
|---|---|---|
| 謹慎的 | 「為了趕上市場窗口,先這樣做,之後重構」 | 「做完才發現有更好的方式」 |
| 魯莽的 | 「沒時間寫測試,先上線」 | 「什麼是 design pattern?」 |
謹慎且有意識的技術債完全沒問題——你知道自己在做什麼,也知道代價。魯莽且無意識的技術債是最危險的——你甚至不知道自己欠了債。
// 沒有追蹤的 TODO——三個月後沒人記得這件事
// TODO: fix this later
// 有追蹤的技術債——有編號、有影響評估、有預計償還時間
// TECH-DEBT(JIRA-1234): 目前用 polling 檢查任務狀態,
// 應改為 WebSocket 降低 API 負載。
// 預估影響:每日約 10,000 次不必要的 API 呼叫
// 預計償還:2024-Q4技術債管理的實務:每筆都記錄、定期評估「利息」(它每週造成多少額外成本)、排進 sprint(每個 sprint 保留 15-20% 容量)。「等有空再做」永遠不會有空。
| 利息等級 | 處理策略 |
|---|---|
| 高利息(每天影響效率) | 立即安排償還 |
| 中利息(偶爾碰到) | 排入下 1-2 個 sprint |
| 低利息(不影響日常) | 有空再還 |
| 零利息(只是不夠優雅) | 不處理。這不是債,是你的潔癖 |
最後一項很重要。不是所有「不夠完美」的程式碼都是技術債。如果它沒有造成任何額外成本,那就別碰它。
部署能力:你部署時會緊張嗎?
| 指標 | 「能部署」 | 「好的部署」 |
|---|---|---|
| 頻率 | 每月一次 | 每天多次 |
| 方式 | 手動 SSH 到伺服器跑腳本 | CI/CD 自動化 |
| 風險 | 每次提心吊膽 | 日常事務 |
| 回滾 | 出問題再看辦法 | 一鍵回滾 |
| 時間 | 2 小時 | 15 分鐘 |
| 影響 | 停機維護 | 零停機(Blue-Green / Canary) |
如果每次部署你都要特別「準備」、特別「通知」、特別「緊張」,你的部署能力有問題。好的部署應該像呼吸一樣自然——你不會特別注意到它在發生。
Bus Factor:誰離職你們會完蛋?
Bus Factor 是衡量知識集中程度的指標——如果團隊中幾個人同時被巴士撞到(或離職),專案就掛了?
| Bus Factor | 風險 |
|---|---|
| 1 | 極高。那個人離職,系統就完了 |
| 2-3 | 高。核心知識集中在少數人 |
| 4+ | 可接受 |
| 全團隊 | 理想 |
提高 Bus Factor 的方法:每個 PR 至少被兩個人 review、定期 pair programming、每週技術分享、把腦中知識寫成文件、輪換 on-call。
如果你的團隊中某個人休假時大家會特別緊張,你的 Bus Factor 有問題。
這篇的重點回顧
可維護性包含程式碼品質、文件完整度、技術債管理、部署能力、Bus Factor。這些東西使用者看不到,但它們決定了你的產品能不能持續進化。一個無法維護的產品,就算現在再好用,也撐不過兩年。
系列文章:
- 好產品的定義(一):好專案 vs 好產品
- 好產品的定義(二):技術品質
- 你在這裡 → 好產品的定義(三):可維護性
- 好產品的定義(四):商業價值與團隊
- 好產品的定義(五):生命週期、指標與反模式
「最好的程式碼不是你寫得最漂亮的那段,是半夜三點你還看得懂的那段。」