為維護(hù)而設(shè)計(jì):架構(gòu)設(shè)計(jì)的首要原則
軟件開發(fā)總成本 = 開發(fā)成本 + 維護(hù)成本;軟件維護(hù)成本 = 理解成本 + 修改成本 + 測(cè)試成本 + 部署成本。—— Ken Beck
在設(shè)計(jì)框架、系統(tǒng)架構(gòu)時(shí),可擴(kuò)展性是人們想追求的特征之一。從技術(shù)社區(qū)的文章上,我們可以看到大量的相關(guān)字典,諸如于“通過配置和定義進(jìn)行可擴(kuò)展”,又或者是“業(yè)務(wù)流程”的可擴(kuò)展,還有各類的“插件 ”以及“可擴(kuò)展的點(diǎn)” 等等的話術(shù)。
真是呢,從我們所經(jīng)歷的大部分項(xiàng)目來說,這些系統(tǒng)的可擴(kuò)展性并沒有真的那么好。有些,可能是在設(shè)計(jì)系統(tǒng)時(shí),它可以滿足于當(dāng)前的需要,不適合未來的場(chǎng)景 —— 這是另外一個(gè)故事了。有此,則是架構(gòu)師在設(shè)計(jì)系統(tǒng)的時(shí)候,缺乏對(duì)于邊界的限定的考慮。對(duì)于邊界限定的另外一個(gè)解釋就是,系統(tǒng)實(shí)現(xiàn)了開發(fā)時(shí)的可擴(kuò)展性,但是忽視了維度時(shí)期帶來的問題。而軟件的開發(fā)周期中,維護(hù)成本往往占據(jù)了成本的主要部分,如開頭 Ken Beck 所說。
這時(shí)就不禁讓人又開始思考起來,沒有為維護(hù)設(shè)計(jì)的系統(tǒng),真的是可擴(kuò)展的嗎?
架構(gòu)真的是可擴(kuò)展?
在諸多系統(tǒng)里,開發(fā)時(shí)正如它的設(shè)計(jì)那樣,具備著良好的可擴(kuò)展性。只是在運(yùn)行和維護(hù)時(shí),它的可擴(kuò)展性可能會(huì)失去效應(yīng)。
一個(gè) DDD 系統(tǒng)腐化的傳聞
這是一個(gè)未嚴(yán)格經(jīng)驗(yàn)證的 DDD 傳聞。
在《領(lǐng)域驅(qū)動(dòng)設(shè)計(jì):軟件核心復(fù)雜性應(yīng)對(duì)之道》一書中,Eric Evans 根據(jù)在項(xiàng)目上的重構(gòu)經(jīng)驗(yàn),給出了領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的系統(tǒng)化方法,并融合了一系列的領(lǐng)域特定相關(guān)的實(shí)踐。我們相信 Eric 在重新構(gòu)筑這個(gè)系統(tǒng)的架構(gòu)時(shí),經(jīng)過了一系列的良好設(shè)計(jì)。而到了十幾年后,這個(gè)系統(tǒng)的架構(gòu)已經(jīng)變成大泥球般,難以看出當(dāng)初的精心設(shè)計(jì)。人員的流動(dòng),知識(shí)傳承的流失,使得系統(tǒng)一步步走向腐化 —— 這幾乎是大部分系統(tǒng)的共同問題。
OSGi 模塊化的回歸測(cè)試
OSGi 是一個(gè)頗為有趣的模塊化、插件化方案,Eclipse 是最具備知名度的一應(yīng)用場(chǎng)景。在可擴(kuò)展性上,OSGi 有非常多的優(yōu)點(diǎn):諸如于動(dòng)態(tài)加載、更新和卸載模塊而不用停止服務(wù),可以實(shí)現(xiàn)系統(tǒng)的模塊化、版本化。
在微服務(wù)架構(gòu)流行之前,它的插件化能力對(duì)于大型系統(tǒng)來說非常有吸引力。發(fā)布部分新功能時(shí),我們不需要發(fā)布整個(gè)系統(tǒng),只需要發(fā)布其中的一個(gè) bundle 包(插件)。
只是基于 OSGi 構(gòu)建的 Web 應(yīng)用會(huì)變成一個(gè)可怕的單體,每個(gè) bundle 可能會(huì)被構(gòu)建成“微服務(wù)”,bundle 之間存在相互調(diào)用 —— 在微內(nèi)核架構(gòu)里,我們不允許這樣的存在。如此一來,一旦我們更新 bundle 時(shí),會(huì)傾向于發(fā)布整個(gè)系統(tǒng),而不是單個(gè)的 bundle 包。
在這時(shí),我們依舊需要對(duì)整個(gè)系統(tǒng)進(jìn)行回歸測(cè)試。
低代碼生成的遺留代碼
這是的“遺留代碼“ 是指難以測(cè)試的低代碼平臺(tái)。這里的低代碼平臺(tái)是指通用的低代碼平臺(tái)。
在那篇無代碼編程 ,DSL 被視為核心要素,一個(gè)經(jīng)過精心設(shè)計(jì)的領(lǐng)域特定語言/類編程語言(非 JSON DSL)。基于 DSL 設(shè)計(jì),能為系統(tǒng)提供良好的可測(cè)試性和持續(xù)集成能力,這一點(diǎn)是普通 JSON 所不具備的。而一旦,我們生成的低代碼是不可測(cè)試的,那么它就可能變成遺留代碼 —— 它取決于平臺(tái)所構(gòu)建的自動(dòng)化測(cè)試機(jī)制,以及自動(dòng)化版本遷移的設(shè)計(jì)。
“復(fù)雜度同力一樣不會(huì)消失,也不會(huì)憑空產(chǎn)生,它總是從一個(gè)物體轉(zhuǎn)移到另一個(gè)物體或一種形式轉(zhuǎn)為另一種形式。”
如果框架本身不考慮可測(cè)試和質(zhì)量的問題,那么總有人得重新去考慮它們。
靈活性的另外一面
過去,PL/SQL 是我見過最靈活的系統(tǒng),“人們可以不寫代碼,就搞定一切”。我們可以將其看作一種 DSL,它和我們遇見過的配置化系統(tǒng)是類似的,只需要簡(jiǎn)單地 “配置”,就可以快速地實(shí)現(xiàn)。
這種靈活的“配置”,非常有意思。我們可以在測(cè)試環(huán)境里,通過人工的方式,反復(fù)地、有預(yù)見性地對(duì)測(cè)試它們。在我們追求自動(dòng)化和穩(wěn)定性的今天,這種不穩(wěn)定性變得異常的可怕。在移動(dòng)端,消息推送是通過配置化和接口的形式進(jìn)行的,我們經(jīng)??梢越邮盏綔y(cè)試人員向生產(chǎn)環(huán)境發(fā)送消息推送。
從意義來看,這種靈活性更多地應(yīng)該被視為補(bǔ)救措施,而非系統(tǒng)設(shè)計(jì)的核心部分。
反思
軟件開發(fā)是一項(xiàng)團(tuán)隊(duì)活動(dòng)。
無節(jié)制的甜頭蔓延
越是在大型系統(tǒng)中,在破窗效應(yīng)愈加的明顯 —— 一旦有一個(gè)人沒有按照規(guī)范來實(shí)施,那么將會(huì)有越來越多的人違反了規(guī)范。這個(gè)問題起源于,相關(guān)的規(guī)范沒有通過流程或者工具有固化。諸如于,沒有嚴(yán)格的代碼檢視流程,缺乏自動(dòng)化的架構(gòu)守護(hù)工具。
諸如于為了單一團(tuán)隊(duì)的原因,臨時(shí)性修改了底層庫的接口,開了一道的口子。導(dǎo)致了后續(xù)其它團(tuán)隊(duì),會(huì)要求新的口子,導(dǎo)致底層的庫偏離了原先的設(shè)計(jì)。因此,在添加新的臨時(shí)性接口之前,考慮一下系統(tǒng)遷移和演進(jìn)時(shí)會(huì)遇到的問題。
我們?cè)试S這種臨時(shí)性地方案存在(緊急地上線總是會(huì)存在的),更應(yīng)當(dāng)在發(fā)生之后,重新設(shè)計(jì)和填補(bǔ)相關(guān)的問題。
自動(dòng)化保障的缺乏
在不考慮調(diào)試的情況下,對(duì)于配置化等具備靈活性的系統(tǒng),它們實(shí)現(xiàn)功能的難以自動(dòng)化測(cè)試,也無法進(jìn)行持續(xù)集成與持續(xù)部署。對(duì)于中大型的軟件系統(tǒng)來說,它將會(huì)成為維護(hù)(開發(fā) + 運(yùn)維)工作的一個(gè)惡夢(mèng)。
特別是,一旦配置化的系統(tǒng)缺乏版本化管理機(jī)制,將會(huì)對(duì)軟件的回滾造成新的挑戰(zhàn)。
軟件開發(fā)所需求的一系列因素,都應(yīng)該在可擴(kuò)展 + 靈活性的情況下,再次進(jìn)行相關(guān)的分析。
本地測(cè)試環(huán)境的缺失
在遇到一些復(fù)雜的 bug,不論是 Serverless ,還是聲明式、配置化的方式,都需要與其它開發(fā)系統(tǒng)進(jìn)行聯(lián)調(diào)。我們需要構(gòu)建一個(gè)本地的環(huán)境,以快速復(fù)現(xiàn)出 bug,才能進(jìn)行修復(fù)?;诂F(xiàn)有的平臺(tái)場(chǎng)景之下,需要在云端進(jìn)行調(diào)度與測(cè)試。
不過,這種混合高度的模式在《云端開發(fā)時(shí)》流行之后,會(huì)有所改觀。
面向維護(hù)構(gòu)建有序
在最近編寫的《 Architecture 3.0》時(shí),我和我的同事 @NoaLand 一直在討論架構(gòu)的有序性,以期待構(gòu)建有序性模型解決部分維護(hù)問題。在現(xiàn)今的這個(gè)場(chǎng)景之下,除了解決上述的問題,可能還需要如下的幾個(gè)方面。
正視問題的復(fù)雜性
如你所見,問題本身是復(fù)雜的,只有正視它,能會(huì)真正帶來突破。
周期性的傳達(dá)設(shè)計(jì)思想
如我們所知,軟件開發(fā)團(tuán)隊(duì)成員的流動(dòng)性會(huì)影響系統(tǒng)的穩(wěn)定性。每隔幾年內(nèi),團(tuán)隊(duì)的成員便會(huì)刷新一遍 —— 諸如于在互聯(lián)網(wǎng)行業(yè),三年便是個(gè)老員工。系統(tǒng)的相關(guān)知識(shí)會(huì)隨著人員的流動(dòng),而被遺忘在某個(gè)角落里,新進(jìn)來的團(tuán)隊(duì)成員不懂得系統(tǒng)原先的設(shè)計(jì)。
在我們嘗試過的諸多方法中,一種頗為有效的方式是:在新員工進(jìn)來一段時(shí)間后(如三個(gè)月),讓他們講述一下系統(tǒng)的架構(gòu)。過程中,糾正他們對(duì)于系統(tǒng)的一些認(rèn)知偏差。
適時(shí)解決技術(shù)債務(wù)
在實(shí)施的過程,需要持續(xù)地為技術(shù)債務(wù)騰出時(shí)間 —— 一個(gè)老生常談的問題。在一系列的解決方式里,持續(xù)更新依賴是一個(gè)非常簡(jiǎn)單而有效的策略,與詳細(xì)可以見《管理依賴的 11 個(gè)策略》。
當(dāng)然,還有其它的技術(shù)債務(wù)了。
其它
To be continue...