歡迎光臨
每天分享高質量文章

如何改善遺留的代碼庫 | Linux 中國

在每一個程式員、專案管理員、團隊領導的一生中,這都會至少發生一次。原來的程式員早已離職去度假了,給你留下了一坨幾百萬行屎一樣的、勉強支撐公司運行的代碼和(如果有的話)跟代碼驢頭不對馬嘴的文件。
— Jacques Mattheij


本文導航
編譯自 | https://jacquesmattheij.com/improving-a-legacy-codebase 
 作者 | Jacques Mattheij
 譯者 | aiwhj

在每一個程式員、專案管理員、團隊領導的一生中,這都會至少發生一次。原來的程式員早已離職去度假了,給你留下了一坨幾百萬行屎一樣的、勉強支撐公司運行的代碼和(如果有的話)跟代碼驢頭不對馬嘴的文件。

你的任務:帶領團隊擺脫這個混亂的局面。

當你的第一反應(逃命)過去之後,你開始去熟悉這個專案。公司的管理層都在關註著你,所以專案只能成功;然而,看了一遍代碼之後卻發現失敗幾乎是不可避免。那麼該怎麼辦呢?

幸運(不幸)的是我已經遇到好幾次這種情況了,我和我的小伙伴發現將這坨熱氣騰騰的屎變成一個健康可維護的專案是一個有豐厚利潤的業務。下麵這些是我們的一些經驗:

備份

在開始做任何事情之前備份與之可能相關的所有檔案。這樣可以確保不會丟失任何可能會在另外一些地方很重要的信息。一旦修改了其中一些檔案,你可能花費一天或者更多天都解決不了這個愚蠢的問題。配置資料通常不受版本控制,所以特別容易受到這方面影響,如果定期備份資料時連帶著它一起備份了,還是比較幸運的。所以謹慎總比後悔好,複製所有東西到一個絕對安全的地方並不要輕易碰它,除非這些檔案是只讀樣式。

重要的先決條件:必須確保代碼能夠在生產環境下構建運行並產出

之前我假設環境已經存在,所以完全丟了這一步,但 Hacker News 的眾多網友指出了這一點,並且事實證明他們是對的:第一步是確認你知道在生產環境下運行著什麼東西,也意味著你需要在你的設備上構建一個跟生產環境上運行的版本每一個位元組都一模一樣的版本。如果你找不到實現它的辦法,一旦你將它投入生產環境,你很可能會遭遇一些預料之外的糟糕事情。確保每一部分都儘力測試,之後在你足夠確信它能夠很好的運行的時候將它部署生產環境下。無論它運行的怎麼樣都要做好能夠馬上切換回舊版本的準備,確保日誌記錄下了所有情況,以便於接下來不可避免的 “驗屍” 。

凍結資料庫

直到你修改代碼結束之前盡可能凍結你的資料庫,在你已經非常熟悉代碼庫和遺留代碼之後再去修改資料庫。在這之前過早的修改資料庫的話,你可能會碰到大問題,你會失去讓新舊代碼和資料庫一起構建穩固的基礎的能力。保持資料庫完全不變,就能比較新的邏輯代碼和舊的邏輯代碼運行的結果,比較的結果應該跟預期的沒有差別。

寫測試

在你做任何改變之前,盡可能多的寫一些端到端測試和集成測試。確保這些測試能夠正確的輸出,並測試你對舊的代碼運行的各種假設(準備好應對一些意外狀況)。這些測試有兩個重要的作用:其一,它們能夠在早期幫助你拋棄一些錯誤觀念,其二,這些測試在你寫新代碼替換舊代碼的時候也有一定防護作用。

要自動化測試,如果你有 CI 的使用經驗可以用它,並確保在你提交代碼之後 CI 能夠快速的完成所有測試。

日誌監控

如果舊設備依然可用,那麼添加上監控功能。在一個全新的資料庫,為每一個你能想到的事件都添加一個簡單的計數器,並且根據這些事件的名字添加一個函式增加這些計數器。用一些額外的代碼實現一個帶有時間戳的事件日誌,你就能大概知道發生多少事件會導致另外一些種類的事件。例如:用戶打開 APP 、用戶關閉 APP 。如果這兩個事件導致後端呼叫的數量維持長時間的不同,這個數量差就是當前打開的 APP 的數量。如果你發現打開 APP 比關閉 APP 多的時候,你就必須要知道是什麼原因導致 APP 關閉了(例如崩潰)。你會發現每一個事件都跟其它的一些事件有許多不同種類的聯繫,通常情況下你應該儘量維持這些固定的聯繫,除非在系統上有一個明顯的錯誤。你的標的是減少那些錯誤的事件,盡可能多的在開始的時候通過使用計數器在呼叫鏈中降低到指定的級別。(例如:用戶支付應該得到相同數量的支付回呼)。

這個簡單的技巧可以將每一個後端應用變成一個像真實的簿記系統一樣,而像一個真正的簿記系統,所有數字必須匹配,如果它們在某個地方對不上就有問題。

隨著時間的推移,這個系統在監控健康方面變得非常寶貴,而且它也是使用原始碼控制修改系統日誌的一個好伙伴,你可以使用它確認 BUG 引入到生產環境的時間,以及對多種計數器造成的影響。

我通常保持每 5 分鐘(一小時 12 次)記錄一次計數器,但如果你的應用生成了更多或者更少的事件,你應該修改這個時間間隔。所有的計數器公用一個資料表,每一個記錄都只是簡單的一行。

一次只修改一處

不要陷入在提高代碼或者平臺可用性的同時添加新特性或者是修複 BUG 的陷阱。這會讓你頭大,因為你現在必須在每一步操作想好要出什麼樣的結果,而且會讓你之前建立的一些測試失效。

