首頁>技術>

今天準備單獨寫一篇文章來談軟體系統的複雜性問題,特別是最近幾年對於低程式碼開發平臺,DevOps持續整合和交付,Serverless無伺服器化,各種高階程式語言,包括AI人工智慧興起後,總會給人一個找到銀彈的錯覺。

所以在這裡首先再次重申個人觀點,即:

面對複雜的軟體系統,沒有銀彈,只有焦油坑。

如果你還是學生或剛畢業參加工作,實際上這本書裡面很多內容你無法真正搞明白,我在2002年第一次看這本書就是這個感覺,而在當了幾年架構和專案經理後再重讀該書,才將很多內容真正讀明白。

書裡面明確提出了:

There is no single development, in either technology or management technique, which by itself promises even one order-of-magnitude improvement within a decade in productivity, in reliability, in simplicity。(沒有任何技術或管理上的進展,能夠獨立地許諾十年內使生產率、可靠性或簡潔性獲得數量級上的進步。)

簡單來說就是你不要期望任何技術和管理能力的提升,將軟體研發研發生產率得到指數級的提升,即使到今天這句話仍然適用。

首要任務和次要任務

在書裡面提到軟體活動的首要任務和次要任務的關鍵概念,即:

所有軟體活動包括根本任務——打造由抽象軟體實體構成的複雜概念結構,次要任務 ——使用程式語言表達這些抽象實體,在空間和時間限制內將它們對映成機器語言。

軟體生產率在近年內取得的巨大進步來自對後天障礙的突破,例如硬體的限制、笨拙的程式語言、 機器時間的缺乏等等。這些障礙使次要任務實施起來異常艱難,相對必要任務而言,軟體工程師在次要任務上花費了多少時間和精力?除非它佔了所有工作的 9/10,否則即使全部次要任務的時間縮減到零,也不會給生產率帶來數量級上的提高。

對於首要任務,核心根本問題書裡面談到:

我認為軟體開發中困難的部分是規格化、設計和測試這些概念上的結構,而不是對概念進行表達和對實現逼真程度進行驗證。

如果你認可這點,那麼軟體開發始終就是困難的,沒有銀彈。同時書裡面對軟體系統的內在特性,複雜度,一致性,可變性和不可見性詳細展開了闡述,在這裡不再給出解釋。

下面還是談下個人對軟體系統複雜性的一些關鍵理解。

從問題域到解決方案設計和對映

那麼如何來理解上面這段話?

任何一個軟體產品從研發到交付實際上包括了兩個方面的重要工作。

1.從需求和問題域提出最終的架構設計和解決方案

2.基於架構方案進行編碼實現和上線交付

我們可以把第一點理解為軟體活動中的首要任務,即完成現實世界中問題域到解決方案域的抽象,而這個抽象本身又可以分為兩個層面。

1.業務建模-將現實世界的需求抽象為需求模型或業務模型

2.技術建模-將業務模型抽象為可以進行開發實現的技術模型

在兩次抽象中,業務模型的第一次抽象又至關重要,只有業務建模抽象合理後,技術架構實現的抽象才能夠匹配。我們經常會遇到一個典型場景,即軟體產品上線後發現對業務需求變更的靈活響應能力很弱,動不動就需要調整程式碼重新部署,而不是能夠簡單透過業務配置方式來解決。這就是典型的業務建模出現問題。

如果你只是實現一個你們公司的定製化報銷系統,你會發現業務建模和架構設計都很簡單,即問題域到解決方案域對映往往是1對1對映。但是如果你是做一個產品化的產品,類似ERP這種套裝軟體,你會發現業務建模異常複雜。

其核心原因就是面對不同業務場景下的多樣化需求,你需要抽象出業務共性,找尋最核心的業務物件和領域模型。簡單來說就是一個複雜的業務流程,完成需要多個業務物件之間相互互動和整合,才能夠完成。你需要搞清楚有哪些業務物件,然後搞清楚各業務物件間如何協作。

