淺議.NET遺留應用改造
TLDR:本文介紹了遺留應用改造中的一些常見問題,并對改造所能開展的目標、原則、策略進行了概述。

一、背景概述
1、概述
或許僅“遺留應用”這個標題就比較吸睛,因為我聽過太多人吐槽了。Robert Martin在《修改代碼的藝術》這本書中做的一個比喻:
“遺留應用使人聯想到在黑暗、亂糟糟的灌木叢中艱難跋涉,腳下是吸血的螞蝗,身邊飛的是蟄人的昆蟲,空氣中滿是黑暗、黏糊糊、沉重、腐爛垃圾一樣的氣味”。
雖然似乎有點夸張,但也說明遺留應用對開發者來說有多痛苦了。
根據維基百科的定義,遺留系統是一種舊的方法、舊的技術、舊的計算機系統或應用程序,“屬于或與以前的、過時的計算機系統有關” ,但仍在使用中。通常,將系統稱為“遺留系統”意味著它可能已經過時或需要更換。
如同人腦會隨著時間的推移不斷的老化一樣,軟件系統也會由于人員流失、開發者逐漸年長、摩爾定律等因素衰老。隨著市場不斷發展的軟件系統,在越來越龐雜的需求面前,原本可能設計優雅的代碼也將變得越來越復雜,由于缺乏動態維護的文檔和測試用例,使其修改越發不易,變成“老態龍鐘”、“焦油坑”“屎山”般的“遺留系統”。
當然,“遺留應用”并非都是歷史演進的產物,時間不是影響其衰老的唯一因素。
有一些應用,也許用的模式或技術本身還是比較先進,但代碼中過多的運用遠超過當前開發人員水平的技術,導致維護成本比較高,也可能同樣會被扣以“遺留應用”的帽子。
還有一些用古老技術,如基于 COBOL 開發的上古應用,雖然經過改造后也能在新的云原生平臺上運行,”強行刷一波技能點“,但毋庸置疑,也會被認為是遺留應用。
若是無腦的將某些應用判定為“遺留應用”,其實是一種主觀判斷,或是由于信息偏差所造成的刻板印象。
當今時代開發者們面臨的信息偏差問題隨著信息傳播問題越發嚴重,在我們所能接觸到的資訊領域內,往往受限于“信息繭房”“回音壁”的影響,舉目看到的也多是某些新技術,兩相比較之下,我們再來看身邊那些采用老技術打造的某些產品時,更容易感到焦慮,于是既會逐漸對“遺留應用”失去信心,甚至可能會對行業前景充滿悲觀心態。
二、綠地應用和棕地應用
我們或許可以試著忘掉“遺留應用”這個容易主觀評判的名詞,改用更加客觀的概念來理解我們所參與的某些產品或項目,例如,“綠地應用”和“棕地應用”,這種“無副作用”的行業術語將引導我們以更加積極的心態來面對一切。

