2008年3月17日 星期一

活在現實,擁抱舊有程式碼

軟體開發人員都希望在專案中應用更先進的技術與觀念,使用新的方法、流程與工具。想要開始引進CVS管控原始碼,想用OOAD方法、用jUnit做Test Driven Development等等,想嘗試導入的不勝枚舉。但手上的專案往往不容許我們導入這些︰舊包袱太多,程式架構很亂。既有的程式碼用的是舊技術,新技術放不進去。期待公司下一個專案能夠讓我們重頭好好做。到時候能做個乾淨的架構,不用背舊包袱,而使用新的流程或技術,讓我們真正發揮工作效率。

這個等待其實是不切實際的。一個進步的技術,要試著將它實現在背著舊包袱的現有專案中,並讓它的在這個專案中展現出價值(當然,前提是專案主管允許你這麼做)。為甚麼呢?

  1. 全新的專案是很罕有的。舊系統的延伸與維護佔軟體開發人員95%以上的工作比例。雖然在學校時,每一份作業,每一項實驗,牽涉到的程式碼都是重新寫的。但是就業以後,卻幾乎都在舊系統的基礎上開發。
  2. 即使我們的下一個專案是全新的專案,它也很快會發展出包袱。時程、人力、需求變更等等傳統專案的問題,也會發生在新專案中,難免在壓力下部份程式碼不會照理想的標準開發。
  3. 更重要的是,禁不起現實專案考驗的技術,就不是適合我們的技術。我們技術人員遇到新技術不能用,往往認為是舊系統程式碼的錯,而不是這個新技術的錯。實際上這個判決不盡公平︰技術的典範轉換進行得很快,有的兩三年就被淘汰了,但一個好的軟體系統卻可能運作十年以上。
另一方面,舊程式碼不見得像你想的那麼糟。著名的[約爾談軟體]發表過一篇好文章討論為甚麼舊程式永遠一團亂,這篇文章叫做你絕對不應該做的事。作者認為,"舊程式永遠一團亂"背後的真相是一個基本的程式設計原理︰讀程式比寫程式困難。而我們設計者偏偏沒有耐心,喜歡剷平一切再創造偉大的事物。作者舉例說明為甚麼一個簡單的視窗函數會寫兩頁長,因為它包含了許多經驗的累積︰
其中一個是南西在沒有Internet Explorer的電腦上安裝時寫的。另一個是對付記憶體不足時的問題。還有一個是要應付檔案放在軟磁上,使用者卻中途抽掉磁片的狀況。LoadLibrary呼叫很難看,不過卻能讓程式在舊版Windows 95上也可以執行。
所以全新的系統不是天堂,沒有過去累積的資產,一切重新來過,壓力可能更大。所以我們每學一個新技術,要先想如何在舊系統裡實現它。

例如我們剛學GoF Design Patterns其實用處有限,透過Refactoring技術才打開更多應用Design Patterns的機會。要引進jUnit要先考慮有沒有什麼方法把舊功能納入Test的保護傘下。在乾淨的環境用新技術做個小範例很簡單,要把它放進各種問題糾結的舊系統才是挑戰。但和後者奮鬥才是我們這一行每天工作的常態。

有一本很好的書叫做Working Effectively with Legacy Code,作者是Michael Feathers。這本書教我們怎麼對老舊的程式碼(Legacy Code)延伸與翻新。他對legacy code有個很棒的定義︰沒有被test程式覆蓋的code就是lagacy code。所以我們不管自認程式碼寫多好,技術多先進,沒有被測試覆蓋的話它就是legacy code。真希望這類的好書多出一些。

2 則留言:

Unknown 提到...

要擁抱舊有程式碼得要先有很高的修行,起碼得先忍住性子壓抑自己去修改的衝動,一步步的把 Testcase 補齊,還能要乞求 User 先給你這些時間。

EC 提到...

Michael Feathers書中說的方法,就如同Sbos大大所言。一步步把test cases補齊。

處理舊程式碼要重構。但是沒測試保護就不敢放手去重構。沒有先重構的話程式黏成一團,測試又加不上去。這個雞生蛋蛋聲雞的死結該怎麼解呢?

書中的說法是,第一次需要把要處理的程式碼先做有把握、很安全的修改,為了將這些程式碼放進test保護傘下。這個第一次修改很可能把已經很醜的程式改得更醜,用很髒的方法做decoupling,這是正常的。因為這次修改沒有測試保護,所以以保守、安全為主,而不用在這個時候改善架構。等到有測試保護了,再用重構逐步美化它。