業務建模和技術建模能否分離?

基於前面的闡述,可以看到業務建模和技術建模實際是完成現實世界問題域到抽象軟體實現的關鍵抽象。

在早期可以看到這兩件事都是由類似系統工程師,系統分析員的角色來完成。而現在兩者逐步分離,由獨立的業務建模或需求分析人員,也由獨立的架構設計師。

但是這種分離卻出現了新的問題,業務建模人員往往沒有足夠的技術儲備來完成這種建模和抽象,而技術人員往往技術驅動只做技術架構設計而忽略了業務。

在人月神話裡面一直在強調,需要有一個類似外科手術醫生這種角色來完成架構設計,以保證高度的概念完整性。但是這種建模設計工作被拆分掉了,導致後續整個軟體系統設計模型本身在擴充套件性,可靠性和和需求滿足方面出現問題。

軟體活動為何不能形成流水線工廠

一直以來,在軟體領域有大量的人投入大量的精力在研究將軟體研發活動自動化,或者說將軟體研發活動變成流水線工廠。

但是截止到今天,沒有任何人,方法或工具取得突破。

任何一個軟體系統可以看到,面對現實世界的業務需求和抽象,形成了軟體實體和操作介面等,但是這些內容基本沒有任何可重複性和可複製性。如果一個工作可重複,那麼就一定可以自動化。但是實際可以看到對現實世界的抽象這個動作很難重複,也很難完全自動化。

軟體開發的核心要素是人,人本身是感性的動物而非機器,人和人之間還需要溝通和協同,這些又進一步加劇了整個軟體活動的複雜性。

如果我們將軟體活動進行分解,可以理解為:

現實需求-》業務建模-》技術建模-》實現交付-》批次交付

那麼對於類似批次交付光碟這種動作才能夠對應到軟體流水線工廠,這是完全的機器能夠完成的事情。截止到今天,大量的類似MDA,類似快速開發平臺和低程式碼開發平臺,實際在做實現交付的工廠化。

但是前面談到的業務建模和技術建模這個首要任務,仍然難以批次複製。

產品研發的模組化說起

如果一個產品生產工廠要實現流水線和自動化,那麼這個產品本身用到的各種元器件,材料要足夠的標準和模組化。如果你自己想去開一個工廠,那麼需要的就不是元器件這個層級的標準化,而是需要半成品這個層級的模組化,這樣你才可能快速組裝。

華強北可以說是山寨機的鼻祖,為何在2005年左右時間野蠻生長並發展迅猛,這裡面不得不說的就是聯發科的貢獻。

2005年,聯發正式推出了"交鑰匙方案",幫助完成晶片、軟體平臺和設計,手機制造商只需購買螢幕、攝像頭、外殼、鍵盤和其他簡單部件就可以生產手機。因此,這種更方便的包裝方案,受到了山寨手機制造商的熱烈追捧,山寨手機市場蓬勃發展。

也就是說你只要投入一個幾百萬,你完全可以自己搞一個工廠生產山寨手機。

也就是說山寨機的生產變成了簡單的產品模組的組裝,你唯一需要考慮的就是產品外形或工業設計等方面的一下簡單內容。

各個產品模組可複用性極強,而且聯發科由於出貨量巨大,產品模組本身也足夠穩定。

由於手機本身不是屬於一個太複雜的產品或系統,同時由於聯發科的貢獻,徹底實現了手機產品製作的躍遷。也就是實現了產品研發和生產製造的分離。

手機生產廠商你可以不用具備研發能力,完全只是組裝即可。

那麼從手機到汽車生產製造,同樣出現了類似的躍遷,這個躍遷簡單來說就是新能源汽車帶來的革命。即你不再需要複雜的發動機,變速箱和傳動系統,所有的都是電力驅動足夠簡單,汽車的生產同樣也可以像類似手機生產一樣元件化和模組化。