1、綠地項目開發
綠地開發是在軟件工程等其他學科中,綠地也是一個缺乏先前工作所施加的任何約束的項目。類似于在綠地上施工,無需改造或拆除現有結構。“一張白紙好作畫”,開發者能夠在這個過程中,從無到有的實踐一些新技術或解決方案,又無需過度擔憂實施了哪些東西會對其他組織、組件、技術或甚至代碼風格形成依賴,放開手腳的釋放自己的能力,在充分享受創造的過程中實現個人的某些追求。
綠地項目雖好,但往往可能缺乏明確的方向,有的甚至連基礎成熟的商業模式都沒有,在摸著石子過河的情況下,往往需要具備較強的“開拓能力”才能做好一個新項目。
許多開發者事實上都不具備從一開始就把事情做對的能力,可能會需要走更多的彎路。如果不能明確目標,理順需求,選擇適宜的開發模式,有可能會建立起一座脆弱的空中樓閣,一旦面臨需求發生某種變化就可能導致整體崩塌。
2、棕地項目開發
棕地開發,用于描述在現有(遺留)軟件應用程序/系統存在的情況下,需要開發和部署新軟件系統的問題空間。這意味著任何新的軟件體系結構都必須考慮到現場已有的線上系統并與之共存。棕地項目由于功能模塊比較成熟,往往其已經能夠穩定運行,對其的任何更改都需要針對性制定策略。
相較于綠地項目, 棕地項目具備許多優勢[1]。開發者無需盲目尋求出路,只需基于一個預定的方向進行擴充。開發過程中將更多的依賴對現有技術方案的改良來完成任務,支持使用定義的業務流程和技術解決方案,依賴大量的重用現有代碼以添加新功能。
當然,開發者對系統的任何修改都應建立在對現有系統、服務和數據有透徹的了解的基礎上,可能需要重新設計現有復雜環境的很大一部分,以便它們對新的業務需求具有操作意義。需要詳細準確地了解現有業務和IT的約束條件,這樣新項目才不會失敗。處理遺留代碼不僅會減慢開發過程,還會增加整體開發成本。
“棕地”和“綠地”這樣從城市規劃行業借用的名詞或許會使我們想到一個城市不同的區域,如老城區和新城區。
長沙的河東就是老城區,可以理解為“棕地”,以前的河東,道路逼仄,路面斑駁,車流量又比較大,稍不留心就發生比較嚴重的擁堵,有的小區也綠化面積不多,街邊的房屋上也浸透了歷史感。有時一些小區僅有的公共區域,也經常看到許多老人聚集一起打牌、下棋或跳廣場舞 , 年輕人相對較少。河東雖然夜生活繁榮,但酒吧也大多是基于老建筑物改造而來,迎合了許多人另類的需求。
而河西作為“綠地”的新城區,公園或濕地眾多,建筑物新穎時尚,裝飾盡顯現代風格,綠化率高,空氣質量優良,生機勃勃,道路寬敞,尤其是靠近IT產業聚集地附近,街上到處都是滿懷夢想的年輕人(加班族)。
當然,長沙河東和長沙河西不見得從綜合指數上有多大區別,老城區商業區居多,人流量大,且最近幾年基礎設施建設過程中大修地鐵或管廊,經常稍不留心就擁堵不堪,但若是基建竣工了,想必狀態會大有好轉。而河西事實上早年工業比較發達,留下過不少鐵銹地帶,由于經過了更好的規劃,并在十年的造城運動中被不斷改造,逐漸改造成現在這種宜人的生活環境。
正所謂“三十年河東,三十年河西”,綠地項目并不總是綠地項目,棕地項目卻一定是棕地項目。不管是何種類型的項目,其存在的意義均只有一個,能夠持續創造價值。即便是綠地項目,若不能上線運行,就連一個玩具都不如。而一個棕地項目,哪怕它的功能代碼再爛,若是能響應業務的變化,也是優秀的產品,同樣其若無法匹配時代的發展,我們也該果斷的采取措施。
“與天斗其樂無窮,與地斗,其樂無窮,與棕地應用斗,其樂亦無窮”。
三、如何改造“棕地應用”
1、概述
坦率而言,目前的IT市場,除了創業公司或發展較快的公司外,成熟 IT 企業從零開始實施一個綠地應用的機會不多,當代開發者大部分的時間,其實都需要基于棕地項目進行改造維護。
尤其是對于.NET應用開發者來說,我們參與的許多成熟項目,可能都是基于.NET Framework的傳統應用,使用的也是相對比較陳舊的特性。也有許多應用,雖然看似功能完備,但實際上只是具備了基礎功能的非完全版本。
有時開發人員面對這些項目做出某些技術決策時,可能會遇到一些困難。簡單的代碼改造或小范圍的重構不足以成為更好的選擇,小范圍代碼改造或重構只能頭痛醫頭,腳痛醫腳,不能解決長期存在的問題。重寫更非萬試萬靈的良藥,若是想當然進行重寫,為了實現某些新的特性采取了某些激進的改造措施,看起來不會存在很大的工作量,實際上實則可能陷入更大的泥坑。筆者有一位朋友他們家公司就曾經嘗試過用微服務架構重寫原來一款成熟銷售的產品,在投入了二十多人花費了將近一年的時間后雖然把產品基本上做出來了,但做出來的新東西連原來老產品的許多基本特性都沒達到,最終前功盡棄。
當今時代每一款成熟產品,其背后的業務代碼邏輯可能遠遠不止我們表面上看起來的那么簡單,有許多在當今的我們看起來毫無邏輯、有悖常理的代碼實現,也是蘊含著前人很多智慧的結晶,雖然有可能許多實現在后來者來看有點像是投機取巧,但由于缺乏當時的背景、環境、資源因素,很難從事后對這些需求進行復盤。
因此改造固然很有必要,但如何才能在恰當時機,采取最合適的方法,完成看似這些棘手的任務?這就需要我們在每次改造前認真規劃目標,定義原則,并建立配套的改造策略。
2、目標
應用改造,其作用是在不影響主體業務的情況下,完成架構或技術路線的升級改造,通過對陳舊的應用進行刷新,使其能夠以最小的代價完成改造,并使業務具備不斷的演進能力。
為此,我們應確定明確的目標。參考《聊聊遺留系統改造的“道”與“術”》這篇文章,總結了一些可供參考的目標。
1、既要確保業務的穩定,又要保證需求的快速響應。
改造只是過程,不是目的。架構改造需采取的措施,應立足于當前,著眼于未來。對于穩定項目的更改,需確保現有業務的穩定,并能滿足新需求的快速響應。
2、通過系統改造,實現運營效率和交付效率的提升。根據企業核心需求出發,通過一系列改造,解決運營或交付過程中的痛點,提升效率和提升產品的核心競爭力。
3、發掘核心需求,“重新開發”“持續演進”。
遺留應用雖然穩定,但時代變化太快,應對需求的能力可能越來越弱,通過改造,開發者們可不斷的發掘關鍵特性,可能要采取“重新開發”的手段,對某些遺留功能模塊進行大刀闊斧的改善,實現關鍵特性的“持續演進”。
4、實現架構升級,帶來資源或成本的降低。
必要的架構升級,可促使資源或成本的降低。
3、原則
同樣是《聊聊遺留系統改造的“道”與“術”》這篇文章,也制定了一些原則,其中“演進能力”“適應度函數”“適當耦合”都是來源《演進式架構》這本書。(值得一提的是,這本書也著重強調了演進式架構千萬要避免陷入“面向簡歷開發”“避免鍍金”的誤區,或可以為廣大焦慮讀者提供參考。)
1、將演進能力作為一種架構特征。
應用架構有許多種度量標準,比如安全性、可靠性、應用性等等,每個不同的階段,企業對于架構或許有不同的追求,如追求靈活性,配置性,安全性,可靠性等,有時追求了靈活性,就降低了可用性,追求了安全性,就降低了便利性。演進式架構支持跨多個維度的引導性增量變更,作為一種度量指標,其有別于能直接看到利益的功能性指標,如配置性,便利性,維護性,其更關注于未來的長期演進。企業架構有時總容易陷入要全,要是要快的艱難抉擇之重,而若把演進能力當作一種架構特征,搭建演進能力出更強的架構,也將更好的適應未來形勢的發展。
2、關注“適應度函數”[2],并用其牽引架構的演進。
適應度函數借鑒自進化計算,其含義是“架構的適應度函數為某些架構特征提供了客觀的完整性評估”,常被用來衡量方案對滿足目標的適合度,有助于自動確定系統在多大程度上符合特定的架構目標和限制條件。如軟件開發過程中,可能關注功能的快速實現,而忽略了運營或運維的便利性,若引入適應度函數,則可根據當前運營或運維的實際能力,設計一些人機交互操作+自動化處理的操作相結合的手段,來提升系統相關能力。日志也是如此,過量的日志設計或過少的日志設計對系統來說都是負擔,通過建立適應度函數,評估動機,可確保日志具有良好的結構化和埋點,并包含足夠的有用信息。
3、“小步快跑,增量變更”。
對于遺留系統來說,步子邁太大,容易摔跤,應用改造過程中,不能為了貪圖快速完成而冒進,如果能以增量變更的方式來做改造,每個增量都能夠回滾,這種改造基本上風險是可控的,團隊若建立敏捷式管理手段,完善迭代式開發的規則,確定每個迭代版本需交付的小目標,則可以更好的達到這個目標。
4、策略
1、組建敏捷項目管理團隊
敏捷旨在打破組織的現有溝通體系,建立起人與人之間互相協作的氛圍。團隊共同擁有代碼,并共同為項目成敗負責。敏捷團隊采用迭代式開發方法,通過規劃版本、小步快跑的形式逐步推進產品的改造過程。敏捷項目團隊采用循序漸進的方法進行軟件開發,把一個大項目分為多個相互聯系、但也可獨立運行的用戶故事,在此過程中軟件一直處于可使用狀態。敏捷團隊強化面對面的溝通,依托站立會議,評審會,反思會,卡片估算等機制確保團隊內部的良好溝通,并通過可視化看板的形式對任務的即時狀態進行跟進。
雖然敏捷項目管理常用于新項目的開發過程,但也同樣可用于遺留應用改造。在遺留應用改造過程[3]中,建立起各類角色,組織頭腦風暴或研討會等形式,對該系統存在的問題進行深入討論,并積極思考解決策略,然后通過閱讀文檔或在可用環境中演示來得到更具象的認識,最終通過閱讀代碼[4]來解除剩下的疑問,完成對系統上下文需求的獲取,并將現存問題作為backlog列入迭代計劃,引入持續集成等策略, 提升產品交付過程中的效率及質量。
礙于篇幅,此處省略一些大家普遍熟知的知識。
2、GitFlow版本流程
而在改造過程中,將敏捷項目管理流程與git flow[5]相結合則更能體現效率。
例如,可將既有應用的某些改造過程劃分為未來需完成的若干個版本開發分支,在完成一個版本的改造后,就將其merge到主分支進行測試,預發布和上線,則能更好地實現“小步快跑”。
git-flow 模式會預設master和develop兩個長期分支,前者為產品代碼,后者則為新開發的基礎分支。

