官术网_书友最值得收藏!

2.4 成長優化期:技術債務與演進

經歷一兩次上線后,項目進入一個穩定上線、交付的階段。筆者將其稱為成長優化期,這是一個技術提升開發體驗,技術帶來更多業務價值的階段。不過,實際上這已經是一個穩定的時期——我們可以抽時間來解決各種各樣的問題。

在設計架構和完善業務的過程中,會暴露出團隊的一系列問題:架構不完善、開發流程不便利等。短期內,這些問題并不會影響我們的開發。然而就長期而言,這些問題還是有可能會影響開發進度。先前我們在追趕業務時也遺留了一些技術問題,尤其是代碼的質量問題,這些問題會隨著時間的推移和業務代碼的堆砌變得越來越嚴峻。當然,如果一個項目的時間短,那么它就不會遇到這些挑戰。不論怎樣,程序員作為一個“匠人”,總得有點“追求”,要不斷地提高自己的水平。

2.4.1 償還技術債務

在技術準備期,我們在構建技術基礎方面花費了大量時間;在業務回補期,我們在支持業務的開發方面花費了大量時間。在這兩個時期,我們都或多或少地采取了一些妥協方案,為的是能加快速度開發流程。這些問題都將在未來成為我們的開發負擔。這種方式就和債務一樣,可以在短期內得到好處,但是在未來必須償還它們。這部分開發負擔,我們稱其為技術債。

技術債包含的內容有如下幾個方面:

(1)代碼質量。常見的問題有接口、函數的重復實現,即a成員在自己的功能內,實現過這個功能,但是b成員又實現一份,沒有提取到公共的方法中。實現方式或模式不統一,比如我們采用某個框架來解決問題,但是在真實場景下,可能又會采取過去的實踐方式,諸如使用Lambda、RxJava、Rx.js、Ramda等框架來進行函數式編程。少部分代碼未按規范進行實踐,這個問題更加常見,不僅存在于沒有代碼檢視(Code Review)的項目,還存在于擁有代碼檢視的項目。未檢視過的代碼,往往容易被遺漏。

(2)測試覆蓋率。在面對技術不熟悉、業務又過急的情況時,UI自動化測試、單位測試往往是最先被拋棄的一環。一方面,自動化測試的目的在于,保持功能不被破壞;另一方面,大部分的項目都擁有專業的測試人員進行測試。值得商榷的是,國內的互聯網公司都不會有測試這種東西,所以它們就存在這種長期的債務了。

(3)依賴問題。依賴對于短期項目來說不是問題,對于長期項目來說,依賴沒有及時更新是一個很嚴重的問題。例如,我們使用Redux 3.0的版本,當4.0版本發布的時候,按照語義化版本的規則,它可能修改了大量的代碼,而我們不得不追隨這個變化——除非,我們決定在未來重寫該應用,否則大量依賴過舊的問題,會導致我們難以對代碼進行重構,因而不得不重寫應用。

有些問題不是一天兩天造成的,比如測試覆蓋率低的問題,要解決這些問題也不是兩三天就能完成的。這往往需要制定一個長期的計劃,才能將它們一個一個地修正過來。多數情況下,我們并沒有足夠的時間在短期內修復,相關的技術債都是項目在不斷演進的過程中,一步步提升的。比如測試覆蓋率,它依賴所有的人編寫測試,并不斷限定測試覆蓋率的下限。這樣一來,在經歷了幾個迭代之后,我們便會擁有不錯的測試覆蓋率。

想要改善代碼質量也不是一件容易的事,改進代碼質量要依賴開發人員的水平,以及團隊的能力。如果團隊中的代碼質量不忍直視,充滿了各種Code Smell(代碼的壞味道),那么可以通過代碼檢視的方式來不斷提高團隊成員的水平。然后,有針對性地進行相應的培訓。

值得注意的是,與日常的業務代碼編寫相比,改進過去的代碼會帶來更多的成長和技術挑戰——我們更容易從錯誤的代碼中學習,而不是從成功的經驗中學習。舉個例子,我們直接看別人寫的與設計模式相關的代碼,并不會直接學到相關的內容,如果從過去寫的代碼重構看到設計模式,就能更深刻地理解相應的技術實踐。

2.4.2 優化開發體驗

提升開發體驗,也是在穩定時期值得考慮的另外一個因素。在我們的日常工作中,有很多是手動完成的,我們可以通過自動化來減少重復性工作。

有這樣一些例子:我們想創建一些測試數據,需要在數據庫中手動創建,但是缺少對應的批量創建腳本或命令;在調試時經常需要手動輸入相關的賬號,這也可以通過插件來自動化登錄;在開發移動應用時,一旦提交了代碼,就應該有相應的工具來自動化構建應用,并在構建成功后上傳到某個包管理中心,同時安裝到對應的測試機。相似的,還有其他各種方式的自動化流程,它們所做的便是減少重復的工作,以不斷提升開發體驗。