類似現在你可以看到的,大量的非傳統車企開始造車,開始接入這個行業,包括恆大汽車,包括最近釋出的小米也準備進入到汽車製造行業。

簡單總結就是一個完整的流水線工廠往往具備兩個條件。其一是研發和製造分離,研發僅僅由少量技術驅動型企業來完成;其二就是產品足夠的模組化,可以靈活的組態。

軟體活動是否具備這兩個特徵?

再回頭來看軟體活動,一個完整的軟體研發實際上涉及到軟體開發框架和軟體技術平臺,其次是平臺上面的軟體功能模組實現。

對應技術平臺由於和業務無關,因此類似資料庫,中介軟體,包括開發框架環境等可以做到足夠的通用化可複用,你不再需要透過類似組合語言來開發應用,這是一個很大的進步;但是平臺上面的業務功能實現層,卻很難做到足夠的通用和模組化。

也就是說這些業務模組的開發往往很難複用,和業務場景和需求強相關。

你很難將軟體研發中的研發活動和軟體生產製造活動分離開。比如對於CRM領域,存在一個類似聯發科的CRM各個元件模組的生產廠家,然後又衍生了大量的基於這些元件模組來開發垂直行業的CRM應用的廠商。

那麼我是否可以在IT系統建設到一定程度後,透過已有IT系統可複用的API介面能力來快速的組裝,編排新的應用呢?

這個就是我們常說的SOA架構思想裡面的關鍵概念,基於服務組裝編排來實現新的業務和流程。這個看起來挺不錯,但是仍然逃不脫前面談到的軟體活動的首要任務。

即你按照SOA參考架構和BPM的做法,你首先是要將業務流程進行BPM流程建模,比如透過BPMN流程建模語言對業務進行抽象,這個抽象本身又足夠複雜,這個首要任務無法透過系統自動來完成。

在過年前我和一個做大中臺規劃的廠家交流,他們提出一個宏大的產品規劃構想。即將企業需要的所有IT能力全部識別和實現為一個個獨立的原子服務,構建覆蓋企業所有業務的大中臺能力服務層。以後業務人員自己就可以上去透過拖拽原子服務的方式完成新業務,新流程的配置,原子服務足夠可複用,應用擴充套件也不再需要開發人員。

這是不是有點找到銀彈的感覺?

現在你可能看到的就是大量沒做過大型軟體研發,軟體專案建設實施的人在忽悠客戶實施上面的產品規劃和架構。拿著一些高大上的概念把客戶忽悠得團團轉。

當你考慮前面的軟體複雜性首要任務的時候,你就很容易看到原子服務本身的可複用度服務保障,新業務的抽象建模無法落地等關鍵問題。所以註定這些思想都是空中樓閣。

再談低程式碼開發平臺

對於軟體活動可以理解為:

現實需求-》業務建模-》技術建模-》實現交付-》批次交付

當前很多低程式碼平臺本身也是同樣思路,即你只需要進行建模和配置,然後軟體功能自動實現,自動部署和交付完成。

這裡面的關鍵點在哪裡呢?

即業務建模和技術建模本身的複雜度問題。

如果僅僅是一個簡單的請假單,掛接一個人工審批流程,這種場景你當然很容易完成業務和技術建模,表單配置,並自動化實現和交付。但是如果你面對的是一個複雜業務的實現,裡面涉及到多個軟體實體物件,包括實體物件之間的複雜關係和協同。

在這種場景下先不說能否做到完全自動化的實現,僅僅是業務和技術建模就不是一個般人能夠完成的,那麼低程式碼開發平臺本身也無存在的意義。

同時,對於要給大型複雜系統的實現,除了最基本的業務功能實現,還包括了軟體系統的可靠性,效能,可擴充套件性等諸多非功能性需求。一個OA系統你可以看到,市場上5萬塊錢你也可以買一套,但是很多集團企業的OA系統建設往往上千萬。