修改平臺

如果你決定轉移你的應用到另外一個平臺,最主要的是跟之前保持一模一樣。如果你覺得需要,你可以添加更多的文件和測試,但是不要忘記這一點,所有的業務邏輯和相互依賴要跟從前一樣保持不變。

修改架構

接下來處理的是改變應用的結構(如果需要)。這一點上,你可以自由的修改高層的代碼,通常是降低模塊間的橫向聯繫,這樣可以降低代碼活動期間對終端用戶造成的影響範圍。如果舊代碼很龐雜,那麼現在正是讓它模塊化的時候,將大段代碼分解成眾多小的部分,不過不要改變數和資料結構的名字。

Hacker News 的 mannykannot[1] 網友指出,修改高層代碼並不總是可行,如果你特別不幸的話,你可能為了改變一些架構必須付出沉重的代價。我贊同這一點也應該在這裡加上提示,因此這裡有一些補充。我想額外補充的是如果你修改高層代碼的時候修改了一點點底層代碼,那麼試著只修改一個檔案或者最壞的情況是只修改一個子系統,盡可能限制修改的範圍。否則你可能很難除錯剛纔所做的更改。

底層代碼的重構

現在,你應該非常理解每一個模塊的作用了,準備做一些真正的工作吧:重構代碼以提高其可維護性並且使代碼做好添加新功能的準備。這很可能是專案中最消耗時間的部分,記錄你所做的任何操作,在你徹底的記錄並且理解模塊之前不要對它做任何修改。之後你可以自由的修改變數名、函式名以及資料結構以提高代碼的清晰度和統一性,然後請做測試(情況允許的話,包括單元測試)。

修複 bug

現在準備做一些用戶可見的修改,戰鬥的第一步是修複很多積累了幾年的 bug。像往常一樣,首先證實 bug 仍然存在,然後編寫測試並修複這個 bug,你的 CI 和端對端測試應該能避免一些由於不太熟悉或者一些額外的事情而犯的錯誤。

升級資料庫

如果你在一個堅實且可維護的代碼庫上完成所有工作,你就可以選擇更改資料庫樣式的計劃,或者使用不同的完全替換資料庫。之前完成的步驟能夠幫助你更可靠的修改資料庫而不會碰到問題,你可以完全的測試新資料庫和新代碼,而之前寫的所有測試可以確保你順利的遷移。

按著路線圖執行

祝賀你脫離的困境並且可以準備添加新功能了。

任何時候都不要嘗試徹底重寫

徹底重寫是那種註定會失敗的專案。一方面,你在一個未知的領域開始,所以你甚至不知道構建什麼,另一方面,你會把所有的問題都推到新系統馬上就要上線的前一天。非常不幸的是,這也是你失敗的時候。假設業務邏輯被髮現存在問題,你會得到異樣的眼光,那時您會突然明白為什麼舊系統會用某種奇怪的方式來工作,最終也會意識到能將舊系統放在一起工作的人也不都是白痴。在那之後。如果你真的想破壞公司(和你自己的聲譽),那就重寫吧,但如果你是聰明人,你會知道徹底重寫系統根本不是一個可選的選擇。

所以,替代方法:增量迭代工作

要解開這些線團最快方法是,使用你熟悉的代碼中任何的元素(它可能是外部的,也可能是內核模塊),試著使用舊的背景關係去增量改進。如果舊的構建工具已經不能用了,你將必須使用一些技巧(看下麵),但至少當你開始做修改的時候,試著儘力保留已知的工作。那樣隨著代碼庫的提升你也對代碼的作用更加理解。一個典型的代碼提交應該最多兩三行。

發佈!

每一次的修改都發佈到生產環境,即使一些修改不是用戶可見的。使用最少的步驟也是很重要的,因為當你缺乏對系統的瞭解時,有時候只有生產環境能夠告訴你問題在哪裡。如果你只做了一個很小的修改之後出了問題,會有一些好處:

◈ 很容易弄清楚出了什麼問題
◈ 這是一個改進流程的好位置
◈ 你應該馬上更新文件展示你的新見解

使用代理的好處

如果你做 web 開發那就謝天謝地吧,可以在舊系統和用戶之間加一個代理。這樣你能很容易的控制每一個網址哪些請求定向到舊系統,哪些請求定向到新系統,從而更輕鬆更精確的控制運行的內容以及誰能夠看到運行系統。如果你的代理足夠的聰明,你可以使用它針對個別 URL 把一定比例的流量發送到新系統,直到你滿意為止。如果你的集成測試也能連接到這個接口那就更好了。

是的,但這會花費很多時間!

這就取決於你怎樣看待它了。的確,在按照以上步驟優化代碼時會有一些重覆的工作步驟。但是它確實有效,而這裡介紹的任何一個步驟都是假設你對系統的瞭解比現實要多。我需要保持聲譽,也真的不喜歡在工作期間有負面的意外。如果運氣好的話,公司系統已經出現問題,或者有可能會嚴重影響到客戶。在這樣的情況下,我比較喜歡完全控制整個流程得到好的結果,而不是節省兩天或者一星期。如果你更多地是牛仔的做事方式,並且你的老闆同意可以接受冒更大的風險,那可能試著冒險一下沒有錯,但是大多數公司寧願採取稍微慢一點但更確定的勝利之路。


via: https://jacquesmattheij.com/improving-a-legacy-codebase

作者:Jacques Mattheij[3] 譯者:aiwhj 校對:JianqinWangwxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出

LCTT 譯者

aiwhj ? ?
共計翻譯:4 篇
貢獻時間:28 天


推薦文章

< 左右滑動查看相關文章 >

點擊圖片、輸入文章 ID 或識別二維碼直達

赞(0)

分享創造快樂