我曾經答應自己,從這週開始要改寫一些輕鬆點的主題。結果到了週一,我的 JSNation 之旅正式落幕,而我突然意識到,我竟然還沒寫一篇關於自己演講的文章。既然一切都還歷歷在目,那就開始吧!

至於 JSNation 本身,我的感受有點複雜。我被選進線上場次,雖然主辦方專業得令人驚嘆,但那種感覺終究還是跟親臨現場不同。你知道的,我本來可以在阿姆斯特丹跟 Wes Bos 一起喝啤酒,結果卻是在廚房裡一邊看自己的演講,一邊回 Teams 訊息。😄 不過,這仍然是很棒的體驗。誰知道呢?也許有一天,我的專業能力,或者說我的名氣等級 😅,會成長到讓我有機會親自到阿姆斯特丹演講。

我在研討會上的 20 分鐘成名時刻

話雖如此,我今年晚些時候或許還是會去那座美麗的城市,不過先別說得太早。😉

回到主題。我的演講題目是 「重寫還是重構?如何安全地將舊有應用程式遷移到現代框架」。我相信我是最適合談這個主題的人。為什麼?因為在我的職涯中,我遷移過很多前端應用程式,各種組合都有:舊版 Angular 到現代 Angular、ASP.NET MVC 5 到 Vue、使用 class component 的 React 到現代 React,諸如此類。

原因很簡單。若某家公司急需做遷移,突然收到一份履歷,而那人剛好已經做過一次這種事,通常他們都會非常樂意錄用——即使薪資比市場價高一點。😄 我大致上就是這樣成為前端遷移專家的,所以我對這件事確實有不少話想說。

但為什麼一定要遷移?

在討論策略之前,我們得先回答一個更根本的問題:為什麼一開始就要遷移?

我常常從利害關係人那邊聽到這個問題,特別是非技術背景的產品負責人。既然應用程式能正常運作,為什麼要去動它?開發人員只是追逐潮流、想把新鮮技術寫進履歷,對吧?

我的回答通常很簡單,也很誠實。

你們很了解我。我不是任何特定框架的死忠粉絲。對我來說,它們只是工具,就像槌子一樣。但當我在打造某樣東西時,我寧可用一把結實可靠的槌子,也不想用一把生鏽、零件還缺東缺西的。😉🔨⛏️🪓🔧🪛

不過,遷移不只是開發者體驗的問題。說到底,它關乎你的產品能不能存活。尤其是在今天,技術演進比以往更快,而 AI 又把一切推得更快。

以資安為例。當某個函式庫已經過時,沒人再維護它時,安全性修補就不會再來了。漏洞報告開始一片紅。歷史上沒有任何一位產品負責人會說:「好啊,我們為了多幾個功能,犧牲資安!」至少我希望沒有 😅

也別忘了現代工具鏈。光是 Vite 的建置就能帶來巨大差異。現代應用程式就是比較快,而更快的應用程式就是更好的應用程式。

還有很多其他原因,但最重要的是這一點:

你越晚遷移,遷移就會變得越困難、也越昂貴。

好吧好吧,我想遷移。但要怎麼做?

有趣的是,LLM 的出現並沒有從根本上改變遷移策略。它們跟 AI 還沒普及之前其實差不多。工作有時會變快,有時不會,但底層做法並沒有改變。

遷移策略大概有多少,資深工程師就有多少種說法,但實務上大致可以分成兩類:

重寫(Big Bang 策略)

把整個應用程式全部重寫;或者,在前端領域常見的情況是,直接把一個非常古老的技術堆疊升級到框架的最新版本。

重構(扼殺者模式)

一邊持續交付新功能,一邊逐步把應用程式一塊一塊重寫。

那麼,哪一種比較好?

老實說,沒必要為這件事發動聖戰。大多數情況下,專案的現實條件會替你做決定。

如果你的應用程式相對小、團隊經驗豐富、文件也算完整,而且你能接受暫停幾週或幾個月的新功能開發,那麼重寫可能是完全合理的選擇。

另一方面,如果你面對的是一個龐大專案,團隊裡有很多初階工程師,或是對技術不熟的人,而且文件幾乎不存在——那麼漸進式重構大概就是你唯一實際可行的方案。

當然,還有很多其他因素要考慮。先快速整理如下:

