爲維護而設計:架構設計的首要原則

軟件開發總成本 = 開發成本 + 維護成本;軟件維護成本 = 理解成本 + 修改成本 + 測試成本 + 部署成本。—— Ken Beck

在設計框架、系統架構時,可擴展性是人們想追求的特徵之一。從技術社區的文章上,我們可以看到大量的相關字典,諸如於 “通過配置和定義進行可擴展”,又或者是“業務流程” 的可擴展,還有各類的 “插件 ” 以及“可擴展的點” 等等的話術。

真是呢,從我們所經歷的大部分項目來說,這些系統的可擴展性並沒有真的那麼好。有些,可能是在設計系統時,它可以滿足於當前的需要,不適合未來的場景 —— 這是另外一個故事了。有此,則是架構師在設計系統的時候,缺乏對於邊界的限定的考慮。對於邊界限定的另外一個解釋就是,系統實現了開發時的可擴展性,但是忽視了維度時期帶來的問題。而軟件的開發週期中,維護成本往往佔據了成本的主要部分,如開頭 Ken Beck 所說。

這時就不禁讓人又開始思考起來,沒有爲維護設計的系統,真的是可擴展的嗎?

架構真的是可擴展?

一個 DDD 系統腐化的傳聞

這是一個未嚴格經驗證的 DDD 傳聞。

在《領域驅動設計:軟件核心複雜性應對之道》一書中,Eric Evans 根據在項目上的重構經驗,給出了領域驅動設計的系統化方法,並融合了一系列的領域特定相關的實踐。我們相信 Eric 在重新構築這個系統的架構時,經過了一系列的良好設計。而到了十幾年後,這個系統的架構已經變成大泥球般,難以看出當初的精心設計。人員的流動,知識傳承的流失,使得系統一步步走向腐化 —— 這幾乎是大部分系統的共同問題。

OSGi 模塊化的迴歸測試

OSGi 是一個頗爲有趣的模塊化、插件化方案,Eclipse 是最具備知名度的一應用場景。在可擴展性上,OSGi 有非常多的優點:諸如於動態加載、更新和卸載模塊而不用停止服務,可以實現系統的模塊化、版本化。

在微服務架構流行之前,它的插件化能力對於大型系統來說非常有吸引力。發佈部分新功能時,我們不需要發佈整個系統,只需要發佈其中的一個 bundle 包(插件)。

只是基於 OSGi 構建的 Web 應用會變成一個可怕的單體,每個 bundle 可能會被構建成 “微服務”,bundle 之間存在相互調用 —— 在微內核架構裏,我們不允許這樣的存在。如此一來,一旦我們更新 bundle 時,會傾向於發佈整個系統,而不是單個的 bundle 包。

在這時,我們依舊需要對整個系統進行迴歸測試。

低代碼生成的遺留代碼

這是的 “遺留代碼 “ 是指難以測試的低代碼平臺。這裏的低代碼平臺是指通用的低代碼平臺。

在那篇無代碼編程 ,DSL 被視爲核心要素,一個經過精心設計的領域特定語言 / 類編程語言(非 JSON DSL)。基於 DSL 設計,能爲系統提供良好的可測試性和持續集成能力,這一點是普通 JSON 所不具備的。而一旦,我們生成的低代碼是不可測試的,那麼它就可能變成遺留代碼 —— 它取決於平臺所構建的自動化測試機制,以及自動化版本遷移的設計。

“複雜度同力一樣不會消失,也不會憑空產生,它總是從一個物體轉移到另一個物體或一種形式轉爲另一種形式。”

如果框架本身不考慮可測試和質量的問題,那麼總有人得重新去考慮它們。

靈活性的另外一面

過去,PL/SQL 是我見過最靈活的系統,“人們可以不寫代碼,就搞定一切”。我們可以將其看作一種 DSL,它和我們遇見過的配置化系統是類似的,只需要簡單地 “配置”,就可以快速地實現。

這種靈活的 “配置”,非常有意思。我們可以在測試環境裏,通過人工的方式,反覆地、有預見性地對測試它們。在我們追求自動化和穩定性的今天,這種不穩定性變得異常的可怕。在移動端,消息推送是通過配置化和接口的形式進行的,我們經常可以接收到測試人員向生產環境發送消息推送。

從意義來看,這種靈活性更多地應該被視爲補救措施,而非系統設計的核心部分。

反思

軟件開發是一項團隊活動。

無節制的甜頭蔓延

越是在大型系統中,在破窗效應愈加的明顯 —— 一旦有一個人沒有按照規範來實施,那麼將會有越來越多的人違反了規範。這個問題起源於,相關的規範沒有通過流程或者工具有固化。諸如於,沒有嚴格的代碼檢視流程,缺乏自動化的架構守護工具。

諸如於爲了單一團隊的原因,臨時性修改了底層庫的接口,開了一道的口子。導致了後續其它團隊,會要求新的口子,導致底層的庫偏離了原先的設計。因此,在添加新的臨時性接口之前,考慮一下系統遷移和演進時會遇到的問題。

我們允許這種臨時性地方案存在(緊急地上線總是會存在的),更應當在發生之後,重新設計和填補相關的問題。

自動化保障的缺乏

在不考慮調試的情況下,對於配置化等具備靈活性的系統,它們實現功能的難以自動化測試,也無法進行持續集成與持續部署。對於中大型的軟件系統來說,它將會成爲維護(開發 + 運維)工作的一個惡夢。

特別是,一旦配置化的系統缺乏版本化管理機制,將會對軟件的回滾造成新的挑戰。

軟件開發所需求的一系列因素,都應該在可擴展 + 靈活性的情況下,再次進行相關的分析。

本地測試環境的缺失

在遇到一些複雜的 bug,不論是 Serverless ,還是聲明式、配置化的方式,都需要與其它開發系統進行聯調。我們需要構建一個本地的環境,以快速復現出 bug,才能進行修復。基於現有的平臺場景之下,需要在雲端進行調度與測試。

不過,這種混合高度的模式在《雲端開發時》流行之後,會有所改觀。

面向維護構建有序

在最近編寫的《 Architecture 3.0》時,我和我的同事 @NoaLand 一直在討論架構的有序性,以期待構建有序性模型解決部分維護問題。在現今的這個場景之下,除了解決上述的問題,可能還需要如下的幾個方面。

正視問題的複雜性

如你所見,問題本身是複雜的,只有正視它,能會真正帶來突破。

週期性的傳達設計思想

如我們所知,軟件開發團隊成員的流動性會影響系統的穩定性。每隔幾年內,團隊的成員便會刷新一遍 —— 諸如於在互聯網行業,三年便是個老員工。系統的相關知識會隨着人員的流動,而被遺忘在某個角落裏,新進來的團隊成員不懂得系統原先的設計。

在我們嘗試過的諸多方法中,一種頗爲有效的方式是:在新員工進來一段時間後(如三個月),讓他們講述一下系統的架構。過程中,糾正他們對於系統的一些認知偏差。

適時解決技術債務

在實施的過程,需要持續地爲技術債務騰出時間 —— 一個老生常談的問題。在一系列的解決方式裏,持續更新依賴是一個非常簡單而有效的策略,與詳細可以見《管理依賴的 11 個策略》。

當然,還有其它的技術債務了。

其它

To be continue...

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/p7NCLEF9cV2mTaQcsbox7A