1. 軟體公司能犯的最嚴重策略錯誤#
Joel 開門見山地指出:從頭重寫程式碼(Rewrite from Scratch)是一家軟體公司能犯的最嚴重的策略錯誤。沒有之一。
這個論點在軟體業界引起了廣泛討論,因為幾乎每位工程師都曾經看著一坨老舊的程式碼,心想:「這東西太爛了,不如全部打掉重來。」Joel 認為,這個衝動雖然可以理解,但幾乎每次付諸行動都會帶來災難性的後果。
「打掉重來」的衝動是一種認知陷阱。你看到的是老程式碼的醜陋,看不到的是它花了多少年累積的 bug 修復與實務經驗。
2. Netscape 的慘痛教訓#
Joel 以 Netscape 瀏覽器作為最經典的反面教材:
- Netscape Navigator 曾是瀏覽器市場的絕對霸主
- 公司決定從頭重寫瀏覽器引擎,開發 Netscape 6
- 重寫過程耗時約 三年
- 在這三年間,Netscape 沒有發布任何有意義的更新
- Internet Explorer 趁虛而入,蠶食了 Netscape 的市場份額
- 最終 Netscape 從市場領導者變成了無關緊要的角色
這三年的空白期是致命的。軟體市場不會等你。當你把所有工程資源投入重寫時,你的競爭對手在持續改進他們的產品、搶占你的市場。
Netscape 重寫時間線的啟示
- 重寫前:Netscape 擁有超過 80% 的瀏覽器市場份額
- 重寫開始:工程團隊全力投入新版本,舊版本進入維護模式
- 重寫第一年:IE 持續推出新功能,使用者開始遷移
- 重寫第二年:IE 市場份額快速增長,Netscape 的品牌認知開始褪色
- 重寫第三年:新版本終於發布,但已經太晚——市場已經不再等待 Netscape
教訓:市場領先地位是時間的函數,而重寫是時間的最大殺手。
3. 為什麼老程式碼看起來很醜#
每位工程師都有過這樣的經歷:打開一個陳年的程式檔案,看到混亂的結構、不一致的命名、莫名其妙的條件判斷,然後得出結論:「這段程式碼寫得太差了。」
但 Joel 指出一個關鍵的認知偏誤:程式碼看起來醜,不代表它寫得差。實際上,那些看似多餘或莫名其妙的程式碼,往往是:
- 修復了一個真實的 bug:某個奇怪的
if判斷,可能是因為某個特定客戶在某個特定情境下遇到了問題,工程師花了三天除錯後加上去的修復 - 處理了一個邊界條件:看似不必要的 null check,可能是因為某個第三方 API 在特殊情況下會回傳意外的值
- 繞過了一個平台限制:那段看起來很蠢的 workaround,可能是因為某個作業系統版本有一個已知的 bug
這些修復和 workaround 不會自動出現在新寫的程式碼中。重寫意味著你必須重新發現所有這些問題——而且很可能你會漏掉其中一些,直到使用者再次遇到同樣的 bug。
4. 重寫的隱藏成本#
決定重寫時,人們通常只看到好處(乾淨的架構、新的技術棧、更好的可維護性),卻嚴重低估了成本:
4.1 你會失去多年的 bug 修復#
老程式碼中包含了數百甚至數千個 bug 修復。每一個修復都代表:
- 一個被發現的問題
- 開發人員花時間理解並解決的努力
- 經過使用者驗證的解決方案
重寫等於把這些全部丟掉,然後祈禱你不會再遇到同樣的問題。
4.2 重寫永遠比預期久#
程式設計師(包括 Joel 自己)天生對開發時間過度樂觀。重寫的預估時間幾乎總是嚴重不足,原因包括:
- 低估了原有系統的複雜度(「不就那麼點功能嗎?」其實有十倍之多)
- 忘記了各種邊界條件與特殊情況的處理
- 沒有考慮到測試、部署、資料遷移的時間
- 新技術帶來的學習曲線與未知問題
4.3 機會成本#
重寫期間,你的團隊無法開發新功能、無法回應市場變化、無法改善現有使用者體驗。在快速變化的軟體市場中,這段空白期可能是致命的。
5. 更好的替代方案:漸進式重構#
Joel 不是說老程式碼永遠不該改善。他的主張是:改善應該是漸進式的重構(Incremental Refactoring),而非推倒重來的重寫(Rewrite)。
漸進式重構的做法:
- 逐步改善:每次修改程式碼時,順便改善周圍的程式碼品質——改善命名、抽取函式、增加測試
- 模組化替換:將系統拆分為獨立模組,然後逐一替換最差的部分,而非一次全部重寫
- 持續交付:在整個改善過程中,系統始終是可運作的,使用者不會感受到中斷
- 風險分散:每次只改動一小部分,如果出問題,影響範圍有限且容易回滾
下次當你想「打掉重來」時,先問自己:「我能不能只重寫其中最差的 10%?」幾乎總是可以的。而且你會發現,修完那 10% 之後,剩下的 90% 看起來也沒那麼糟了。
6. 什麼時候「似乎」可以重寫#
Joel 承認有極少數情況下重寫可能是合理的,但他提醒要非常謹慎:
- 原有程式碼所依賴的平台已經完全消亡(例如從 DOS 遷移到 Windows)
- 原有程式碼規模非常小,重寫成本確實可控
- 你有充足的資源同時維護舊版本並開發新版本
但即使在這些情況下,他的建議仍然是:盡可能多地保留和複用舊程式碼中的邏輯,而非從白紙開始。
老程式碼不是「技術債」——它是「技術資產」。那些看起來醜陋的程式碼,每一行都可能代表一個你還不知道的教訓。在你丟掉它們之前,請確保你真的理解它們為什麼存在。