開發過程中,開發人員不斷地基于develop分支創建新的分支作為特性分支,并對其進行某些特性改造,改造完成后,將代碼通過pull request的形式合并到develop分支中,并刪除該特性分支。

在對develop分支進行完整測試后,對該分支進行發布構建,并將代碼提交到master分支,使其成為包含最新代碼的穩定分支。如此這般,通過一個又一個的特性分支,實現了應用的改造過程。
介紹一個使用git的小技巧:在代碼改造過程中,一般都會涉及到對源碼的文件遷移。隨著git等源碼管理工具等廣泛使用,開發者可能更習慣于使用git來管理提交信息,往往忽略了注釋,若直接移動文件或文件夾可能會造成源碼提交記錄的丟失,而能靈活的運用git mv
工具,則可以避免此類問題。由于git本身是基于文件來進行版本追蹤的,使用了該工具可以實現對文件進行源碼跟蹤,方便后續通過文件來查看相關需求提交記錄。
git mv [-v] [-f] [-n] [-k] #將 重命名為 ,它必須存在,并且可以是文件,符號鏈接或目錄。
3、封裝。
封裝是面向對象編程的特性,即隱藏對象的屬性和實現細節,僅對外公開接口,控制在程序中屬性的讀和修改的訪問級別;將抽象得到的數據和行為(或功能)相結合,形成一個有機的整體,也就是將數據與操作數據的源代碼進行有機的結合,形成“類”,其中數據和函數都是類的成員。
對于企業應用而言,封裝則意味著將某些具有關聯性的功能模塊或代碼進行封裝,這就可能牽涉到需要將某些具備關聯性的業務邏輯或對象,按照某種形式整合,使其能夠按照合適的形式來響應業務變化。通過封裝數據和功能可實現對現有業務代碼的利用,也可通過暴露API的形式將它們作為服務對外提供。
而基于依賴注入框架提供的特性,在進行業務封裝時,可按照業務的層次,將代碼按文件的形式拆分成高層或低層模塊,再從現有代碼中,抽出基礎設施層、領域層、業務層代碼,再按照依賴倒置原則,通過注入的方式,實現高層模塊對底層模塊的引入。
領域驅動設計也是一種典型的業務分析手法,由于業務邏輯的復雜性,或可通過從現有說明文檔中提取統一語言的形式,實現對遺留應用的逆向工程處理,將其拆分成獨立的限界上下文來實現對現有業務的封裝,并在后期迭代中不斷的強化某些特定的限界上下文,使之能夠獨立演進為粒度更具體的模塊或服務。
礙于篇幅,此處省略一些大家普遍熟知的知識。
4、遷移。
遷移是指針對某個特定基礎組件的遷移改造,常見的有數據庫的遷移、基礎設施的遷移等。
可根據需要建立對應的遷移流程,大體可分為如下幾個步驟[6]:
1、設定遷移的目標;2、制定遷移的計劃;3、遷移計劃的驗證(PoC);4、實施應用程序的遷移;5、校驗遷移結果。
當然,遷移或許是最充滿風險的一部分。以數據庫等遷移為例,雖然近些年來ORM已經得到了廣泛使用,基于EF等Linq技術,實現數據庫等遷移還是比較簡單的。但若是由于遺留應用很少使用這些新特性,可能還需要從存儲過程中將部分業務遷移過來,實現對存儲過程代碼的業務映射,將其用ORM技術進行實現,此工作量有時則想都不敢想。若是基于某種考慮,必須將海量歷史數據進行遷移,此時若按照上述步驟建立周詳的方案,則可有效地避免由于倉促動手造成的被動性。
在數據遷移過程中,數據模型映射、腳本轉換、數據準確性校驗是比較重要的三大問題。
數據模型映射:由于新老系統可能不僅僅只是數據庫引擎不同,連數據庫的字段也有可能會進行小范圍的修改,則需要開發做好模型映射規則,此過程需要既熟悉新系統,又熟悉老系統,并且還要熟悉數據庫設計。建議安排多人協同參與,新老搭配,共同制定計劃、確定范圍。
腳本轉換:不同的數據庫會使用不同的數據庫腳本如存儲過程來完成某些批量操作,則需要安排專業人員對腳本進行轉換。
數據準確性校驗:數據庫遷移過程中若發生了數據丟失,可能會引起比較重要的生產事故, 有時還需通過開發專用的工具來轉換,并從老系統中提取樣本數據來反復演練、測試驗證,確保正式轉換時的高可用性。有時演練階段還需刻意尋找一些臟數據來進行驗證,并對該數據的成因和解決方法進行反復討論調整,避免由于實際生產環境下存在某些幽靈數據污染了全局目標數據的情況。
有條件的情況下,或可參考如何做好大型遺留系統的數據遷移[7]來建立一個更復雜常見下數據遷移的思路。
5、特性升級。
特性升級是指針對某些功能性或非功能性指標的升級,該升級可能包括多種類型:
1、如包括某個基礎包的升級,如將數據庫訪問ORM組件從低版本升級到高版本的升級,可能不會升級較大范圍的變更,做好版本測試很重要。
2、某些基礎特性的升級,如認證機制從傳統的表單認證升級到oauth2的認證機制或jwt的認證機制,有可能牽涉面比較廣,則可能需要運用到灰度發布策略。灰度發布會通過基于老版本構建一個新的特性分支,在其上實現特性升級后,同時部署新版本和老版本,并讓其中一部分用戶使用老版本,而另一部分則使用新特性,并根據應用范圍逐漸擴大,最終實現所有用戶的遷移。灰度發布開始到結束期間的這一段時間,稱為灰度期。
3、環境的升級。如從宿主機遷移到k8s環境,需要的更多的是運維能力建設。
6、代碼重構。
重構就是通過調整程序代碼改善軟件的質量、性能,使其程序的設計模式和架構更趨合理,提高軟件的擴展性和維護性。
在開發過程中,定期對某些維護成本較高的代碼進行重構,可促使其能更好的維護,進而延長代碼的生存周期。重構并非無限制的重構,而應該建立在合理的測試邊界設計的原則上。有條件的情況下,為代碼添加足夠的單元測試代碼,構建代碼的安全網,可有效地確保代碼改造過程與原來的預期匹配。若是無法依托單元測試,端到端測試或接口測試也是非常必要的保障措施。
業界也有許多介紹重構的書,其中尤其以《重構改善既有代碼的設計》這本書最為著名,在這本書里面介紹的眾多手法都比較適合開發者練習。由于知識密度比較大,許多開發者表示不太理解如何實際操作。譯者熊節老師曾經分享過一種策略:先概覽一下全書,對基本的模式有一個初步的認識后,再找一些與書中模式類似的代碼,按照介紹的模式進行一些實際操作,實現對重構的手法有了初步的認識,并通過不斷的練習,形成肌肉記憶,以后如果遇到類似的代碼,也就能手到擒來了。
在《修改代碼的藝術》中也介紹了許多手法,如在每次修改前,梳理相應的修改點,進行特征測試,形成影響圖、特性圖等方式,或是使用mock、建立接縫、面向差異編程等常見的測試驅動開發的手段。重構過程中,依賴注入也是一種非常重要的手法,用依賴注入取代創建可以解耦對象和它們所使用的服務,這會讓軟件更容易測試和擴展。如果不注入真實的依賴而是注入模擬依賴,就很容易測試代碼。依賴注入也會使代碼更容易維護,它有助于將業務決策集中化,簡化對象使用方式。通常[8],為了理解一個新系統,首先看的就是對象實例化的地方。我們查看工廠對象,依賴注入框架,或者其他進行對象實例化的地方。這會告訴我們很多關于系統的信息,讓我們知道如何改進它。
若是沒有足夠的精力按照書中的方法來學習重構,倒是可以通過《代碼整潔之道》中的原則來建立起較為初級的手法。
參考一些通用做法,或可以采用如此操作:
1、代碼掃描工具輔助。
運用sonarqube工具進行代碼掃描,檢查代碼中的異味,并按照操作說明對代碼進行適度的重構。
2、拆大為小。
在visualstudio中,無論是項目、還是解決方案文件夾或代碼文件夾,其命名都應該遵循一定的原則,即為解決某些特定的問題,或者是“單一職責”,若是在上述文件夾中存在了多個職責,則意味著其具備重構的目的,此時我們可以在建立充足的業務知識的前提下,對解決方案文件夾、項目、代碼文件夾進行清理,使其滿足“單一職責”的目的。
3、改善大類和大函數。
在《代碼整潔之道》中,作者認為函數應該足夠“短小”,如20行左右為最佳,當然類的代碼行也不能太多,盡量控制在一個范圍,如一個類包含10個左右的方法,約為200行左右,這就是比較合適的類。若是代碼行超出這個值,則說明函數或類可能存在優化的成本。盡量使每個類、每個函數“只做好一件事”。將方法按照職責進行組合,并逐步將方法遷移到獨立的partial類,并最終實現用依賴注入的方式將類獨立。
4、改善命名。
命名是軟件開發最為重要的一件事情,代碼寫得好不好,首先取決于命名是否符合業務需求。參考《代碼整潔之道》
“優秀的程序員或設計師,其工作之一就是分離解決方案領域和問題領域的概念。與涉及問題領域更為貼近的代碼,應當采用源自問題領域的命名” 開發者有時候會經常會糾結于代碼如何命名,事實上可能是未能建立起業務的概念。所以,當我們無法給某些函數、類、變量命更好的名時,回到對應的業務領域,深挖業務的價值。
4、改善代碼的結構。
常見的代碼結構問題包括縮進、復雜度過高,過多的使用三元表達式、遞歸、過多的For 循環。縮進問題可通過VisualStudio快捷鍵來進行改善,盡量按照VisualStudio提供的默認縮進,反而更適合閱讀。復雜度過高表現在判斷語句過多的情況,有時開發者會寫幾重判斷語句,或將嚴重影響類代碼的可讀性。三元表達式雖然在復雜判斷語句的情況下頗有益處,但過于復雜的三元表達式,遠不如衛語句更適合程序員閱讀。遞歸雖妙,但往往除了作者當時還能理解,是否幾乎都不能理解。For循環可以使用Linq替換,將極大地改善可讀性。
6、重新開發。
重新開發旨在將逆向工程、重構和正向工程組合起來,將現存系統重新構造為新的形式,以開發出質量更高、維護性更好的軟件。
近幾年來,非IT行業的“重新開發”成為了一個新的潮流,如某氣森林就在跨國公司的層層壁壘、攔腰堵截之下,“重新開發”了果味汽水等多款爆款產品,從飲料快消市場撕開了一道口子,創造出了不錯的業績。在該品牌的帶動下,快消行業也掀起了一波“重新開發”的熱潮,許多以前被視作鐵板一塊的市場,如酒水、零食、奶茶等行業,一度都被大廠壟斷多年,卻也被國人創造的許多新興品牌打開了新的商機。
相較而言,IT行業的“重新開發”或許早已是司空見慣的事情,由于IT技術發展飛速,人們經常要面對各種不同的形式的“重新開發”,簡單的有某些接口的重新開發、模塊的重新開發,復雜的有子系統的重新開發,甚至整體業務的重新開發。有時不管某些業務系統是運行的好還是壞,總會有一些由頭將其廢棄,并“重新開發”為另外一套與之相差無幾的新系統。此種情況在政務信息化領域尤其常見,早年我參加過的某些政務系統,十年后再跟業主交流,他們基本上都說已經都更換了,少的換了一兩波,多的可能一年一換。從這個意義上來說,“重新開發”也許是一種促進共同富裕最好的方式。
當然,相較于從零開始開發的“篳路藍縷”,有了之前老系統的成功的先例,站在其巨人的肩膀上重新開發一套,顯然更容易實現。那么換一個角度來理解,“重新開發”實則是一種快速試錯的手段,通過不斷的打造階段性可用的版本跑通模式,再通過后續迭代不斷的完善自我,在必要的時候采取主動的措施,對不能更好迭代的系統進行“重建”,這是當下應對復雜市場環境最為明智的選擇。
當然,“重新開發”不是“綠地應用開發”,其更應該抓住一些核心要素[9],以最小的成本實現目標的達成。
1、識別遺留應用資產。
從現有遺留應用中總結形成組織過程資產,這些資產包括說明文檔、包括遺留系統的描述和流程圖以及災難恢復計劃;公司內部數據中心所在的設施;與遺留系統有關的利益相關者參與情況:這包括當前用戶(包括高管)、開發人員、系統管理員和業務分析人員;遺留系統運行在上面的IT基礎設施;以及開發人員的技術技能。也包括可用于指導未來業務開發的模式、知識和其他文檔,總結遺留應用的問題或風險,使之形成新應用開發的機遇等。
2、發現可復用價值點。
對源碼或數據庫建模及其他研發交付過程資料進行梳理,識別具有復用價值的組件,分析和用圖表描述組件之間的依賴關系及數據流轉模型。
3、組件改造。
運用一些手段對組件進行改造,可以采用諸如修建者,絞殺者,挎斗[10]等方式進行改造。修建者、絞殺者、挎斗常用于微服務架構改造過程[11],不過事實上也并非只能在微服務架構改造[12]過程中才能使用,傳統的應用架構重新開發過程中都可以運用。
在修建者模式中,開發者需將待修繕部分進行隔離,并用新的方式加以實現,修建過程中,需確保除被修改內容外,其他部分功能仍然正常使用。得益于.NET Core依賴注入框架的便利性,開發者可通過對遺留應用功能的識別,梳理出有價值的ServiceProvider,并對其進行修建,這部分改造工作也是前面所提到的“封裝”模式的運用。這也是本文第三次提到依賴注入。
絞殺者模式則是基于現有應用的外圍進行新模塊的開發,用新的方式將不斷地開發新的組件。由于在開發過程中,暫不涉及核心功能的修改,實質上是對原業務的重寫,逐步實現對老系統的替換。隨著時間的推移,模塊改造過程也會逐漸地從外圍深入到內部,最終實現對老系統的整體替換。
挎斗模式更類似于適配器的模式,通過接入功能代碼集中在一起,作為一個獨立的進程或服務,為不同語言的遺留系統提供一個同構的接入接口。在部署結構上,挎斗服務與原遺留系統緊密相關,原遺留系統在哪里它就在哪里。對于原遺留系統應用程序的每個實例,

