How to rewrite a legacy system

如何重寫陳舊系統:就像忒修斯之船

前言

近期面對許多陳舊代碼維護的問題,碰巧我翻找到這個部落格:Understand Legacy Code🔗,紀錄一下「重寫」陳舊系統可能會遇到的困境與解套方案。以我自己手上的專案為例,在進行專案升級評估時發現事情比原先想像還要複雜!

  • 框架版本升級:說好的向後兼容呢?版本一換代碼風格與架構全都要改,舊版本的套件框架將被永遠拋棄。
  • 不存在的文件:因為太匆忙了沒有時間紀錄文件,「學習」累積成為每個團隊成員必經的痛,上手專案之前都要消化大量的學習債務。
  • 根深蒂固的反模式:錯誤的開發模式已經深植到成為整個專案的文化,現有的反模式也容易造成破窗效應🔗的發生。

總之到達一個無法回頭的地步,或許只剩下重寫這個高風險的選項,因為……

  • 程式間耦合性過高牽一髮動全身…
  • 無測試或難以被測試…
  • 重寫都比重構還要低成本…
  • 專案再也承受不起變化…

於是你踏上了專案重寫的道路。

重寫專案的陷阱

  1. 你跟管理層討論了停止推出新功能的策略,用這段時間重寫現有的應用程式。
  2. 你估計重寫將需要 6 個月的時間來覆蓋現有應用程式的功能。
  3. 幾個月後,發現了嚴重的錯誤,絕對需要在舊代碼中進行修復。因此,你修補了舊代碼和新代碼。
  4. 幾個月後,一個全新的功能被銷售給客戶,但由於新版尚未準備好,只能先在舊代碼中實現,並且也要在新版本中添加此功能。
  5. 經過 5 個月,你意識到項目將延遲。舊應用程式做了比預期多得多的事情。你開始更加努力。
  6. 經過 7 個月,你開始測試新版本。QA 提出了很多應該修復的問題。
  7. 經過 9 個月,業務無法忍受「不開發功能」的狀況了。領導層對這種情況感到不滿,你感到很疲憊。你開始對令人痛苦的舊代碼進行更改,同時努力跟上重寫進度。
  8. 最終,你們在生產環境中擁有了兩個系統。長期目標是淘汰舊系統,但新系統還沒準備好。每個功能都需要實現兩次。

故事中重寫造成兩套完全獨立的系統,可以想像重寫在這個例子當中帶來多大多沉重與負擔。

忒修斯之船模式

如果船舶部件隨著腐爛而逐步更換,那麼船舶完全更換後還是原來的船嗎?先不討論哲學,這種做法的觀點在於漸進的替換重寫片段,代表更快的反饋、更少的工作(至少更少的心理負擔)以及避免失去功能!

  • 讓新代碼作為舊代碼的代理,用戶使用新系統只是導向到舊系統中。
  • 逐步實踐舊代碼的行為到新代碼當中
  • 漸進的淡出舊代碼,取代上新代碼

使用忒修斯之船模式前後比較流程圖
使用忒修斯之船模式前後比較流程圖

總結

我們對重寫的概念通常是「打掉全部重做」,但這麼做過於理想化,並且在有些規模需要重寫的專案上,這樣的做法可能會讓專案陷入無底深淵。退一步思考,並重新審視重寫的最終目的,忒修斯之船模式是簡單但極容易被忽略的選項。

延伸閱讀