關於程式碼版本管理的思考和建議

文鵬發表於2020-07-22

      眾所周知,良好的系統研發應該有延續性和一致性,所以很多公司非常注意程式碼版本控制,並逐漸慢慢的迭代自己的產品。

  具體到自然資源行業來說,我司純粹的產品銷售較少,專案開發較多,不同的地區,不同的客戶對於同一個功能的理解可以千差萬別。即使是產品銷售也同樣面對相對強勢的無可避免的增加一些個性化功能。

  長期以來,公司事業部研發期望統一程式碼版本的願望與具體各個專案之間個性化需求導致的程式碼變更(資料庫結構變更)之間矛盾越演愈烈。最終形成了底層框架儘量不改,新增功能通過擴充套件來改,業務相關沉澱到前端或資料庫函式(檢視,儲存過程)去改的微妙平衡。

  但是這個平衡是很脆弱的,有較大的風險。

  1. 首先,前端頁面,指令碼,資料庫結構沒有通過程式碼版本管理器進行管理,很容易出現頁面,指令碼,資料庫表結構衝突的問題。一旦衝突雖然影響範圍侷限在專案當地,但是對公司的產品質量口碑影響較大。
  2. 擴充套件的新功能,絕大多數控制權在具體的研發手中,很難把控程式碼質量,進行程式碼稽核,員工因各種原因離職後,也不利於公司對程式碼的回收管理。
  3. 雖然採取了主框架,前端,擴充套件的分離,但是主框架更新時總部研發依然要小心翼翼,儘量避免任何與通用功能無關的邏輯寫入,而事實證明,除非緊鎖程式碼許可權,只讓極少數人修改相關程式碼,否則這一點基本不能做到。

  如上所述,對於程式碼版本控制的討論在我們公司已經舉行了很多次,絕大多數討論都是從管理層面出發,聚焦於程式碼許可權控制,程式碼分支控制等等。今天我想從技術角度談一談程式碼版本控制。

  從技術角度怎麼做程式碼版本控制?如何解決通用版本與各個地區相同功能不同表現形式之間的程式碼衝突?其實C#的asp.net框架和Java的spring框架已經告訴了我們答案:基於介面開發。

  就拿日誌來說,無論是asp.net還是spring都為我們提供了官方預設的日誌類,但是如果這些日誌類無法滿足我們的需求怎麼辦?我們完全可以基於官方介面自己實現一個特殊的,基於個性化業務需求的日誌類,然後把這個獨一無二的日誌類通過依賴注入註冊到框架中,替換和接管原來的通用日誌類。核心框架內部因為全部是基於介面實現的操作,所以什麼都不需要修改。而實現這一點最關鍵的一步就是核心框架中,不同的元件之間,不同的類之間通過LoggerFactory生成的ILogger去執行日誌記錄,而具體到底使用官方的通用日誌類實現ILogger還是第三方的特殊日誌框架如Log4都無所謂。
通過這種方式,asp.net和spring做到了核心框架和業務程式碼的剝離以及各自的獨立發展。簡略的架構描述如下:

  回到我們的自然資源以及自身的產品(至少我目前瞭解到的政務,不動產登記以及權籍系統),為什麼感覺程式碼版本控制很難,本質上是將通用業務模組與框架緊緊的耦合在一起了。簡略的架構描述如下:

  這種架構的優點是:框架即產品,開箱即用。它的缺點也非常明顯:只要是和具體業務相關的改動都很不靈活(包括業務程式碼,甚至包括部分工作流程式碼),並且地區擴充套件中的程式碼只能擴充套件全新的方法,一旦設計到舊方法的改造,還是隻能回來修改框架,而為了減少對框架的汙染只能依賴關鍵核心員工修改,效率較低,響應也不快。
  對於改造工作量,我認為無需擔心,要想使用這種模式來控制版本控制,也不是一定要全面的重構程式碼,大動干戈的改造,我們完全可以一個模組一個模組的逐步的進行改造。
以不動產農房改造的戶籍資訊為例:
  最初的農房批量生成附記因為邏輯相對複雜,沒有按照管理在前端或資料庫完成,而是寫在了核心程式碼庫BDC2的HjxxDomainService領域類中。

  但是批量附記屬於業務性非常強的內容,每一個地區的不動產可能都有自己的要求,如衡陽提出要求在家庭成員下新增預編不動產單元號,房屋來源等等內容。這些內容的資料庫欄位甚至是衡陽特有的,根本無法在通用版本里面直接新增 。
  遇到這種情況,要求衡陽自己通過擴充套件另外重寫一套批量生成附記進而導致有兩套極其類似的大段程式碼,要麼通過核心程式設計師在通用版本中增加非常麻煩的版本判斷分支語句。不論採用哪一種方式都非常不利於程式碼的版本控制和維護。
  如果我們採用先前描述的框架對應介面,附帶一個通用實現,不同地區有自己的對應實現,那麼我們就可以簡單的通過增加類檔案的形式完成衡陽修改。甚至在個性化類還在利用大部分通用類方法時可以引入一個通用方法代替實現。粗略架構圖如下:

  具體到農房批量生成附記,UML類圖改造如下:

  核心程式碼改造點如下:

  1. 將外部控制器要操作的核心領域類從通用的HjxxDomainService,修改為抽象的AbstractHjxxDomainService。

  2. HengyangHjxxDoMainService通過通用的CommonHjxxDomainService實現無需特殊化處理的方法,減少重複程式碼。

  3. CommonHjxxDomainService(原HjxxDomainService)繼承AbstractHjxxDomainService和IplGenerateFj(視情況複雜度可以不要),其餘不做修改。

  4. 在AbstractHjxxDomainService中增加一個具體實現工廠(後期可以優化成依賴注入的形式)

  5. 外部控制器中涉及到農房批量註記時將前端傳入的方法名傳入工廠,呼叫生成對應業務類並執行動作

  通過以上5步改造,我們就不動產的農房業務的戶籍資訊領域模組單獨拆分了一套通用版本和一套衡陽版本,其中衡陽版本除了批量生成附記,其他模組本質上還是用的通用版本方法,只不過已經具備了繼續擴充套件獨立的可能性。改動量小,安全,且切實可行的能夠解決版本控制問題。因為既然能夠拆分成單獨的檔案,那麼一定可以繼續拆分成單獨的動態庫,然後在工廠方法中通過反射的方式動態尋找,載入和繫結。
  遇到全新的方法時,我們也可以從容的選擇多種方式,如果是在現有工程中修改,則修改抽象類(介面),然後首先實現通用版本,對其他地區版本直接呼叫通用版本方法。當然這樣對程式碼破壞性比較大,我們完全可以不要改動現有類庫中的抽象類和介面,通過擴充套件的方式在原類上增加新的方法和介面(以C#語言為例)。

相關文章