差別就在於大型集團的系統對高併發,高效能的要求導致整個技術建模和架構複雜性的增加,你可能會引入類似分散式,訊息中介軟體,快取,全文檢索等諸多的新技術,這些都間接進一步增加了軟體系統的複雜度。

軟體系統的複雜性的應對

如果說大型軟體系統的複雜性應對,只說一點的話就是分而治之。

我們拿機房佈線來做簡單舉例,如下是一個混亂的機房佈線圖:

當你面對如此場景的時候一定考慮的是分而治之,首先進行分類和大區域的劃分,比如引入相關的歸類裝置進行簡單的歸類整合。

即使這樣你還會發現一個歸類裡面還有很多的網線,為了進一步進行區分,你還需要進行抽象或者標籤化,如下:

斯坦福大學計算機系教授John Ousterhout在《軟體設計哲學》一書專門談到一點,降低複雜性的基本方法就是把複雜性隔離。"如果能把複雜性隔離在一個模組裡,不與其他模組互動,就達到了消除複雜性的目的。"

這實際和我前面講的事物認知,理解和分析事物的思路完全一樣。即當你面對的是一個複雜事物的時候,你一定需要對事物進行分解,分解為幾大塊後各個攻破,然後再來考慮分解後之間的元件如何協同去完成一個業務目標。

最終的企業資訊化軟體並沒有分出這麼的子系統,就是一個大系統。但是後續隨著複雜性的增加細分了諸多的業務子系統出來,類似SRM,CRM,ERP,MES,WMS,CMS等。乃至SRM系統構建中你覺得招投標模組複雜,還可以基於招投標模組單獨構建一個子系統。

當前大家談得多的是微服務。

即使是現在的類似CRM這種單體系統也足夠複雜,還需要進一步細粒度拆分。實際上前面一個大系統拆分為多個子系統同樣是微服務的思想。

那麼一個大型軟體系統實際最複雜的點就在於,究竟應該拆分為幾個子系統或元件,各個元件之間應該如何去整合和互動,來完成一個業務流程或需求場景。

即我們前面談到的業務建模和技術建模的關鍵動作。

當這個步驟完成後,剩下的事情往往僅僅是單個業務功能模組裡面單個功能的實現,API介面的設計和開發,並沒有什麼技術難度。

這個建模誰來做?

即前面談到的類似外科手術醫生,具備業務+技術雙重能力的軟體架構師

當前的架構師往往更多變成了技術驅動的純技術架構師,核心能力在搭建一個技術平臺,搭建要給分散式,高可用的集群系統。而忽視了業務架構和業務建模,同時導致了業務建模和技術建模完全脫節。

在完成這個核心建模後,你才能夠考慮提升效率的點。即前面談到的軟體活動裡面的次要任務,這些次要任務都是可以考慮提升效率或做到完全自動化的點。

比如核心的技術元件和技術平臺,這個往往和業務無關並且可以複用。再比如軟體程式碼的自動生成技術,功能的靈活配置技術。當然也包括了當前我們談得比較多的採用DevOps後的持續整合和持續交付自動化能力提升等。

但是你要意識到這些始終是次要任務,無法實現指數級的軟體效率提升。

分散式架構進一步引入複雜性

在軟體活動的早期,特別是傳統IT架構模式情況下,實際上對於技術架構往往並不複雜,即資料庫透過類似HA架構,應用層透過叢集架構來解決了高可用和擴充套件性問題。

當你採用的是一個非分散式基礎設施的時候,比如你按照的Oracle資料庫,這個時候是同時滿足了高可用性和一致性的,即CAP中的C和A同時滿足。

隨著整個IT資訊化的發展,業務量和併發的需求擴充套件,包括當前雲原生,中臺,微服務等各種新技術和架構的引入,特別是分散式架構的引入。你會發現技術架構的複雜性進一步提升。比如常說的在一個分散式系統,CAP裡面高可用性和高一致性你往往並不能兼顧。