4、評審。
改造過程中定期組織相關方進行評審,確保價值的有效傳承。
四、總結
遺留應用改造相較于新項目開發,雖然可能會接觸各種屎山,但也有望站在成熟業務的巨人肩膀構建自己的業務體系,也同樣能夠實現技能的長期積累,值得開發人員圍繞其投入更多精力。
當今時代,技術發展日新月異,我們總能接觸到許多新的知識體系或方法,但盲目的沉迷于新技術的“船貨崇拜”或執著的堅守某些老技術,都不可取。
我們在新技術的同時,也應該關注產品的本身價值,通過各種形式的改造,豐富其產品特性,使之能夠適應當前和未來形勢的變化,不斷煥發出新的生機。
References
[1]
?綠地項目, 棕地項目具備許多優勢:?https://segmentfault.com/a/1190000040410354[2]
?關注“適應度函數”:?https://www.infoq.cn/article/bpwfovfcjzukg6e*buxz[3]
?遺留應用改造過程:?https://insights.thoughtworks.cn/legacy-system/[4]
?閱讀代碼:?https://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052[5]
?git flow:?https://www.git-tower.com/learn/git/ebook/cn/command-line/advanced-topics/git-flow[6]
?如下幾個步驟:?https://new.qq.com/omn/20200630/20200630A0SRPE00.html?pc[7]
?如何做好大型遺留系統的數據遷移:?https://cloud.tencent.com/developer/article/1877779[8]
?通常:?https://blog.51cto.com/u_14977574/2546965[9]
?核心要素:?http://www.d1net.com/cloud/tech/336477.html[10]
?挎斗:?http://m.blog.itpub.net/31562044/viewspace-2650826/[11]
?微服務架構改造過程:?https://servicecomb.apache.org/docs/how-to-reform-a-legacy-system/[12]
?微服務架構改造:?https://www.infoq.cn/article/8qvoyyrmb0dgpny9l0s5