在這個過程中,我們可能寫了大量的接近重復的代碼。而這些代碼表面看上去并不是重復的,但是從抽象層來看是重復的。為了應對這種問題,我們可以進行代碼重構,也可以通過諸如創建領域特定語言的方式來進一步抽象出內部DSL的代碼。這樣,我們就可以減少花費在業務代碼上的時間。

此外,還可以思考怎樣將一些代碼的編寫實現自動化,例如在UI層通過采用Sketch2Code來生成模板頁面,或者編寫相應的拖曳生成UI界面的工具。一旦我們優化了這些開發流程——尤其在相應的自動化功能完成之后,開發人員就開始面對一些新的挑戰。

2.4.3 帶來技術挑戰

堆砌業務代碼對大部分技術人員來說是一件難熬的事情——每天都在重復工作,總想尋找一些挑戰。而對于周期長的應用來說,這種事情更為可怕。不論怎樣的項目,技術人員都需要獲得一定的能力增長。如果不能滿足這種訴求,那么就不能進步,也就相當于是一種能力方面的退步。因為技術在不斷地進步,新的技術總會很快地淘汰舊的技術。一個長期項目一旦結束,新的技術體系就可能與舊的技術體系完全不一樣了。

面對這種情況,一種常見的做法是引入新的技術棧。它既可以是一個框架,又可以是一門語言。我們可以引入Rxjs進行響應式(Reactive)開發,或者引入Rambda.js進行函數式編程。也可以采用新的語言,如在開發Android應用時,除了使用C++,還可以使用Kotlin語言來完成部分功能;在開發iOS應用時,可以結合Objective-C來使用Swift語言進行開發。

對于前端應用而言,我們還可以嘗試使用新的前端框架。目前維護大型的前端應用用的是Angula框架,如果要開發一些新的簡易的應用,那么可以嘗試使用React或者Vue框架來實現。

此外,對于稍有余力的項目團隊來說,可以嘗試進行一些小型的模擬項目。在這些小型的模擬項目上,我們關注于使用新的技術來開發現有的應用。一方面,可以為以后的架構演進做準備;另一方面,讓我們的技術與主流接軌。在筆者經歷過的項目中,曾經有一個項目,每隔幾個月開展一次Hackday活動,活動內容是使用新技術來重寫舊的應用。此外,我們還會做一些Workshop來練習使用項目潛在的新技術棧。

當然,對于項目管理者來說,堆砌業務代碼會輕松很多,畢竟出現新技術風險的可能性較低。但是在保證進度的情況下,也需要適當地帶來一些成長的機會。

2.4.4 架構完善及演進

經過大量的業務沉淀,我們也會發現架構中存在的一些問題。這些問題大都是一些架構設計上的偏差,經過調整,有的糾正到原來的設計上,有的使用新的架構設計。如果使用新的設計,我們還需要將那些使用過去設計的部分進行相應的修改,這意味著會有一定的返工工作量。

我們還可能遇到由于業務變更導致架構需要修改的情況,這時不僅需要調整,還需要進行全新的設計,才能適應新的業務需求。比如,如果在前端應用中包含大量的第三方應用,就要考慮插件化的方案,設計一個新的插件化架構。再舉一個例子,如果我們最開始開發應用的時候使用Cordova的WebView容器,那么出于安全考慮,會遷移到自己開發的混合應用框架上。這兩個例子都只是在架構設計上發生一些改變,但是從代碼的角度來看,所需要的修改并不會太多。

當然,這也并不是說我們預先設計的架構有問題,因為最初的架構滿足的是創建架構時設計的業務場景。當業務發生一些變化時,架構也需要做相應的調整。我們不可能讓過去的架構一直適應新的業務變化,在變化發生的時候演進系統的架構才是一種正常的形態。

此外,在更低級別的代碼層里,我們會發現代碼中存在一些復雜、混亂的相互調用,如果不加以規范并重構代碼,那么會使得新增的代碼加劇這個問題的產生。比如在項目中后期參與開發的人員編寫的代碼,可能與我們最初的架構風格不一致。如果原先的架構風格更符合需求,那么還需要幫助這些后來的開發人員解決相關的問題。

主站蜘蛛池模板: 成安县| 金山区| 时尚| 萨嘎县| 金秀| 深圳市| 澎湖县| 金沙县| 通化县| 东乌珠穆沁旗| 文登市| 北票市| 淄博市| 敦化市| 绥中县| 贵州省| 米林县| 兴文县| 阿勒泰市| 新乡县| 嘉义县| 石家庄市| 岳普湖县| 福贡县| 疏附县| 田林县| 包头市| 白玉县| 怀宁县| 柞水县| 蒙城县| 双城市| 龙门县| 山阳县| 罗平县| 嫩江县| 遂平县| 清远市| 星子县| 自治县| 方城县|