Working with Legacy Code - 修改軟體
May 15, 2022
修改軟體
軟體工程師開發時實際上只會針對軟體進行四種改動:
- 增加功能:增加軟體過去沒有的行為,通常在程式上增加全新的區塊。
- 修改 bug: 對於修改功能依照主觀認定可能視為新增功能或是修改 bug,然而不論是新增功能或是修改 bug 對於軟體來說最大的差異是增加了軟體的行為或變動了軟體的行為。使用者依賴於這些行為,希望我們新增其期許的功能但並不期待既有的行為被移除或改變而需要做出下游變動。
- 改善程式架構、改善設計:改變既有的軟體架構使其更好維護,並且不希望更動軟體的行為。在不改變行為前提下進行架構的整理稱為[[重構]],每一個微小的變動都會在測試下驗證其行為不變性,便可以在不改變軟體的行為為前提下使軟體更去可維護性。
- 改善程式效能、資源使用(最佳化):類似於重構,不過著重於讓軟體更加有效率的使用硬體資源如時間和記憶體,並且同樣是以不更動軟體的行為為前提。
小結
對一個系統進行修改時,會有三個方面產生改變:結構、行為(功能)、效能。上述四種改動分別主要改變哪些層面:
增加功能 | 修改 bug | 重構 | 最佳化 | |
---|---|---|---|---|
結構 | 改變 | 改變 | 改變 | - |
行為(功能) | 改變 | 改變 | - | - |
效能 | - | - | - | 改變 |
通常增加功能、重構、最佳化理想下會維持既有功能不變,而修改 bug 則會變動既有功能,但這比例佔不變動的功能的比例是很小的。而進行軟體修改,我們九成的精力會花在保持不變的行為而一成的精力會花在修改軟體的行為、效能、結構是正確的。而保持不變的行為並不意味著只要不觸碰那些程式碼即可,而在於在我們希望安全修改軟體需要理解軟體的行為,及這些修改會有哪些連帶的影響。
危險的修改
為了減少修改軟體所伴隨的風險,我們需要考慮三個問題:
- 要進行哪些修改?
- 如何確認修改是否有效?
- 如何確認沒有破壞任何既有的功能?
以盡量避免改動 code base 的心態進行修改,長久下來是危險的,避免建立新的方法和類別會讓既有的方法和類別變得越來越龐大並難以理解,反之若對需要修改的區塊花一些時間來熟悉,則在後續修改中會更加了解整體的架構,下一次的修改也會更有信心。另一方面,長久不願意接觸既有的程式,久而久之則會對其感到生疏並產生恐懼心理而不易再做改動,隨著時間越久越懼怕修改既有程式,反之應該經常練習接觸則會越來越熟練修改的技巧,並可以輕易辨識哪些程式可以分解哪些不可以。
如何進行修改
對軟體進行變動有兩種主要的方式:
- 編輯並祈禱(edit and pray):仔細計畫所要進行的變動,確保自己理解要修改的部分及可能相依的區塊,接著開始進行修改。修改結束後執行修改後的系統,確認所做的變動是否生效,最後再針對系統整體確認並修復,確保變動沒有損壞其他程式執行,而此刻在沒有測試覆蓋的前提下,只能夠仰賴祈禱自己沒有破壞系統,並且這個階段需要花大量時間進行驗證。即便整個過程都秉持著小心翼翼的心態,卻並不代表不會出意外,因為程式的安全性是主要取決於程式的複雜度及維護性而非一個人的細心度。
- 覆蓋並修改(cover and modify):透過測試軟體覆蓋準備要修改的軟體,蓋住我們要修改的程式碼,確保糟糕的更動不會污染其他程式碼,當有程式碼一組良好的測試時,我們就可以安心的對他進行修改,並快速發現修改的結果好壞。測試除了檢測執行結果以外,也可以用來檢測程式碼的變化。相當於[[回歸測試]](Regression Test),週期的檢驗程式碼的已知行為是否如同過去那般工作,然而差異在於迴歸測試其測試的層級較高,包含了環境設置、情境等其應用程式介面層級,測試所花的時間相較單元測試並不能夠立即給予修改程式的開發者快速的回饋。
修改遺留程式(Legacy Code)的演算法
- 確定程式修改的變動點
要修改的地點和程式碼的架構會有緊密連繫可能有著高相依性。若對程式的理解程度不足,導致難以判斷正確的修改點的話可以參考[[對程式碼理解不足的解方]]及[[程式碼毫無邏輯可言的解方]]
- 找出測試點
對於遺留的程式碼難以找到測試點的話可以參考[[修改時應測試哪些方法]]和[[在同一地方進行多處修改,是否應對所有相關的類別都解依賴]]
- 解依賴
依賴性是測試的最大阻礙,包含難以在測試控制工具中實例化的目標物件,也難以在測試控制工具中執行的方法。通常在遺留程式中需要先解依賴後才能夠將測試放置其中。在[[無法將類別放置測試控制工具中]]和[[無法在測試控制工具中執行方法]]提供了一般解依賴問題的方法,進一步可以參考[[解依賴技術]]。通常解依賴本身的程式碼也需要受到測試的保護,在[[降低修改的風險]]提供了實作的方法讓所做的第一個變動可以收到測試保護而變得更安全。
- 編寫測試
測試新編寫的程式碼和測試遺留程式碼有些許不同,可以參考[[修改時應該怎麼寫測試]]。
- 修改及重構
在測試前提下進行改動理想上可以透過[[測試驅動開發]]來為遺留程式碼增加新功能,[[在遺留程式碼添加功能]]中提及了 TDD和其他添加功能的技術。而隨著對程式碼的了解越深後往往可以多做一些重構,在[[處理大類別]]、[[需要修改大量相同的程式碼]]、[[要修改一個巨型方法但無法為其編寫測試]]中有提及可以讓遺留程式碼程式結構更好的技術