接手了嚴重過時的軟體,到底是該逐步重構還是摧毀重寫呢?

1功能改進

一個好的起點是定義重寫和重構不是什麼,它們是改進應用程式功能的策略。這種型別的工作,無論是修復缺陷,交付新特性,還是清理使用者介面,我們都可以稱之為增強。它是關於改進應用程式為使用者所做的事情的,正如我們稍後將看到的那樣,它是正常的狀態。

接手了嚴重過時的軟體,到底是該逐步重構還是摧毀重寫呢?

但在某些情況下,功能增強的範圍可能相當大。例如,企業可能想要確定某應用程式是為正確的使用者群提供服務的,但所有的功能都需要徹底檢查。這種情況也可能被稱為重寫,但是在這裡我們要做一個區分。因為這種型別的工作需要構建所有新的功能,所以它與新建專案基本上沒有區別。當需要定義新的功能需求時,從零開始開發一個獨立的系統,並且不能繼承原邏輯或程式碼,我們會將其視為新開發的應用程式,而不是重寫。

2重構是什麼

嚮應用程式新增功能並不是本文的重點。我們的場景是:應用程式通常會執行預期的操作,但缺少如何執行的能力,換句話說,即缺少系統的非功能或 質量屬性。例如,使用者可能對這些功能感到滿意,但應用程式可能過於難以維護,或者可能頻繁崩潰,或者在峰值負載下效能很差。當這些非功能屬性缺失時,我們才會考慮重寫或重構。

關於重構,我們經常使用這個術語來指代不同的工作範圍。 Martin Fowler在他的 《 重構》 一書中是這樣定義重構:

重構是一種用於重組現有程式碼主體,在不更改其外部行為的情況下更改其內部結構的規範

重構是一種用於重組現有程式碼主體,在不更改其外部行為的情況下更改其內部結構的規範技術。

從這種純粹的意義上講,重構主要是為了使程式碼更易於維護。這可能是分解冗長的或複雜的函式,修復不一致的命名,新增單元測試,或者重組類的層次結構、資料結構或模式。請注意,沒有更改任何對使用者可見的內容,但是修改了內部的程式碼結構,使其更容易為開發人員所使用,從而提高了我們的工作效率(和幸福感!)。

然而,在我們做重寫或重構決策的場景中,這個定義過於嚴格。在我們的場景中,當我們談論重構時,我們通常不會區分內部和外部,而是 會區分功能和非功能。例如,我們可能會說,我們選擇重構現有的程式碼庫,以提高應用程式的可靠性或效能。從技術上講,這些質量屬性不是系統的內部屬性(使用者可以明顯感知到它們,因為它們直接影響使用者),它們只是非功能性的。這可能是一個過於學術的區別,但本著精確的精神,我認為有必要指出來。在本文中,我們將使用更廣泛的重構定義:

重構是一種方法,通過這種方法對現有的程式碼主體進行增量重組,以提高系統的質量屬性。

重構是一種方法,通過這種方法對現有的程式碼主體進行增量重組,以提高系統的質量屬性。

最後,需要注意的是重構是關於迭代變更的。它會對應用程式進行細微的調整,將其交付,然後沖洗並重復。在功能增強的基礎上,重構可以使我們的使用者滿意,使我們的程式碼庫保持健康,並能最大限度地減少技術債和功能缺陷。然而,如果被忽略,我們可能需要考慮更重的替代方案。

3重寫是什麼

與重構一樣,重寫也有著相同的基本目標:改善應用程式的非功能性。區別在於更改了多少。簡單地說,如果重構是管道膠帶,那麼重寫就是一個大錘或一個反剷。它不是要對現有的功能進行漸進式的改進,而是要摧毀它,重新構建。對應於我們討論過的其他型別的開發工作,我們可以這樣視覺化地展示重寫:

接手了嚴重過時的軟體,到底是該逐步重構還是摧毀重寫呢?

可以說,重寫是一項涉及到 對系統進行重大更改的工作,以便對其質量屬性進行根本性的改進。但也有灰色區域。重寫工作通常會擴散到其他象限。例如,一個應用程式可能會因為技術債而癱瘓,以至於幾乎不可能再新增新特性。我們可能會選擇重寫然後建立一個新的基礎來提高可維護性和可擴充套件性(質量屬性),但是在重寫的過程中,我們也可能會加入一些新的特性來滿足業務需求。它基本上是重寫的,但也進行了一些增強。

同樣地,在重寫和重構的邊界上也存在一些模糊性。在一些“平移”(lift-and-shift)的情況下,系統被遷移到一個新的平臺上,使得它在本質上成為了一個不同的應用程式,但其中的程式碼實現基本相同,即沒有重構。這感覺像是重寫了,但真的是這樣嗎?需要做多少更改才能被視為是重寫呢?