因素 重寫(Big Bang) 重構(扼殺者)
應用程式規模 小型或中型 大型
功能開發 可暫停 必須持續
文件 良好 不佳
團隊經驗 混合
風險承受度 較高 較低
首次看到成果的時間 較短 較長
遷移期間的複雜度 較低 較高
遷移永無止境的風險

Big Bang——沒有聽起來那麼可怕

讓我先簡單介紹這兩種方法,先從 Big Bang 開始。

我得承認——我其實滿喜歡這種策略。沒錯,很多書籍和文章都把它描述成很高風險,有時甚至說它是一種反模式。而且如果我們談的是一個二十年歷史、沒人敢碰的 Java 單體系統,那他們說得沒錯。

但很多前端應用程式其實都還算年輕、規模也不大。在這種情況下,Big Bang 反而可能更快也更便宜。

完整重寫在現在其實已經比較少見。不過,大型升級仍然很常見,例如從 Angular 7 升級到具備 Signals 的現代 Angular,或是從舊版、使用 class component 的 React 升級到使用 hooks 的現代 React。

這些專案仍然需要大量工作,也要花很多時間翻找舊程式碼,但你通常會比扼殺者模式更快抵達終點。

我不打算在這裡細講規劃階段、遷移腳本、codemod,或我實際用過哪些工具。這些細節會因專案而異,放在一篇通用文章裡也不太有意義。

不過,有些事情幾乎在每一次遷移中都會以驚人的一致性出現。😉

真實案例:從 Angular 7 一次性升級到最新版 Angular

這裡有個有趣的例子。有時候我們會想,某個團隊到底怎麼能讓應用程式多年不升級。其實這比想像中容易,尤其當沒有人實際在維護它,因為產品處於維運模式,而且好幾年都沒有太大變化時。

這次的情況就是如此。

後來某一天,利害關係人決定大幅擴充這個應用程式,並新增好幾個功能。我說服他們升級到最新版 Angular,主要是基於資安考量——系統裡存放的是重要資料。

簡單來說,四位開發者花了四個月把它升到最新版本。而且結果比我原先預期的還難很多。

首先,很容易低估問題的範圍。從外面看起來很簡單:「就升級 Angular 而已。」

但真正的問題通常不是框架本身,而是它周邊的生態系。

例如,我們主要的元件函式庫在 12 到 13 版之間引入了重大的語法變更。你可以想像有多少地方需要更新!當然,AI 可以幫忙,但如果某個很有企圖心的 UI 工程師決定改 CSS 類別名稱、改元件結構,AI 也救不了你——最後還是得手動修。

有些第三方函式庫早就停止維護了。這裡有件事值得記住:

如果某個函式庫多年沒更新,作者看起來也早就忘了它的存在,而且它在 Node 18 上甚至已經無法編譯,那它不是「可用的程式碼」,而是一顆定時炸彈。

沒錯,我們也有很多端對端測試。結果其中不少都因為元件函式庫的變更而炸掉了。😄 所以就算是自動化測試,也不一定能救你。

另一個重要教訓是:務必要為熱修補建立穩健的分支策略。先假設事情一定會出錯。因為最後,它大概真的會出錯。

遷移像冰山一樣呈現

幸好,這次遷移成功了。

建置時間大幅改善。打包體積明顯縮小。新版版面也比舊版好看得多,所以連最抗拒技術變動的利害關係人都印象深刻。

而且也許最重要的是,這個應用程式已經為未來升級做好準備。甚至連 QA 團隊——一開始很討厭我們——最後也承認,定期升級遠比每七年才升級一次要好得多 🤣

漸進式遷移——魚與熊掌兼得

不過,有時你的應用程式實在太大,無法採用 Big Bang,或者你根本不能停止交付新功能。在這種情況下,漸進式遷移就成了唯一實際可行的選項。

這種一步一步的重構通常會透過 扼殺者模式 來實作。當然還有其他方法,但我個人很喜歡這個模式,因為它優雅、相對簡單,而且經過世界上幾家最大科技公司的實戰驗證。

那麼,扼殺者模式到底是什麼?

看看下面這張漂亮的圖吧,這是我親自在 draw.io 裡手工做出來的。😄

漂亮的扼殺者模式圖解

如你所見,你一開始先有一個舊有應用程式。然後在旁邊建立一個新應用程式。接著在前面放一個反向代理,由它決定哪些路由要導向哪一個應用程式。