要麼是滿足CP優先,要麼是AP優先。

一個分散式架構的引入,雖然提升了效能和可擴充套件性,但是卻大大增加了分散式系統本身的可靠性,開發的複雜性,後期運維的複雜性。

一個傳統的企業,當你傳統IT架構都做不好的時候,一定不要輕易的進入到類似前面談到的微服務,分散式架構引入這個階段。簡單來說引入分散式架構後複雜性變成了兩個部分。其一是前面談到的業務和功能性架構建模。其二是純技術架構設計和實現。

你會看到軟體研發的精力被分散,本身應該將精力放在系統分析和設計,業務建模上。但是由於技術複雜性引入,你將更多精力放在了類似SpingCLoud微服務框架,服務限流熔斷,分散式事務一致性解決這些事情上。

比如我們常說的很多企業實際在傳統的IT規劃和IT架構上本身就存在業務系統劃分不合理,介面設計不合理,整合混亂,主資料不一致等諸多問題沒有解決。這個時候你應該優先去考慮解決這些問題,而不是將傳統單體進一步拆分為微服務,或者引入分散式架構來加入更多的複雜性內容。

從現實世界到抽象世界的對映

再次說明下對於軟體活動可以理解為:

現實需求-》業務建模-》技術建模-》實現交付-》批次交付

那麼在技術建模完成後到實現交付是否可以做到完全的自動化。在前面我已經表面了一個觀點,即對於簡單系統可以,但是對於複雜系統這塊也很難完全自動化實現。

MDA(Model Driven Architecture)是模型驅動架構,它是由OMG定義的一個軟體開發框架。它是一種基於UML以及其他工業標準的框架,支援軟體設計和模型的視覺化、儲存和交換。和UML相比,MDA能夠創建出機器可讀和高度抽象的模型,這些模型獨立於實現技術,以標準化的方式儲存。

MDA包括PIM(Platform Independent Model,平臺無關模型)、PSM(Platform specific Model,平臺相關模型)和程式碼。PIM是具有高抽象層次、獨立任何實現技術的模型。PIM被轉換為一個或多個PSM。PSM是為某種特定實現技術量身定做。

對於MDA實際希望做到的就是建立完整的PIM和PSM模型,然後能夠實現程式碼自動生成,功能自動實現,實現抽象模型和應用實現的統一。

但是實際情況這應用得並不好,包括現在MDA提起的人很少了。

比如一個規則引擎,當面對複雜規則的時候你會發現對規則的指令碼化描述工作量實際還大於實際透過程式碼來實現規則。在透過JUnit進行自動化單元測試的時候,你會發現如果要實現全業務和流程覆蓋的測試,你寫的單元測試程式碼的量往往超過功能實現原始碼。

如果僅僅定期清楚需求或實現到PIM,你會發現同樣一個需求或模型,當你分發給兩個不同的軟體開發團隊開發的時候,實際最終實現的應用程式碼千差萬別,很難找到任何共性可複用的特徵。

也就是說現實世界和抽象世界之間並沒有真正實現完整對映。

你雖然可以透過類似視覺化,原型驅動開發等方式,但是僅僅停留在介面一致性,而內部功能邏輯實現程式碼仍然差異極大。

有趣的皮囊千篇一律,但是好看的靈魂卻五花八門。

但是一個建築,當設計師設計好詳細的圖紙,缺點好材料後,你卻可以做到分發給不同的建築公司都能夠實現基本一致的實物。特別是在當前BIM模型進一步推廣的時候,這個更加能夠做到所見即所得的效果。類似的例子還有在數字化轉型裡面,智慧製造中的數字孿生技術應用,真正實現了抽象模型和現實世界的統一。

但是在軟體活動中,我們並沒有找到類似的方法。

8
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • SpringMVC非同步處理的 5 種方式