單個團隊內部的持續集成已經是成熟的實踐. 跨團隊的集成則碰到了很多問題, 包括全部測試運行時間過長,
合并成本高等問題. 針對這些問題有一些對應的解決方案, 如合理的分支策略, 分層的集成等.
這里想討論一下幾個基本的矛盾, 和理想中的解決方案
1. 并行開發 與 集成 之間的矛盾
這是本質問題, 如果所有功能都是由單一開發者循序漸進的完成, 則集成并不是大問題.
由于團隊內部的集成已經有大量成熟的實踐, 因此前面的假設可以修改為"如果所有功能都是由同一團隊循序漸進的完成, 則集成并不是大問題".
這就為我們指出了一條思路: 如果把需要集成的部分, 都分配給一個團隊去完成, 則會大大降低集成的難度
傳統的大規模開發中, 往往按照模塊劃分開發團隊, 比如做UI的, 做網絡通信的, 做數據庫訪問的,
做協議的, 等等, 分別是不同的團隊. 而最終為了完成某個用戶可見的功能, 需要協調多個團隊并行開發, 然后聯調, 也就是集成. 也就是說, 是特性需要集成.
如果我們按照特性來劃分團隊, 則跨團隊的集成將轉化為團隊內部的集成. 而團隊內部的集成已經有大量成熟的實踐
關于特性團隊和模塊團隊的全面比較, 請參見<<Choose
Feature Teams over Component Teams for Agility>>
降低集成成本的第一原則就是避免集成. 減少跨團隊的集成需求, 如果不能避免的話.
按照特性劃分團隊將帶來一個明顯的問題, 就是不同的團隊的人可能會同時修改同一個文件, 同一個類,
同一個函數等, 因為不同特性都會涉及同樣的功能模塊, 如數據庫/網絡/UI等. 這就是我們想討論的第二個問題
2. 基于文本的合并 與 基于語意的邏輯 之間的矛盾
現代的版本控制系統都能夠智能的自動合并文本. 然而合并后的結果是不是我們所期待的,
則只能通過測試來驗證. 并且對文本級別的沖突, 版本控制工具也無能為力, 還需要開發人員人工干預. 因此降低合并成本第一原則, 就是避免合并.
這里有幾種可能的方式來達到這一目標:
-
基于插件的架構: 這是開閉原則的應用. 增加任何新的功能,
都不需要修改原來的文件或類, 而是增加文件或類. 一個大尺度的例子就是Eclipse生態系統.
如果把Eclipse的各個插件都看作某個完整系統的所需要的特性的話, 那么這些特性的開發者有的甚至從未謀面,
開發過程中也不需要跟其它插件進行代碼級別的集成, 然而最后它們卻能和諧的一起工作
-
基于小文件/小類/小函數的代碼組織: 這是單一職責原則的應用,
"小"只是代碼外在的表現, 真正的含義是職責單一. 以C語言為例, 考慮兩種極端的情況. 一個極端是把整個系統都寫在main函數里,
另一個極端是每個獨立的功能都寫成獨立的函數, 并且每個文件只包含一個函數. 前一種情況下, 任何團隊的任何改動, 都需要跟其它團隊的改動做合并,
并通過全面測試驗證合并后的行為. 后一種情況下, 合并的需求大大降低, 只有大家改同一個函數的時候才需要, 并且測試也可以以影響范圍為邊界進行測試.
當然我們不需要像后一種情況這樣極端, 但至少指明了前進的方向.
這類方案也帶來一個問題, 就是我們無法一開始就設計出如此良好的插件體系, 只能是一個演進的架構.
這期間, 插件和框架之間的接口變化將為集成帶來挑戰, 即舊客戶與新服務如何和平相處. 這就是我們要討論的第三個問題
3. 依賴的穩定性 與 依賴自身的演進升級 之間的矛盾
這是一個普遍問題, 尤其對分層的體系結構, 或者"平臺 + 產品"模式的開發項目.
在理想情況下, 所謂的理想情況是指, 代碼集體所有制/IDE完善的重構功能/全面的自動化測試用例/等,
公共API的變化所引起的客戶代碼的修改, 都可以由某個團隊一次性的完成并提交. 然而在大量的大規模遺留項目中這是不可能的. 這種情況下,
"第三方代碼線"(參見<<Software
Configuration Management Patterns>>)或者"變更控制修改組"(負責實現變更, 并提交)是可選的解決方案
這里想討論一下另外一個大尺度上相對更通用的但會引入管理成本并有點風險的解決方案, 就是向后兼容,
或者Versioning. 幾個例子:
-
我們知道COM技術就是為了解決DLL版本地獄的問題. COM組件的新版本與老客戶能和平相處,
是因為COM組件的升級, 并不是直接修改老接口, 而是增加新接口, 保留老接口, 并提供能力查詢接口, 這樣新老客戶都能各取所需.
這是"擴展對象"模式的一種應用. 這是API級別的向后兼容/Versioning
-
Subversion. Subversion的客戶端可以和服務器協商版本,
從而選擇一種大家都理解的協議. 這是協議級別的向后兼容/Versioning
-
XML. XML本身就是為擴展設計的, 可用于實現向后兼容的消息/協議等.
但實際的項目中, 我們并不希望老客戶和老API/老協議長期存在, 而是希望把老API/老協議刪掉,
所有老客戶全部使用新的API和協議. 因此我們需要階段性的應用上述方案, 并提供管理或者技術手段, 在過渡時期結束的時候, 確保新老交替已全部完成.
這里的風險就是, 混亂無力的管理一旦允許老接口存在一段時期, 它就會一直存在
以上三個問題在大規模遺留系統中很難得到徹底解決, 因此這幾種解決方案或思路,
僅僅希望在開發新系統時能夠提供一些考慮的因素