實戰(zhàn)篇:一個核心系統(tǒng)3萬多行代碼的重構(gòu)之旅
經(jīng)典著作《重構(gòu)》這本書中有這么一段話:
01 先聊聊這個系統(tǒng)的歷史包袱
我們的廣告引擎在這次重構(gòu)前大概經(jīng)歷了1年半時間的迭代,初期針對的是搜索場景,業(yè)務(wù)單一,流程清晰。
1、業(yè)務(wù)場景開始變得復(fù)雜,除了搜索廣告,還需要支持信息流推薦以及相似推薦場景。
2、廣告流量開始快速增加,除了滿足功能性需求,還需要兼顧好性能。
經(jīng)過梳理,整個引擎有大部分邏輯是可以公用的,因此我們定義了一個主體框架,同時將可擴(kuò)展部分進(jìn)行了抽象。這樣,各個場景能夠根據(jù)自身業(yè)務(wù)的特殊性實現(xiàn)某些公共接口即可。另外,從性能角度考慮,我們犧牲了一些代碼可讀性,把某些邏輯并行化了。
隨著業(yè)務(wù)的發(fā)展,搜索場景開始進(jìn)入快速迭代期,新增策略越來越多,我們的主體框架也是在這個時候逐漸變得不靈活。
1、為了兼容搜索的特殊邏輯,我們需要在其他場景中增加各種 if 判斷來繞過這些邏輯。
2、廣告策略越來越多,累計了幾十個,當(dāng)框架失去清晰的結(jié)構(gòu)后,有些策略的實現(xiàn)開始變得定制化,缺少層次化的劃分和可插拔式的抽象設(shè)計。

