02.19 換血!重寫舊系統的一場噩夢被我親手終結

不少工程師對舊項目和代碼庫談之色變。但如果舊代碼反覆遭到黑客入侵,那就躲無可躲,必須提出有效的方案解決這個“不定時炸彈”。

選擇重寫:噩夢的開始

複雜繁多的應用程序往往牽一髮動全身,當你想重做部分應用時,發現其他的應用程序也會受到影響。

更糟糕的是,當你更改代碼前試圖編寫單元測試時,發現該代碼最初並沒有設計成可測試的代碼。所以,在進行了種種掙扎和嘗試後,你可能就會把這個應用程序凍結起來,再也不想碰它了…

那麼,有沒有一種辦法既能更改無法維護的代碼,又能使局面不那麼糟糕呢?

我們知道,更改代碼存在一定風險,而重構成本又太高。在這種情況下,從頭開始重新編寫代碼看起來是個不錯的主意。

按照這個思路,接下來會發生什麼?

  1. 你在重寫現有應用程序的同時,與管理層討論一段時間內停用新功能。
  2. 重寫一個包含現在應用程序功能的新程序大約耗時 6 個月。
  3. 幾個月後,出現了一個令人討厭的 bug,並且這個 bug 必須在舊代碼中修復。因此,你又修補了舊代碼和新代碼。
  4. 再過幾個月,公司將一些新功能交付給了客戶。但新功能必須用舊代碼才能實現,因為新版本尚未準備好。你不僅要再次返回到舊代碼中,還要添加一個 TODO 以便這些新功能在新版本中實現。
  5. 轉眼 5 個月過去了,你意識到項目可能要延遲,舊應用程序遠比想象得要棘手。
  6. 7 個月過去了,你開始測試新版本,QA 質量檢查發現了很多需要修復的問題。
  7. 9 個月後,公司再也受不了“不開發功能”。領導開始不滿,你為此身心俱疲。你一邊掙扎著更改舊代碼,一邊加快速度重寫代碼。
  8. 最終結果是,你做出了兩個系統。擺脫舊代碼還需要一段時間,因為新代碼還沒準備好。每個功能都需要在新系統和舊系統中實現兩次。

最終,我選擇扼殺

我現在的項目,就是在處理這個問題。我們內部有兩個並行工作的系統:cart(舊系統)和 booking(新系統)。實際上,booking 應該替換掉 cart。

該項目始於三年前,但三年過去了,項目仍然未完成。

booking 總體上講要優於 cart,但並不是說所有方面都比 cart 出色,一些購買流程會使用 booking,但仍有很多流程沿用 cart。

現在,由於新舊系統並行工作,所以新功能的實現時間是原來的兩倍。有趣的是,由於最初的設計目的並不是為了支持我們想要的新功能,而是因為 booking 已經過時了,所以才會建議“適當重寫 cart 系統。”

如果按照這個思路,接下來幾個月,我們可能要讓兩個系統並行運行。顯然,這不是個好辦法,我還知道另外一種能有效解決系統問題的辦法,就是“扼殺”。

如何“扼殺”舊代碼庫

方法很簡單:逐步刪除舊的代碼庫,使用新的代碼庫。

具體操作如下:

  • 讓新代碼充當舊代碼的代理服務器(proxy)。用戶使用新系統,新系統重定向到舊系統。
  • 在新代碼庫重新實現每步操作,這種操作在終端用戶看來沒有任何變化。
  • 通過讓用戶使用新功能來逐漸淡化舊代碼。刪除舊的、未使用的代碼。

實際操作

來說說我們的系統,我們有一個用於處理付款的 cart模塊。

換血!重寫舊系統的一場噩夢被我親手終結

我們嘗試重寫,想法是創建一個新的、比 cart 更好的 booking 支付方式。但是這個項目沒有 100%交付,因為重寫工作耗費了太多時間,我們不得不在舊 cart 系統上開發新功能。

最終,我們還是創建了兩個模塊。

換血!重寫舊系統的一場噩夢被我親手終結

讓我們再試一次,這次我們來“扼殺”cart 模塊。與上一種方式不同的是,這次我們引入新 booking 模塊作為代理服務器。

換血!重寫舊系統的一場噩夢被我親手終結

設置起來很容易。很快,我們可以在不重複付款處理邏輯的情況下將其交付生產。然後,逐步地,我們可以開始將付款邏輯遷移到新的 booking 模塊。

換血!重寫舊系統的一場噩夢被我親手終結

在遷移邏輯時,我們擺脫了 cart 模塊上未使用的代碼。這個過程可能會需要一段時間,但我們正逐漸摒棄掉舊的、難以維護的 cart,開始應用新的、更好的 booking。

換血!重寫舊系統的一場噩夢被我親手終結

結束語

這樣做最好的地方是可以解決重寫期間無法交付新功能的問題。使用這種技術,無需複製代碼,更無需實現兩次新功能。另外,你需要儘快將新系統投入使用,更快地獲得反饋,最大程度減少工作量並且降低把事情搞砸的風險。

最後,你可以有條不紊地進行重寫,而無需將代碼凍結 6 個月。儘管“扼殺”可能帶有暴力色彩,但這種隱喻恰恰描述了慢慢擺脫舊系統的方法,與完全轉換相比,這樣做的風險較小。當舊代碼實在難以使用時,這可能是邁向更好設計的第一步。

如果你在從事領域驅動設計(DDD),我建議你採用這種方法逐步淘汰舊系統。

你可以將舊系統視為黑匣子,創建一個 Bubble Context ,在其中應用部署 DDD 原理。Bubble Context 與舊系統進行交互。逐漸地,新功能將在不斷增長的 Bubble context 中實現。同時,業務中用到舊系統的機會也越來越少,直到有一天,你可以徹底關閉舊系統。

原文鏈接:https://understandlegacycode.com/blog/avoid-rewriting-a-legacy-system-from-scratch-by-strangling-it/


分享到:


相關文章: