接手歷史悠久的老項(xiàng)目,干or跑?
本文轉(zhuǎn)載自微信公眾號(hào)「跨界架構(gòu)師」,作者Zachary。轉(zhuǎn)載本文請(qǐng)聯(lián)系跨界架構(gòu)師公眾號(hào)。
大家好,我是Z哥。
這是我的第199篇原創(chuàng),離200篇還有一步之遙。為了慶祝這重要時(shí)刻,我也在琢磨著要不要在第200篇的時(shí)候搞個(gè)什么小活動(dòng)。還沒想好,等下周再看吧。
不過不管怎樣,你也應(yīng)該為Z哥的堅(jiān)持點(diǎn)個(gè)贊吧,哈哈。所以,看完文章后不要忘了劃到文末點(diǎn)贊哦~
好了,回到正題。
很多人吐槽老項(xiàng)目是怎么怎么垃圾。這的確是一個(gè)很大的問題。根據(jù) IEEE Spectrum 之前發(fā)布的一份報(bào)告數(shù)據(jù):
自 2010 年以來,全世界IT 產(chǎn)品和服務(wù)支出約為 35 萬億美元,其中約四分之三用于運(yùn)營(yíng)和維護(hù)現(xiàn)有的 IT 系統(tǒng)。另外,至少有 2.5 萬億美元用于嘗試替換舊的 IT 系統(tǒng),而且其中約有 7200 億美元被浪費(fèi)在失敗的替換工作上。
https://spectrum.ieee.org/computing/it/inside-hidden-world-legacy-it-systems
因此,面對(duì)不堪的、充滿歷史味道的老項(xiàng)目對(duì)程序員群體來說會(huì)是一個(gè)常態(tài)。
不過我們可以做些什么呢?難道是要么硬著頭皮去填別人挖的坑,要么跑路么?
如果你不跑路的話,最常見的應(yīng)對(duì)方式無非是「重寫」或者「重構(gòu)」。
一 重寫
一般來說,越是年輕的程序員越是最喜歡重寫。除了年輕氣盛的原因外,主要是因?yàn)橥耆珨[脫了老代碼的技術(shù)債和束縛,可以盡情地施展自己的才華。
但是對(duì)于老系統(tǒng)的重寫,如果沒有高層的支持、業(yè)務(wù)部門的配合,靠譜的開發(fā)團(tuán)隊(duì),這事大概率很難能按預(yù)期完成。
而且選擇「重寫」方案不僅僅是重新寫一套代碼這么簡(jiǎn)單,系統(tǒng)切換也是一個(gè)頭疼的事情,在復(fù)雜一些的項(xiàng)目里,它的難度并不亞于重寫一套代碼。
而且一旦選擇「重寫」,意味著在一段時(shí)間內(nèi)要么老系統(tǒng)并行開發(fā),要么會(huì)攢一堆需求,業(yè)務(wù)方能不能持續(xù)支持「重寫」也會(huì)面臨很大的挑戰(zhàn)。
就算這些問題都能搞定。「重寫」還可能造成的一個(gè)結(jié)果是:在過了幾個(gè)月后,重寫后的系統(tǒng)又成了別人眼中的“破爛不堪的老系統(tǒng)”。這是最諷刺的……
如果在重寫過程中,由于任何原因犧牲了對(duì)質(zhì)量的要求,這個(gè)諷刺很容易成為現(xiàn)實(shí)。
因此,很多公司之所以不支持重寫,理由是:
又不是不能用。
這么看來他們并不是迂腐,也是一種理性的選擇,也是沒錯(cuò)的。
二 重構(gòu)
相對(duì)地,年紀(jì)越大的程序員可能更傾向于重構(gòu)。畢竟他們有更多的機(jī)會(huì)見過、經(jīng)歷過那些慘痛的重寫事件。當(dāng)然更主要的原因是,他們掌握了更多應(yīng)對(duì)“垃圾代碼”的方式方法,認(rèn)為通過小范圍的代碼重構(gòu)也能化腐朽為神奇,提升項(xiàng)目質(zhì)量。
的確沒錯(cuò),但這是有一個(gè)前提的。就是垃圾代碼的產(chǎn)生速度要低于重構(gòu)的速度。可是對(duì)大部分中小公司來說,開發(fā)團(tuán)隊(duì)可能不具備這樣的條件。因?yàn)榧夹g(shù)好的程序員要么走向了管理崗位,要么身上的重?fù)?dān)太多,沒太多時(shí)間來做重構(gòu)。再加上技術(shù)差的程序員繼續(xù)復(fù)制粘貼,挖更多的坑。通過重構(gòu)來改善老項(xiàng)目,可能永遠(yuǎn)在“重構(gòu)的路上”。
而且,如果項(xiàng)目的技術(shù)復(fù)雜度大于業(yè)務(wù)復(fù)雜度,那么配合重構(gòu)的測(cè)試成本就很高了。
除了這兩種常見的方式外,還有其它的方式也能應(yīng)對(duì)這些老系統(tǒng)。它們的的風(fēng)險(xiǎn)和收益在下圖里已經(jīng)很好地呈現(xiàn)出來了。
其實(shí)Z哥認(rèn)為,這些方式我們都可以用,但是要沿著正確的思路來。(絞殺是什么?往下看)
首先從業(yè)務(wù)角度來考慮到底是選擇哪種方案。
- 從業(yè)務(wù)敏捷視角來看,能不能讓響應(yīng)力變得更快?
- 從運(yùn)營(yíng)效率視角來看,如何通過系統(tǒng)改造,提升業(yè)務(wù)運(yùn)營(yíng)的效率?
- 從客戶洞見視角來看,如何讓系統(tǒng)更好地發(fā)現(xiàn)客戶洞見,進(jìn)而更好地理解客戶需求和演進(jìn)產(chǎn)品?
其次是從系統(tǒng)本身來考量,比如,我們想通過改造獲得多大的彈性?
最后,根據(jù)整體上對(duì)業(yè)務(wù)幫助的價(jià)值大小來選擇哪一種方式。如果對(duì)業(yè)務(wù)的幫助巨大,完全值得投入大量資源去重寫,那么就不要畏畏縮縮的小范圍重構(gòu)。如果對(duì)業(yè)務(wù)幫助很小,那么也就別想著重寫了,老老實(shí)實(shí)在小范圍內(nèi)修修補(bǔ)補(bǔ)就好了。
當(dāng)你拿捏不準(zhǔn)方案的時(shí)候,選擇按模塊替換會(huì)是一個(gè)不錯(cuò)的辦法,相當(dāng)于通過每一次換一個(gè)零部件,把整個(gè)機(jī)器上的零部件全部翻新一遍,以此完成重寫的效果。Martin Fowler 稱之為「絞殺法」。
「絞殺法」的最佳實(shí)踐是DDD+微服務(wù),因?yàn)樗鼈兛梢蕴峁└玫摹父綦x性」「自治性」,更有利于「替換」的進(jìn)行。
當(dāng)確定好改造的方案后,還需要制定度量指標(biāo),在實(shí)施改造的過程中持續(xù)關(guān)注這些指標(biāo)的變化。最好能夠通過可視化工具將這些信息共享出來,這樣的好處有兩點(diǎn),
- 一是能夠讓團(tuán)隊(duì)了解改造進(jìn)展和成果,確保改造朝著正確的方向走;
- 二是能夠讓相關(guān)的干系人(領(lǐng)導(dǎo)、業(yè)務(wù)部門)也能了解到工作的進(jìn)展情況,提高對(duì)預(yù)期的確定性,以持續(xù)獲得他們的支持。
關(guān)于具體重構(gòu)方法我就不說了,可以翻看我之前的一篇文章《好的重構(gòu)方法才能擺脫“屎山”》。
關(guān)于重寫可以來聊幾句,之前我們沒聊過。我的建議是遵守以下幾個(gè)原則。
系統(tǒng)具備演進(jìn)的能力。
憑借度量指標(biāo)來把握演進(jìn)的方向。
小步快速迭代。
01 系統(tǒng)具備演進(jìn)的能力
既然是重寫系統(tǒng),那么一定要為可見的未來做一些預(yù)留,方便后續(xù)的演進(jìn)。畢竟能夠決定重寫的系統(tǒng)自然是比較核心的系統(tǒng),不管在技術(shù)上還是在業(yè)務(wù)上都會(huì)持續(xù)發(fā)生變化,注重系統(tǒng)的演進(jìn)能力就是在降低未來項(xiàng)目復(fù)雜度增長(zhǎng)的幅度。
具體的方法可以概括為3個(gè)詞:抽象、分類(分層)、解耦。
02 憑借度量指標(biāo)來把握演進(jìn)的方向
前面也提到了度量指標(biāo)的價(jià)值。這里繼續(xù)強(qiáng)調(diào)一下它的重要性。
如果說在重寫時(shí)期,度量指標(biāo)是衡量重寫工作完成好壞的尺子,那么在重寫后它就是指引未來系統(tǒng)演進(jìn)的明燈。
不管系統(tǒng)后續(xù)要做什么升級(jí)改造,對(duì)自身了解的越清楚,做出的決定和選擇自然越合理。而度量指標(biāo)起到的就是這個(gè)效果。
一個(gè)缺少度量指標(biāo)的系統(tǒng),隨著業(yè)務(wù)邏輯的不斷堆砌,很容易自由生長(zhǎng)過了頭。
03 小步快速迭代
近些年隨著CI/CD的普及,小步快跑式的敏捷開發(fā)被越來越多人提到,并且開始運(yùn)用。
它的好處是顯而易見的,對(duì)項(xiàng)目的可控性更高,容錯(cuò)性更強(qiáng)。
如果你不知道“小步”該多“小”?你就按發(fā)布出了問題能不能快速回滾作為標(biāo)準(zhǔn)去考慮就好了。能快速回滾的迭代節(jié)奏就是“小步”。
其實(shí)重寫系統(tǒng)完之后還有一個(gè)頭疼的事情要解決,就是數(shù)據(jù)遷移。業(yè)界常用的方案有四種,我先大致列一下主要思路,后續(xù)再發(fā)文展開聊聊。
01 雙寫
- 新庫(kù)配置為舊庫(kù)的從庫(kù),從舊庫(kù)同步數(shù)據(jù)。
- 數(shù)據(jù)寫入的時(shí)候,不僅要寫入舊庫(kù),也要寫入新庫(kù)。
- 數(shù)據(jù)校驗(yàn)。
- 兩邊數(shù)據(jù)完全同步后,灰度切流量。
由于有雙寫的存在,所以在切換的過程中出現(xiàn)任何的問題,都可以將讀寫流量隨時(shí)切換到舊庫(kù)去,安全感極強(qiáng)。
02 異步雙寫+對(duì)賬
這個(gè)方案其實(shí)是雙寫的變種,將同步寫入變成了異步寫入。也因此需要一個(gè)對(duì)賬機(jī)制,確保最終一致性的達(dá)成。
一般這個(gè)異步的機(jī)制要么通過 MQ ,要么通過數(shù)據(jù)庫(kù)的 binlog 進(jìn)行。
03 直接用新庫(kù),數(shù)據(jù)惰性遷移
這個(gè)方案中間需要架設(shè)一個(gè)緩存層,用來存放數(shù)據(jù)的key。過程是這樣的:
- 先訪問新庫(kù),如果有數(shù)據(jù)則直接進(jìn)入步驟 3 ,如無數(shù)據(jù)進(jìn)入步驟 2 。
- 訪問舊庫(kù),找到相應(yīng)的數(shù)據(jù) Insert 到新庫(kù),再進(jìn)入步驟 3 。
- 進(jìn)行業(yè)務(wù)邏輯的操作……
隨著時(shí)間的推移,數(shù)據(jù)慢慢會(huì)從舊庫(kù)同步到新庫(kù)。剩下還未同步的數(shù)據(jù)使用同步工具做一次全量同步。
原則上,以上的每一個(gè)方案在實(shí)際切流量之前都要對(duì)數(shù)據(jù)做核對(duì),確保兩邊的數(shù)據(jù)一致。
最后遷移完后可以通過流量回放工具(如阿里的 jvm-sandbox-repeater )或者全鏈路壓測(cè)工具來驗(yàn)證新版本的系統(tǒng)是否能運(yùn)行地符合預(yù)期。
好了,總結(jié)一下。
這篇呢Z哥和你分享了應(yīng)對(duì)老項(xiàng)目的思路,總的有5種:
- 重構(gòu)
- 重新部署
- 更換平臺(tái)
- 重寫
- 絞殺
我建議根據(jù)業(yè)務(wù)敏捷、運(yùn)營(yíng)效率、客戶洞見、系統(tǒng)本身的韌性和彈性來考慮選擇哪一種。
接下來專門對(duì)重寫展開細(xì)說了一下,建議遵守三個(gè)原則:
- 系統(tǒng)具備演進(jìn)的能力。
- 憑借度量指標(biāo)來把握演進(jìn)的方向。
- 小步快速迭代。
最后分享了3個(gè)常用的新老數(shù)據(jù)遷移方案。
- 雙寫
- 異步雙寫+對(duì)賬
- 直接用新庫(kù),數(shù)據(jù)惰性遷移