從那裡開始,你就一個畫面一個畫面、一條路由一條路由地遷移。隨著時間過去,舊有應用程式會越來越小,直到最後——希望如此——只剩下新系統,然後你就可以終於把舊應用程式和反向代理都移除掉。

在這種情境下,兩個應用程式應該共用登入驗證和後端,但不要共用前端程式碼。

而且如果你非得讓兩個應用程式互相溝通,我會強烈建議你使用像自訂 DOM 事件這種簡單的方法,而不是所謂的「暫時性」轉接器。

因為我們都知道在軟體工程裡,「暫時性」是什麼意思。永遠。 😅 而且不知不覺間,那些轉接器就會變成全新的舊技術包袱。

真實案例:用扼殺者模式把 Backbone 遷移到 Vue

這是一個很舊的 Backbone 應用程式——我得承認,它其實寫得非常好。不過,它還是 Backbone。😄

這個應用程式不算大,所以理論上 Big Bang 也是可行的。但當時我們是一間新創公司,有一天我們聽到那句著名的話:「各位,我們剛賣出了一個還不存在的功能。你們有三個月的時間交付。祝你們好運!」

我當下的反應是:「哈哈哈,真有趣。要我用 Backbone 做這個,絕對不可能。」 幸好,我們的利害關係人很聰明,最後同意採用扼殺者遷移。

我的團隊只有我,還有……兩位初階 Java 開發者。老實說,我發誓他們應該是人生第一次聽到「TypeScript」這個詞。

幸好他們很有企圖心,也學得很快。嗯,其實他們也沒什麼別的選擇。😄 這也是這種策略的另一個優點:初階開發者可以在過程中學習。

我另外建立了一個新的 Vue 應用程式。公司裡有很多人本來就會 Vue,所以選它很合理。

我先把登入畫面遷移過去,當作概念驗證。接著我們開始處理客戶已經付費購買的那個新功能。😉

接下來的一年,我們就一個畫面一個畫面地把整個應用程式遷移過去。

扼殺者模式像絞殺榕一樣呈現

最後,Backbone 完全消失了。而且在整個過程中,我們完全沒有停機。客戶甚至沒注意到遷移正在發生。

跟前一個例子一樣,打包體積明顯變小,建置時間也大幅改善。

不過,扼殺者模式也不是只有陽光和彩虹。

首先是時間因素。遷移過程會拖很久。在我們這個相對小的應用程式裡,還是整整花了一年。

還有遷移永無止境的風險。你大概知道我在說什麼。截止日期一個接一個。新功能一直進來。過一陣子之後,你甚至會開始半懇求半哀求你的產品負責人,拜託挪幾個 story points 給遷移工作。

而且很遺憾,完全不碰到舊程式碼是不可能的。機率是零。相信我,開發者都討厭這件事。我聽過像這樣的話:「Sylwia,妳為什麼要把事情搞得這麼複雜?現在我們得同時懂兩套技術堆疊!」

所以不管你選哪一種策略,你大概都會變成團隊的替罪羊。至少在遷移進行中的時候是這樣。

但等一切結束、打包體積砍半、漏洞報告不再一片紅、客戶也不再抱怨時,你又會突然變成公司的英雄。😄


最後幾句話

遷移舊有應用程式,很像買一棟老房子。房子快要塌掉不是你的錯,但把它修好就是你的責任。你可以花幾週時間一次全部翻新,也可以先搬進去,再一間一間慢慢整理。

唯一真正糟糕的策略,就是什麼都不做。遲早你還是得遷移——只是到時候,你是因為發生了生產環境事故才被迫遷移。😉

那麼,你會怎麼整理你的舊有應用程式?

免責聲明

以上描述的故事都沒有真的發生。或者有可能真的發生了?😉

身為專業人士,以及一個受 NDA 約束的人,我刻意把多個遷移故事混合在一起,確保不會辨識出任何特定公司或專案。這並不特別困難,因為過了一段時間之後,所有遷移看起來都會驚人地相似。😄


如果你喜歡我的文章,也可以在 LinkedIn 追蹤我。


原文出處:https://dev.to/sylwia-lask/who-here-has-worked-with-legacy-the-longer-you-wait-the-worse-it-gets-58bk


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝15   ❤️1
435
🥈
我愛JS
📝1  
13
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
📢 贊助商廣告 · 我要刊登