再次,讓我們看看是否可以增加一些精度。在本書中,我們使用以下定義:

重寫就是重新構建存在於遺留應用程式中的相同功能,但使用不同的語言 / 框架,在新的程式碼庫(不僅僅是分支)中維護,並作為一個全新的構件進行部署(可能部署到不同的平臺上,如伺服器、硬體、無伺服器、等)。

重寫就是重新構建存在於遺留應用程式中的相同功能,但使用不同的語言 / 框架,在新的程式碼庫(不僅僅是分支)中維護,並作為一個全新的構件進行部署(可能部署到不同的平臺上,如伺服器、硬體、無伺服器、客戶端等)。

換句話說,我們要畫一些明確的界限。例如,如果我們要重寫一個重要的函式、類甚至模組,但是我們的工作是在程式碼庫主線的分支上完成的,那麼這就不是重寫。同樣地,如果我們重新實現了應用程式的一部分,但是系統本身仍作為同一構件(二進位制檔案、WAR 等)部署,這也不是重寫。在我們的場景中,重寫是很大的(BIG)。它們是涉及需要構建和部署全新應用程式的重大更改。是的,在實現這一目標的過程中可能會有一些漸進的步驟,我們稍後將會看到,但這是一種與重構根本不同的工作。

為了幫助自己梳理具體的情況,你可以把它畫出來。即對於可能現代化或改進應用程式的不同途徑,究竟要更改些什麼?這裡有一個例子:

接手了嚴重過時的軟體,到底是該逐步重構還是摧毀重寫呢?

實際上,變更的性質可能與重寫或重構的定義不一致,但這沒有關係。例如,上圖可能表示了這樣一種情況:我們提議使用一組更現代化的技術來重新實現某個服務,但同時保持公開的 API 和底層持久層結構不變。這是一個較小變更和重大變更的混合體,所以應該如何確切地標記它可能仍然不清楚。然而,重要的是,我們已經瞭解了更深層次的細節,這將有助於我們更好地思考和證明這個決定。

現在我們的準備工作已經差不多完成了,但是在我們開始我們的旅程之前,讓我們先把它們放在一起,看看這些不同型別的開發工作是如何適應給定應用程式生命週期的。換言之,讓我們探究一下起源故事,以便重寫。

4重寫的起源故事

這一切都是從新建開發階段開始的,在這個階段我們有了一個想法,並開始從中構建一個功能強大的應用程式。經過數週或數月的不懈努力,某些產品最終被投放到“市場”(可能是實際的付費客戶,或只是一組內部業務使用者,等等)。如果該應用程式很受歡迎,它將會在一段時間內處於增強的平衡階段,在此階段會新增新功能並修復缺陷。每個人都很開心。但最終,技術債會累積起來,我們開始看到努力的回報在遞減,雖然在過去一週的開發足以新增一個全新的功能,但現在一週卻不足以改變一個按鈕的顏色。

接手了嚴重過時的軟體,到底是該逐步重構還是摧毀重寫呢?

在這一點上,我們可能會質疑是否值得投入更多的時間和金錢。此外,自從應用程式首次釋出以來,可能已經出現了一些令人興奮的新技術,我們可能會對如何利用這些技術來使我們的應用程式更具彈性、更易於使用、更具效能等抱有一些巨集偉的設想,因此我們開始制定重寫計劃。其想法是在短時間內凍結現有系統的開發,然後將資源轉移到替換系統上。我們將首先構建基礎(使用更現代化的模式、工具、語言等等),然後將現有的功能遷移到該基礎中。使用者只需要安然度過“暫停”(即不需要任何新的更新),但當重寫系統就位時,工作效率就會是之前的兩倍(或更多!)。

雖然這個計劃看起來很直接,但它掩蓋了一些關鍵的風險:技術、組織和心理因素,所有這些因素都會導致重寫階段是極不穩定的。隨著這一階段的拖延,我們成功替換的機會會越來越渺茫。在接下來的文章中,我們將探討一些隱藏在重寫工作中的危險,以及為什麼我們總是不顧這些危險勇往直前的原因。

原文連結:

http://www.bennorthrop.com/rewrite-or-refactor-book/chapter-1-what-we-mean-by-rewrite-and-refactor.php

InfoQ 讀者交流群上線啦!各位小夥伴可以掃描下方二維碼,新增 InfoQ 小助手,回覆關鍵字“進群”申請入群。回覆“資料”,獲取資料包傳送門,註冊 InfoQ 網站後,可以任意領取一門極客時間課程,免費滴!大家可以和 InfoQ 讀者一起暢所欲言,和編輯們零距離接觸,超值的技術禮包等你領取,還有超值活動等你參加,快來加入我們吧!

  • 點個在看少個 bug👇

點個在看少個 bug👇

喜歡這篇文章嗎?立刻分享出去讓更多人知道~

相關文章