轉(zhuǎn)機(jī)出現(xiàn)在 2019 年年底,由于廣告業(yè)務(wù)的特殊性,流量開始自然走低,另外產(chǎn)品運營團(tuán)隊將重心放在了第 2 年的工作規(guī)劃上,因此給了我們非常好的窗口期開始此次重構(gòu)。
我們將工期定成了 1 個月,最終僅比預(yù)期晚上線了一天,雖然出現(xiàn)了兩個線上問題,但是在灰度期都及時發(fā)現(xiàn)和修復(fù)了,并沒有造成線上事故。
02 重構(gòu)前,我們做了哪些準(zhǔn)備工作?
這次重構(gòu)的代碼量很大,3 萬多行,而且是廣告系統(tǒng)最核心的引擎部分。啟動前,我們能預(yù)期到下面這些困難:
1、業(yè)務(wù)側(cè)的阻力:廣告是極其以業(yè)務(wù)為導(dǎo)向的,本次重構(gòu)雖然能帶來長期研發(fā)效率的提升,但是沒法直接提升業(yè)務(wù)收益,而且開發(fā)周期不會太短,如何才能得到業(yè)務(wù)同學(xué)的支持?
▍讓所有人看到痛點
前面提到:隨著業(yè)務(wù)迭代,我們廣告引擎的主體框架已經(jīng)變得模糊不清,另外幾十個廣告策略散落在不同的業(yè)務(wù)場景中,配置凌亂。
針對這兩個痛點,我們提前1個月啟動了現(xiàn)有業(yè)務(wù)的梳理,走讀舊代碼、同時翻閱以前的需求文檔,最終我們將不同場景的核心流程以及廣告策略歸類成了一張清晰的表格。
正是這一張表格,讓技術(shù)和產(chǎn)品第一次很清晰地看到了我們引擎部分的全貌,體會到了業(yè)務(wù)的復(fù)雜度以及當(dāng)前技術(shù)上的瓶頸。
▍明確重構(gòu)的目標(biāo)和價值
讓所有人感受到痛點后,我們規(guī)劃了本次重構(gòu)的兩個核心目標(biāo):
1、主體框架的重構(gòu):將主流程模塊化,重新定義上下層協(xié)議,確保接口清晰;各層級內(nèi)部也需要做好抽象,具備良好的擴(kuò)展性。
2、策略靈活可配置:將廣告策略按照業(yè)務(wù)意圖進(jìn)行歸類抽象,策略的執(zhí)行條件動態(tài)可配置,同時策略可任意插拔。
此外,我們將這兩個核心目標(biāo)完成后可帶來的預(yù)期收益進(jìn)行了細(xì)化:
1、技術(shù)收益:代碼結(jié)構(gòu)更清晰,更容易理解和維護(hù);可擴(kuò)展性增強(qiáng),引擎的開發(fā)效率將進(jìn)一步提升。
2、業(yè)務(wù)收益:策略能做到更細(xì)粒度的配置和擴(kuò)展,對業(yè)務(wù)支持更友好;研發(fā)提效后能進(jìn)一步加快業(yè)務(wù)的迭代速度。
▍整體節(jié)奏的把控
整體節(jié)奏的把控也是非常重要的一環(huán),能讓所有人對這件事情有一個時間上的預(yù)期。
首先,我們將工期定成了 1 個月,一方面考慮了業(yè)務(wù)側(cè)可以接受的最大周期,技術(shù)上也希望速戰(zhàn)速決;另一方面,春節(jié)即將來臨,我們必須趕在公司封網(wǎng)前上線,同時預(yù)留出1-2周的 buffer 以防意外情況發(fā)生。
03 執(zhí)行過程中有哪些可分享的經(jīng)驗?
1. 高質(zhì)量的技術(shù)設(shè)計方案
這一點得益于日常的要求,針對開發(fā)周期超過3天的項目我們都會進(jìn)行技術(shù)方案設(shè)計,本次重構(gòu)當(dāng)然也不例外。
框架部分的整體架構(gòu)、模塊之間的協(xié)議設(shè)計、以及策略的可擴(kuò)展性設(shè)計是本次技術(shù)方案的重點,團(tuán)隊前后討論了不下3次。
在大方案定稿后,團(tuán)隊進(jìn)一步對數(shù)據(jù)庫、接口字段、緩存結(jié)構(gòu)、日志埋點等公共部分進(jìn)行了細(xì)化,因為涉及到多人協(xié)作開發(fā),團(tuán)隊約定以文檔作為溝通界面,文檔始終保持和代碼同步。
2. 預(yù)重構(gòu)出框架性代碼
這一個 PR 非常關(guān)鍵,是我們從技術(shù)方案落地到代碼最重要的一步。我們把重構(gòu)后的包結(jié)構(gòu)、模塊劃分、各層之間的API定義、不同廣告策略的抽象進(jìn)行了梳理,先忽略實現(xiàn)的細(xì)節(jié)。
這樣主體代碼基本成型,能很清楚地描繪出我們理想中的框架。然后,我們組織了多次集中代碼審查,最終形成了統(tǒng)一意見。
這一步能很好地避免過早陷入實現(xiàn)細(xì)節(jié),導(dǎo)致主體框架關(guān)注不夠、代碼不穩(wěn)固,后期再返工反而會拖累效率。
3. 頻繁溝通和成對代碼 Review 機(jī)制
進(jìn)入到細(xì)節(jié)實現(xiàn)階段后,很重要的一點是:對現(xiàn)有邏輯的理解。引擎代碼經(jīng)過一年半的迭代,歷史上被很多人開發(fā)過,但是本次只有 3 個同學(xué)參與重構(gòu)。
整個過程中,我們遇到任何代碼邏輯不明確的地方,都是反復(fù)溝通和求證,不主觀猜想,這一份謹(jǐn)慎其實很關(guān)鍵。
另外在代碼審查上,我們按模塊分配了對這塊業(yè)務(wù)比較熟悉的同學(xué)來負(fù)責(zé),成對搭配,機(jī)制靈活。
4. 有效的測試方案
重構(gòu)未動,測試先行。這個原則是《重構(gòu)》一書中重點強(qiáng)調(diào)的,也是我們本次技術(shù)方案討論的重點,我這里單獨拎出來詳細(xì)展開下。
首先,我們前期便約定好:不動任何老代碼,完全建新的 package 進(jìn)行重構(gòu)。這樣方便比對重構(gòu)前后的結(jié)果,同時進(jìn)行線上灰度實驗。
測試方案上,以下 4 點值得借鑒:
1、端到端測試:本次重構(gòu)不涉及功能性的調(diào)整,因此外層API的行為是不會有任何變化的,這樣端到端的測試方法最為有效,這個是研發(fā)和QA測試最主要的手段。
2、冒煙測試:QA同學(xué)提供冒煙 Case,由研發(fā)同學(xué)進(jìn)行冒煙,研發(fā)提測前必須保證所有冒煙 Case 執(zhí)行通過。這一點在大部分互聯(lián)網(wǎng)公司都不常見,但是對于大型項目絕對有效。
3、沙箱環(huán)境雙流程驗證:前面提到我們重構(gòu)前后的代碼都保留了,因此可以通過腳本抓取線上環(huán)境的入?yún)⒆鳛閏ase,然后用自動化的方式對 API 的返回字段進(jìn)行逐一比對。
4、線上環(huán)境灰度實驗:灰度對于重構(gòu)非常重要,我們利用已有的ABTest平臺,逐步放開灰度流量,從5%、到10%、到30%、最后到100%,制定了很謹(jǐn)慎的放量節(jié)奏,然后通過日志以及業(yè)務(wù)指標(biāo)監(jiān)控進(jìn)行驗證。
寫在最后
回顧整個重構(gòu)的過程,總結(jié)成下面 7 個關(guān)鍵點:
7、小心求證,為每行代碼負(fù)責(zé)
當(dāng)然,最關(guān)鍵的因素還是人,大型項目重構(gòu)極其考驗團(tuán)隊的協(xié)作能力,如果每個人都很靠譜,重構(gòu)就已經(jīng)成功了一半。
特別推薦一個分享架構(gòu)+算法的優(yōu)質(zhì)內(nèi)容,還沒關(guān)注的小伙伴,可以長按關(guān)注一下:
長按訂閱更多精彩▼
如有收獲,點個在看,誠摯感謝
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!