作者:來自 vivo 網際網路伺服器團隊- Zhang Mengtao
在專案研發過程中,由於時間、能力等因素往往會出現設計方案沒有做到最好或最優、編碼質量不夠好等問題,技術債的出現是不可避免的,並且隨著時間的推移,技術債對系統的影響會越來越大,同時使得對程式碼和架構設計的更改越來越困難,想要進一步提升效能必須要對技術債進行管理,本文透過在活動中臺系統的技術債實踐經驗,介紹技術債的含義、分類和管理。
一、技術債的含義
1.1 技術債的含義
關於技術債的概念可以追溯到1992年,沃德·坎寧安(Ward Cunningham)首次提出,第一次釋出程式碼,就好比借了一筆錢。只要透過不斷重寫來償還債務,小額負債可以加速開發。但久未償還債務會引發危險。複用馬馬虎虎的程式碼,類似於負債的利息。整個部門有可能因為鬆散的實現,不完全的物件導向的設計或其他諸如此類的負債而陷入窘境[1]。
《維基百科》中提出,技術負債(Technical debt),又稱技術債,也稱為設計負債(design debt)、程式碼負債(code debt),是程式設計及軟體工程中的一個比喻。指開發人員為了加速軟體開發,在應該採用最佳方案時進行了妥協,改用了短期內能加速軟體開發的方案,從而在未來給自己帶來的額外開發負擔。這種技術上的選擇,就像一筆債務一樣,雖然眼前看起來可以得到好處,但必須在未來償還。軟體工程師必須付出額外的時間和精力持續修復之前的妥協所造成的問題及副作用[3]。
如下圖所示,技術債在研發人員的日常工作付出中佔據了一定的比例。
1.2 技術債的危害
我們可以從效率、質量、體驗三個方面來看:
1.2.1 效率
這是最直接的影響,當技術債不斷增加,軟體系統會變得非常脆弱。這種脆弱主要是由不良的架構設計或程式碼設計導致,不管最初是選擇了劃分良好的微服務架構,還是單體架構,技術債不斷打破設計原則,讓原則不復存在,以至於很難理清系統元件之間的關係和職責。當修改其中的一部分元件時,其他元件也會牽連,可能會陷入惡性迴圈。
1.2.2 質量
以上圖為例,從研發持續交付角度來分析的話,在專案版本迭代前期,業務功能較少的情況下,高質量要求的專案可能會比低質量要求的專案迭代速度慢一點,但是隨著專案逐漸發展,高質量要求的專案對比低質量要求的專案迭代速度顯著提升,技術債的持續累積是導致質量下降的關鍵原因,技術債是無法避免的,因此技術債的有效管理和消除是我們保障高質量軟體的必不可少的方式之一。
1.2.3 體驗
軟體產品需要不斷演進才能在長時間後依然還能適應市場,才能具有較強的生命力。相反,有的軟體在經過幾年的開發之後,隨著技術債的增加,已經變的很難維護,很多時候只能被推倒重寫,主要是不斷疊加的技術債導致。技術債的疊加不斷增加系統的複雜性,從而開發的成本逐漸增高,每次迭代都需要解決設計不足或技術所帶來的問題。
二、技術債是怎麼產生的?
技術債的出現是不可避免的,但是不同的場景下會產生不同的技術債,帶來的影響也是不一樣的,如果按照健康角度分類的話,對於研發人員允許出現的技術債可以劃分為健康的一類,對於研發人員儘量避免的技術債則劃分為不健康的一類。我們可以從以下四個維度進行分析。
2.1 衝動/有意 - “沒有做更好的設計”
研發團隊雖然識別到這樣做會導致技術債的積累,但是不清楚帶來的後果,沒有去做更好的設計方案。比如專案線上上執行時,突然出現了一個線上問題,如果不盡快修復上線,就會造成很大的損失,這種情況下,已經無法再針對問題作詳盡的設計方案,現在需要以最快的方式修復上線,這種情況下往往不會考慮更好的設計方案,而是以最快捷的方式解決問題。對於當下的臨時方案在未來會帶來什麼技術債,研發人員並沒有關注。
2.2 謹慎/有意 - “必須儘快交付”
當研發團隊面臨業務壓力時,例如在釋出新產品時需要快速上線以佔領市場時,快速解決問題的重要性常常超越了更好的實踐。在這種情況下,團隊往往會選擇快速完成產品交付,然後再處理技術債務。團隊清楚這樣做會帶來技術債務,也知道逾期還債的具體後果,以至已經安排好了未來的改進計劃。這種場景很常見,是已知技術債的一種主要來源。
2.3 衝動/無意 - “不知道怎樣設計更好”
這個維度技術債務產生的原因通常是由於人員技能的不足。在實際研發中,不可能保證所有人的水平都是一樣的,由於缺乏相關技能,研發人員可能不清楚如何編寫更優秀和精煉的程式碼,不知道如何設計更好的架構或者給出更佳的解決方案,這種情況下,研發人員按照自己的理解和設計方案進行工作,可能會帶來一部分技術債。不管怎樣的團隊,人員的更替都是避免不了的,可能對專案不夠熟悉,對某一塊功能不夠熟悉,短時間內快速理解並給出設計方案,可能會有很大難度,個人的經驗不同,認知不同,在實現相同的功能時選擇的方案也是不同的。
2.4 謹慎/無意 - “現在有更好的方案”
隨著團隊成員的能力提升或者行業技術上的演進,對於之前認為的最佳方案現在看來並不是最好的解決方案。但在當時,可能並不知道有更好的做法。這種技術債確實也是無法避免的,甚至會經常遇到,最簡單的是基於當下的經驗甚至業界最優的一些實踐選擇技術方案或者技術框架。可能在之前做這塊功能的時候,團隊成員已經對當時的方案達成了一致,認為是最優的方案,但是現在突然發現有更優的方案,那麼為了專案的長遠發展、穩定迭代,同樣需要對專案作最優方案的替換。
以上將技術債務分為四類。我們通常認為,健康的技術債是右邊的兩個維度,不健康的技術債是左邊的兩個維度。基於此我們可以分析技術債產生的原因並制定相應的改進措施。
-
對於衝動/有意型別的技術債,我們可以在日常的研發過程中擬製嚴格的規範,加強流程化的研發管理,讓我們的研發人員對最優設計達成習慣和規範;
-
對於謹慎/有意型別的技術債,我們可以和產品達成一致,每次面臨這種緊急需求,或者需要快速上線而沒有采取最優方案的情況,可以直接記錄,後續儘快最佳化;
-
對於衝動/無意型別的技術債,我們可以對所有的專案成員進行能力和認知的提升,儘可能讓研發人員在熟悉功能和專案的情況下進行研發,另外可以增加設計方案評審流程和程式碼提交評審流程,有效減少這種型別的技術債;
-
對於謹慎/無意型別的技術債,我們需要在識別到的時候第一時間記錄下技術債,並且根據研發排期合理的安排時間進行修復。
為了保證產品持續的競爭力,上面幾點只是方法,如果沒有成本上的投入,只能淪為空談。從整個產品團隊,都要提升對技術的正確理解,技術的構建並不是一勞永逸的,是需要不斷的成本投入來維護的。
三、技術債管理實踐
3.1 一個技術債真實案例
下面以一個專案中遇到的真實技術債案例來介紹,活動中臺系統是一個面向使用者的中臺專案,可能每天都會產生大量的活動資料,即使進行了大量的分表處理,但是動輒千萬級的資料仍然給資料庫操作帶來了一定的負擔,所以我們目前沉澱了一套通用的資料庫資料清理方案,根據不同型別的表配置不同的清理策略,基本參考維度是資料的產生時間和活動狀態,如果一個已經結束的活動且資料產生時間大於半年則直接刪除。這種方案雖然通用,但是線上上發現了嚴重的慢SQL問題,即使是透過離線庫操作,仍然會讓系統存在一定的風險,顯然這個問題需要關注。
3.2 原來的做法
在原本的做法中,研發團隊對於專案產生的技術債採取的方案是隨機修復,也就是發現後會簡單的記錄下,如果是緊急問題則會同步專案組儘快上線,如果是非緊急問題,則會在下次版本迭代涉及該模組時進行修復,或者在版本gap期間隨機進行修復,缺乏系統性的管理,往往可能會導致問題的遺漏,並且對於技術債的修復缺乏系統的分析和判斷,雖然在有意識的修復技術債,但是效益容易被忽略,往往看不到真正的價值。
對於這個資料清理帶來的慢SQL問題,雖然會產生慢SQL,但是對線上業務影響較小,所以優先順序不高,暫時擱置,待到項版本有空閒人力再去最佳化,這個問題由於是技術側單純的技術最佳化,所以沒有納入需求列表,單純的依賴開發人員人工記憶,等到人力空閒時再想是否有問題需要最佳化,這個時候才想到塵封已久的問題。方案的變動需要測試的介入來回歸功能,這個時候開發人員想要最佳化,可能測試人力緊張,沒能及時開發和測試,功能的上線又會擱置。
3.3 新的做法
按照現有方案,我們已經形成了一套穩定的技術債機制,相應的按照以下步驟進行處理:
3.3.1 識別技術債
想要管理技術債,首先就是要識別技術債,技術的持續改進離不開團隊中每個人的努力,因此需要每個成員都積極參與。通常我們在識別技術債的時候可以從以下幾個類別去篩查。
當我們發現這個資料清理問題時,可以直接記錄在技術債跟蹤列表中,記錄技術債的所屬專案、問題描述、建立人、處理人、建立時間、修復時間、技術債狀態、規劃版本、備註等屬性,便於技術債的跟蹤和審視。這種跟蹤表只是一種方式,我們還有各種看板、空間可以用來記錄,專案內達成一致即可。
3.3.2 分析技術債
記錄技術債之後,時常會遇到的問題是,需要改進的地方太多,尤其是對於遺留系統。怎麼辦?分析優先順序。我們可以基於價值/成本矩陣來評估改進任務的價值和成本。基於下圖的價值-成本矩陣,我們會:
-
優先解決高價值+低成本的技術債;
-
嘗試將高價值+高成本的技術債拆分為高價值+低成本的技術債,逐步解決;
-
在沒有高價值+高/低成本的技術債時,再來考慮低價值+低成本的技術債;
-
最後如果只剩下低價值+高成本的技術債,還是先拆分,再解決,或可考慮直接移除。
3.3.3 解決技術債
分析完技術債的價值、成本、優先順序、方案,我們可以在版本的gap期間跟隨版本修復技術債,如果某部分功能剛好規劃在版本中,那我們技術債的修復剛好由測試一起迴歸,這樣可以做到工作量最小化,如果是高優的技術債,我們就需要儘早安排修復,緊急線上問題更是需要迅速迭代小版本上線。當然,修復完一定要及時更新技術債的狀態。對於資料清理這條技術債,我們分析後得出結論:對於系統穩定方面有影響,需要一次性徹底最佳化,價值較高,優先順序較高但是無需緊急修復上線,所以在下一個版本人力空閒期間就可以伺機修復上線。
這裡要注意的是,對於同一條技術債,記錄的人、修復的人可以不是同一人,這裡需要技術債記錄詳細,便於方案的執行。資料清理這條技術債我們在隨後的一段時間內就整改上線,完成了這條技術債的修復。
3.3.4 階段審視
在實行技術債機制的管理過程中,建議進行階段審視,查漏補缺。我們已經詳細記錄了技術債的所屬專案、問題描述、建立人、處理人、建立時間、修復時間、技術債狀態、規劃版本、備註等屬性,便於技術債的跟蹤和審視。可以從多方面審視,一個是統計資料,如下表,可以看出每個階段的修復數量、新增數量、上線總數、正在修復的數量和待開始的數量。這也是衡量專案質量的影響指標之一。
另外一種是趨勢圖,我們可以從下圖這樣的折線圖明顯看出不同狀態的技術債的趨勢,包括不同階段的技術債新增數量、修復數量、修復中數量和技術債總數趨勢變化。
除此之外我們可以觀察團隊成員在修復技術債方面的工作量體現,比如統計一年不同季度、不同團隊成員每個季度對於技術債的修復數量,都是一些階段審視的方式。
3.4. 技術債管理機制
3.4.1 明確管理機制和責任分配
團隊在針對技術債的治理過程中一定要確定主要責任人,雖然解決團隊技術債問題是所有團隊成員的責任,但是為了管理流程化、合理化、最最佳化,往往需要指定一個負責人專門跟蹤技術債的管理。除此之外,技術債的管理機制要在團隊內部達成高度一致,整個團隊對於技術債問題的認知、修復、管理都是經過正式裁決的。
3.4.2 主動預防原則
通常來說,開發人員能直觀感受到技術債的壞處,大都願意去償還技術債,所以技術債累積的主要原因是,沒有認識到技術債累積給業務發展帶來的巨大壞處。這也就意味著,解決技術債的第一步就是,要意識到償還技術債的重要性,從而願意投入資源去解決。對主動引入的技術債,要儘量讓管理層和產品團隊瞭解技術上的捷徑將會帶來的長期危害,儘量減少技術債的引入。
3.4.3 高價值優先原則
需要遵循高價值優先的原則來優先修復技術債,上面我們已經分析過技術債問題的價值/成本矩陣,對於分析結果,我們在版本中解決技術債問題的時候就要嚴格按照高價值優先的原則去修復,而不是根據技術債發現的時間,或者是成本低的技術債優先修復,切忌隨機修復技術債。
四、經驗總結
4.1 技術債是不是越少越好?
當然不是!提到技術債,我們想到的往往是它的壞處,比如難以維護、難以增加新功能等,但實際上它也有好處。關於技術債的好處,我們可以對應著金融領域的經濟債務來理解。我們都知道,經濟債務最明顯的好處在於,可以幫助我們完成很多本來不可能完成的任務,比如貸款買房。相應的,技術債可以在短期內幫我們快速完成業務開發,滿足使用者需求,就類似房貸的作用。當研發團隊面臨業務壓力時,例如在釋出新產品時需要快速上線以佔領市場時,快速解決問題的重要性常常超越了更好的實踐。在這種情況下,團隊往往會選擇快速完成產品交付,然後再處理技術債務。團隊清楚這樣做會帶來技術債務,也知道逾期還債的具體後果,只要有計劃的進行最佳化即可。
4.2 持續管理技術債帶來的益處有哪些?
-
提升系統穩定性:不斷規範化的解決更多的技術債有利於系統更加穩定;
-
形成穩定機制:活動中臺系統經過長時間的實踐,逐漸形成穩定的技術債管理機制,面對專案中的技術債不再頭疼如何跟蹤;
-
提升專案質量:隨著技術債機制的實行,專案成員在迭代時就會更多的考慮技術債方面的問題,久而久之專案質量也會有所提升。
4.3 技術債是否可以作為專案管理的重要指標之一?
技術債已經成為很多專案管理的一個重要指標,用來衡量專案整體的研發效能,雖然本文沒有過度涉及技術債在研發效能工具方面的體現,但是在這個過程中工具很重要。
參考文件:
[1] Ward Cunningham,《WyCash 投資組合管理系統》,ACM SIGPLAN OOPS Messenger4, no.2 (1992)
[2] https://www.productplan.com/glossary/technical-debt/
[3] https://zh.wikipedia.org/wiki/%E6%8A%80%E6%9C%AF%E8%B4%9F%E5%80%BA#cite_note-oopsla92-1
[4] Philippe Kruchten, Rod Nord, and Ipek Ozkaya, 《管理技術債務:減少軟體開發中的摩擦》(Addison-Wesley, 2019)
[5] https://time.geekbang.org/column/article/142210