昨天曾提到,難改的程式通常設計不會太好;程式會設計不好有很多因素,比方說,筆者最常看到的是:因趕上線而寫出設計不好的程式碼。
我們會因為某些因素,而寫出未來難以修改的程式,這個狀況稱之為「技術債」。技術債是一種隱喻,以債務來形容這個狀況,非常地貼切!同時也能讓非技術人員理解技術人員的處境。
在現實生活裡,通常我們會為了某個目的去跟銀行貸款,比方說買房、投資或是週轉等等。當然銀行沒那麼好心,肯定會要你付出一點代價--利息。只是因為達成目的所帶來的短期利益大過於利息,因此我們仍然會選擇借貸。
技術債也是一樣,一個快速開發且設計不良但能動的程式,如果提早推出市場搶得先機,勢必能收到短期成效。但在開始維護的時候,就得付出程式難改的代價--時間。有道是時間就是金錢,技術債所付出的代價一樣也是金錢。
一般貸款都會經過各種評估,銀行才肯放款。技術債則不大一樣,開發人員有可能會不知不覺中欠下技術債。
Martin Fowler 把技術債產生原因分成了兩種維度,分別是魯莽(reckless)、謹慎(prudent)與有意(deliberate)、無意(inadvertent)。
這兩種維度剛好切出四個象限,如下圖:
圖片來源: TechnicalDebtQuadrant | Martin Fowler
有時我們很清楚現在這麼做會有什麼缺點,以及未來該如何移除這個問題,只是礙於現況不得不這麼做,這正是屬於右上角謹慎與有意的技術債。
但相反地,我們也很有可能無意中欠債,比方說,初學程式的新手,通常都會使用萬解複製貼上來解決類似的需求,但有設計經驗的老手都知道,這通常不是個好的實作方法,而這正好屬於左下角魯莽與無意的技術債。
依欠債方法來看,技術債有以下種類:
廣義來說,只要技術上會難以修改的理由,都可以算是技術債。
知道技術債的產生原因後,我們可以了解該如何避免產生技術債。
最簡單的方法是幫自己的程式寫註解、把開發過程遇到的問題和解法寫筆記、建置專案過程寫文件等等。撰寫文件是馬上能做的事,新專案可以避免欠債,舊專案也能馬上還債。
但通常遇到趕上線,而文件也不是必要的交付物,接著就會被忽略。而這問題會到越來越多不了解程式架構的協作者(甚至是三個月後的自己)加入專案時,才會漸漸被發現。最明顯的警訊就是:協作者開始要求寫文件,因為程式已經難以理解到需要文件支援了。
趕上線而沒寫文件,是屬於有意與魯莽的技術債。
在提測試前,先來聊聊人見人怕的 bug 。
大家都很怕系統有 bug 。換個角度想,事後被別人(或是未來的自己)發現的才叫 bug ,自己開發當下發現的不算(因為當下就會修好)。也因此 bug 最可怕的地方在於,它通常都是不知不覺寫下來的,而開發人員卻能一直與 bug 共存,甚至是使用它。直到 bug 被發現後,才注意到整個系統都依賴 bug ,這時要修 bug 已經是天方夜譚了。
這個 bug 也可以稱之為技術債--因為修改程式都可能要考慮如何跟 bug 共存。
寫測試,是開發者的職責一部分。測試除了驗證功能外,還能保護原始碼,讓開發人員在修改有測試保護的程式時,比較不會因為粗心而寫下 bug 。這就像是為程式買保險一樣。
可想而知,有保險的程式, bug 不容易出現,技術債當然會大幅減少。
粗心寫出 bug ,是屬於無意的技術債。
除非是天才開發者,不然缺少設計的程式,通常會因為擴展性差、難以修改而成為未來的技術債。開發階段做的設計,如 DB schema 設計、程式物件導向設計、系統架構規劃等等,都能有效減少技術債產生。
雖然設計能減少技術債,但「天下武功,唯快不破」。產品只要能用,越早上線越好,不管是 POC 為了驗證產品可行性,或是提早進入市場獲得使用者回饋都是非常有價值的。而且「設計再怎麼好,都無法應付需求變化」,不管再怎麼謹慎,今天寫的每一行程式碼,明天還是有可能成為技術債,非常難以避免。
如何在「產生技術債與價值」和「高品質程式」之間取得平衡,這是企業所應該要面對的課題。
無意的技術債難以避免,但在可以選擇的時候,就跟銀行借錢一樣,當然還是謹慎地欠債好,而不是魯莽地欠債。
技術債會讓程式碼難以修改,而重構的目的之一,正是改善設計並讓其他人更容易地理解程式碼,換言之,重構正是在還債。
技術債跟信用卡的循環利息一樣,如果置之不理,會像滾雪球越滾越大。而技術債要付的利息是時間,團隊會發現估算開發時間越來越長,因為程式碼越來越難修改,直到團隊無法掌控專案被解散為止。
當團隊意識到產品有技術債時,應當把當它卡債一樣,訂定償還計畫--也就是定時重構,減少技術債,同時程式碼也能持續運作,為企業產生應有的價值。