對抗軟件複雜度的戰爭

一、何爲研發效能?

當我們談研發效能的時候,我們在談些什麼?這個議題被拋出來,有人討論,是因爲存在問題,問題就在於實際的研發效率,已經遠低於預期了。企業初創的時候,一個想法從形成到上線,一個人花兩個小時就完成了,而當企業發展到數千人的時候,類似事情的執行,往往需要多個團隊,花費好幾周才能完成。這便造成了鮮明的對比,而這一對比產生的印象,對於沒有深入理解軟件工程的人來說,顯得難以理解,可又往往無計可施。

細心的讀者會留意到,前文我既用了“效能”一詞,也用了“效率”一詞。這是爲了做嚴謹的區分,效能往往是用來衡量產品的經濟績效,而效率僅僅是指提升業務響應能力,提高吞吐,降低成本。

這裏的定義引用了喬梁的《如何構建高效能研發團隊》課程材料,本文並不討論產品開發方法,因此後面的關注都在“效率”上。

本世紀 10 年代,早期的互聯網從業者開發簡易網站的時候,只需要學會使用 Linux、Apache、MySql、PHP(Perl)即可,這套技術有一個好記的名字:LAMP。可今天,在一個大型互聯網公司工作的開發者,需要理解的技術棧上升了一個數量級,例如分佈式系統、微服務、Web 開發框架、DevOps 流水線、容器等雲原生技術等等。如果僅僅是這些複雜度還好說,畢竟都是行業標準的技術,以開發者的學習能力,很快就能掌握。令人生畏的複雜度在於,大型互聯網公司都有一套或者多套軟件系統,這些軟件系統的規模往往都在百萬行以上,質量有好有壞(壞者居多),而開發者必須基於這些系統開展工作。這個時候必須承擔非常高的認知負荷,而修改軟件的時候也會面臨破壞原有功能的巨大風險,而風險的增高就必然導致速度的降低。

因此研發效率的大幅降低,其中一個核心因素就是軟件複雜度的指數上升。

二、本質複雜度和偶然複雜度

Fred Brooks 在經典著作《人月神話》的「沒有銀彈」一文中對於軟件複雜度有着精彩的論述,他將軟件複雜度分爲本質複雜度(Essential Complexity)和偶然複雜度(Accidental Complexity)。這裏的本質和偶然兩個詞來源於亞里士多德的《形而上學》,在亞里士多德看來,本質屬性是一個物體必然擁有的屬性,偶然屬性是一個物體可以擁有的屬性(也可以不擁有)。例如,一個電商軟件必然會包含交易、商品等業務複雜度,因此我們稱它們爲本質複雜度;而同一個電商軟件,可以是基於容器技術實現(也可以不是),可以是基於 Java 編寫的(也可以不是),因此我們稱由於容器技術或者Java 技術而引入的複雜度,爲偶然複雜度。

Fred Brooks 所描述的軟件本質複雜度,指的就是來自問題域本身的複雜度,除非縮小問題域的範圍,否則是無法消除本質複雜度的。而偶然複雜度是由於解決方案帶來的,例如選擇了 Java,選擇了容器,選擇了中臺等等。

此外,我們可以從所謂問題空間(Problem Space)和方案空間(Solution Space)來理解這兩個複雜度,問題空間就是現實的初始狀態和期望狀態,以及一系列約束規則(我們常常稱之爲業務),方案空間就是工程師設計實現的,一系列從初始狀態達到期望狀態的步驟。缺乏經驗的工程師往往在還沒理解清楚問題的情況下就急於寫代碼,這便是缺乏對於問題空間和方案空間的理解,而近年來領域驅動設計爲那麼多工程師所推崇,其核心原因就是它指導了大家去重視問題空間,去直面本質複雜度。Eric Evans 在 2003 年的著作《Domain Driven Design》,其副標題是 “Tackling Complexity in the Heart of Software”,我想這也不是偶然。

《人月神話》寫於 1975 年,距今已經有 47 年了,Brooks 認爲軟件的本質複雜度是無法得到本質上的降低的,同時認爲隨着高級編程語言的演進,開發環境的發展演進,偶然複雜度會得到本質的降低。他的論斷前半部分對了,然而後半部分是錯了,我們今天的確有更高級的編程語言,功能更豐富的框架,能力更強大的 IDE,但是大家逐漸發現學習這些工具已經成爲了一個不小的負擔。

三、複雜度的爆炸

軟件只要不消亡,只要有人用,有開發者維護,那麼它的複雜度幾乎必然是不斷上升的。軟件的生存發展意味着商業上的成功,隨着時間的積累,越來越多的人使用它,越來越多的功能被加入進去,它的價值越來越大,給企業帶去源源不斷的收入。前面我們解釋過,軟件的本質複雜度實際上是問題空間(或者稱之爲業務)帶來的,因此給軟件加入越多的功能,那麼它就必然會包含越多的本質複雜度。此外,每解決一個問題,就對應了一個方案,而方案的實現必然又引入新的偶然複雜度,例如爲了實現支付,需要實現某種新的協議,以對接一個三方的支付系統。軟件複雜度是在商業上成功的企業所必須面對的幸福的煩惱。

和Brooks的時代所不同的是,今天的軟件已經從深入到人類生活的方方面面。稍有規模的互聯網軟件,都服務着數百萬、千萬級的用戶。阿里巴巴的雙11在2020年的峯值實現了每秒58.3萬筆的交易;Netflix 在2021年Q4擁有了2.2億的訂閱用戶;而 TikTok 在2021年9月宣佈月活數量超過10億。這些驚人的商業成功背後,都少不了複雜的軟件系統。而所有這些複雜軟件系統,都不得不面對巨大的 Scalability 的挑戰,服務一個人的系統,和服務一億人的系統,其複雜度有着天壤之別。

本質複雜度是一個方面,畢竟更多用戶意味着更多的功能特性,但我們無法忽略這裏的偶然複雜度,其中最典型的就是分佈式系統引入的偶然複雜度。爲了能夠支撐如此大規模的用戶量,系統需要能夠管理數萬機器(調度系統),需要能否管理好用戶的流量(負載均衡系統),需要能夠管理好不同計算單元之間的通訊(服務發現,RPC,消息系統),需要能夠保持服務的穩定性(高可用體系)。這裏的每一個主題都能延展開用幾本書來描述,而開發者只有在初步掌握了這些知識後,才能夠設計實現足夠 Scalable 的系統,服務好大規模的用戶。

相比於分佈式系統引入的複雜度,團隊的擴張更易帶來偶然複雜度的急劇增長。成功產品的軟件研發團隊動輒數百人,有些已經達到了一兩千人的規模。如果企業沒有嚴格清晰的人才招聘標準,人員入職後沒有嚴格的技術規範培訓,當所有人以不同的風格,不同的長短期目標往代碼倉庫中提交代碼的時候,軟件的複雜度就會急劇上升。

例如,團隊成員因爲個人喜好,在一個全部是 Java 體系的系統中加入了 NodeJS 的組件,當該成員離開團隊後,這個組件對於其他不熟悉 NodeJS 的成員來說,就是純粹多出來的偶然複雜度;

例如,團隊新人不熟悉系統,爲了急於上線一個特性,又不想影響到系統的其他部分,就會很自然地在某個地方加一個 flag,然後在所有需要改動的地方加 if 判斷,而不是去調整系統設計以適應新的問題空間;

例如,同一個領域概念,不同的人在系統不同的模塊中使用了不同的名字,核心內涵完全一致,但又加入了差異的屬性,平添了大量理解成本。

類似的複雜度都不是軟件的本質複雜度,但它們會隨着時間的流逝而積累,給開發者帶來巨大的認知負擔。如果軟件存在的時間很長,那除了當前開發團隊的規模之外,還得一併考慮歷史上給這個軟件貢獻過代碼的所有人,也難怪當程序員看到“祖傳代碼,勿動!”之類調侃的時候,會會心一笑。

我喜歡學習各種能力強大的編程語言,例如具備元編程能力的 Ruby 和 Scala,使用這些能力你可以盡情發揮自己的興趣和創造力。但是我對在有一定團隊規模的生產環境中使用這些編程語言持保留意見,因爲除非花很大力氣 Review 和控制代碼風格,否則很可能 10 個人寫出來的代碼是 10 種風格,這種複雜度的增長是個災難。相反,使用 Java 這種不那麼靈活的語言,大家寫代碼的風格就比較難不一致。

團隊的擴張還會帶來另外一個問題,在大規模的團隊中,關鍵干係人的目標事實上是影響軟件複雜度的關鍵因素。我親眼見過許多案例,其方案空間中明明放着簡單的方案,但因爲這個原因,當事人不得不選擇複雜的方案,例如:

  • 原本方案只需要直接改動系統 A,但由於負責系統 A 的團隊並沒有解決該問題的動力,其他人不得不繞道去修改系統 B,C,D 來解決該問題。
  • 原本方案只需要直接改動系統 A,但迫於系統 B 負責人或者上司的壓力,方案不得不演進成同時改 A,B,甚至引入 C。

更有甚者,爲了各種各樣的原因,提出一些完全假設出來的問題(即,事實上並不存在的本質複雜度),然後拿着軟件系統一陣無謂折騰。最後個人或者某個團隊的目標實現了,但軟件沒有提供任何增量的價值,而複雜度卻不會因此而停止增長。

因此,只要軟件有價值,有用戶,有開發者維護,那麼就不斷會有功能增加,而商業上獲得成功的軟件必然伴隨着用戶量的增長和研發團隊的增長,這三個因素會不斷推動軟件複雜度的增長直至爆炸,研發效率自然會越來越低。軟件工程要解決的一個核心命題,就是如何控制複雜度,以讓研發效率不至於下降的太厲害,這是一場對抗軟件複雜度的戰爭。

四、錯誤的應對方式

面對效率地不斷下降,研發團隊的管理者必須做點什麼。不幸的是,很多管理者並不明白效率的降低是由軟件複雜度的上升造成的,更沒有冷靜地去思考複雜度蔓延直至爆炸的根因是什麼,於是我們看到許多管理者其膚淺的應對方式收效甚微,甚至起到了反作用。

最常見的錯誤方式是設置一個不可更改的 Deadline,用來倒逼研發團隊交付功能。但無數經驗告訴我們,軟件研發就是在質量、範圍和時間這個三角中求取權衡。研發團隊短期可以通過加班,犧牲假期等手段來爭取一些時間(長期加班實際有百害無一利),但如果這個時間限制過於苛刻,那必然就要犧牲需求範圍和軟件質量。當需求範圍不可縮減的時候,唯一可以被犧牲的就只有質量了,這實際就意味着在很短的時間內往系統中傾瀉大量的偶然複雜度。

另一種做法是用“更先進”的技術去替換現有系統的技術,例如用 Java 的微服務體系技術去替換 PHP + Golang 體系的技術;或者用支撐過成功商業產品的中臺類技術去替換原來的微服務體系技術;或者簡單到用雲產品去替換自建的開源服務。這些做法背後的基本邏輯是,“更先進”的技術在成功的商業場景中被驗證過,因此可以被直接拿來解決現有的問題。

但在現實情況下,決策者往往忽略了當前的問題是否是“更先進”的技術可以解決的問題。如果現有的系統服務的用戶在迅速增長,Scalablity 面臨了嚴重的困境,那麼這個答案是肯定的;如果現有的系統的穩定性堪憂,經常不可用且嚴重影響了用戶體驗,那麼這個答案是肯定的。但是,如果現有的軟件系統面臨着研發效率下降問題,那麼“更先進”的技術不僅幫不了什麼忙,還會因爲新老技術的切換給系統增加偶然複雜度。

五、正確的技術戰略

前文我解釋了導致複雜度增長的幾個核心因素,包括業務複雜度的增長,分佈式系統規模的增長,團隊規模的增長,以及關鍵干係人目標的因素。這其中,分佈式系統引入的偶然複雜度是最容易被消除的。爲了更好得理解這個觀點,我先簡單介紹一下 Wardley Map。

Wardley Map 是一個幫助分析技術戰略的工具,它以地圖的方式展現,地圖中的每個組件可以被理解成一個軟件模塊,縱座標是價值方向,越往上越靠近用戶價值,橫座標是進化方向,越往右越靠近成熟商業產品。

例如上圖中,Compute 是計算資源,在今天有許多成熟的雲計算公司提供,但它離圖中上下文業務的用戶價值非常遠。Virtual Fitting(虛擬試衣)則離用戶價值非常靠近,因爲它可以讓用戶更有信心自己是否購買了合適的衣服,但是這個技術顯然還談不上是成熟產品,只是自己研發的模塊,遠沒有達到開放商業化的階段。

設計研發一套用來支撐百萬、千萬級用戶的分佈式系統,是非常有挑戰的事情,而且會給系統引入大量的複雜度,管理好這些複雜度本身則又是一項巨大的挑戰。幸運的是,今天的雲廠商,包括阿里巴巴,亞馬遜,谷歌和微軟等,在這方面都具有豐富的經驗,並且已經通過多年的積累,把這些經驗通過商業產品提供給市場。

從 Wardley Map 的方式去分析,我們就會發現,幾乎所有的業務,其左上角(貼近直接用戶價值,不成熟)都必須是要自己研發和承擔複雜度的,而只要做好正確的軟件架構,那麼就能把右下角的部分(遠離直接用戶價值,有現成商業產品)提取出來,直接購買。所以在今天,一個合格的架構師,除非自己是雲廠商,否則絕對不應該自己去投入研發數據庫、調度系統、消息隊列、分佈式緩存等軟件。通過購買的方式,研發團隊完全不用承擔這些複雜度,也能輕鬆地支撐好用戶規模的增長。

六、微觀層面的複雜度控制

正確的技術戰略能夠在宏觀層面幫助系統控制複雜度,在微觀層面我們需要完全不同的方法。在討論方法之前我想先引用一個來自《Grokking Simplicity》書中的一個簡單例子。(有趣的是,這本書的副標題 “Taming complex software with functional thinking” 也是在表達對抗複雜度的意圖。)

讓我們來看兩個函數(JavaScript):

function emailsForCustomers(customers, goods, bests) {  var emails = [];  for(var i = 0; i < customers.length; i++) {    var customer = customers[i];    var email = emailForCustomer(customer, goods, bests);    emails.push(email);  }}
function biggestPurchasePerCustomer(customers) {  var purchases = [];  for(var i = 0; i < customers.length; i++) {    var customer = customers[i];    var purchase = biggestPurchase(customer);    purchases.push(purchase);  }}

初看起來這兩個函數沒什麼問題,都是準備一個返回值,寫一個循環,根據具體業務邏輯提取需要的數據,差別只是在於,前一個函數的業務邏輯是獲取客戶的 Email,後一個函數的業務邏輯是獲取客戶下過的最大的單。然而就這麼簡單的代碼來說,也是存在可以降低的複雜度的,理解閱讀這兩個函數,每次都需要去理解 for 循環,這個複雜度是否可以進一步被降低呢?答案是肯定的。

對於這種到處可見的邏輯,即遍歷集合的每個元素,對其中每個元素做一些處理,返回一個新元素,最後裝成一個新的集合,可以被抽象成一個 map 函數。在這個例子中,我假設 JavaScript 支持 map 函數,那麼上面的代碼可以寫成:

function emailsForCustomers(customers, goods, bests) {  return map(customers, function(customer) {    return emailForCustomer(customer, goods, bests);  });}
function biggestPurchasePerCustomer(customers) {  return map(customers, function(customer) {    return biggestPurchase(customer);  });}

拋開語言語法的因素不談,這段代碼除了這個 map 函數,剩下的就是函數名了,而函數名只要是命名得當的,那它其實就是本質複雜度,就是業務邏輯。行業先輩,大家耳熟能詳的 Martin Fowler、Kent Beck、Robert C. Martin,無不在他們的書籍中強調命名的重要性,都是希望代碼能夠清晰地溝通意圖,而這裏最核心的意圖應當是與問題域匹配的。

這個例子中的代碼是極其簡單的,所有程序員都能理解,可即便在這些地方還有降低複雜度的空間。可以想象在數年日積月累的代碼中,會存在多少複雜度可被消除。我又想起多年前的一位同事兼長輩的程序員的教誨,他說優秀的代碼應該是:

  • It works
  • It is easy to understand
  • It is safe to change

事實上要做到第二點已經是非常高的要求,這需要軟件工程師精心地設計,清晰地溝通好需求,思考和遺留系統的融合,還需要壓制住自己使用新技術(新語言,新的範式)的衝動。而第三點實際上是教會我們認真的寫單元測試。

我不知道大家是否體驗過這種感覺:在需求討論清晰後,我編寫了對應的代碼和單元測試,具體是在原來的幾萬行 code base 上加了幾百行,並原來 1000 個左右的單元測試上加入了 3-5 個單元測試,然後我在本地執行了一次 mvn clean test,這個過程也就消耗幾分鐘,全部測試都通過了,這時候我非常有信心把代碼提交,而且我知道代碼在生產環境運行出問題的概率極低。

這種感覺的核心是質量反饋,這個反饋時間越短,效率就越高,反饋時間越長,效率就越低。除了控制複雜度之外,軟件工程師必須明白及時質量反饋的重要性,如果一行代碼寫下去,要等好幾個小時,甚至好幾天才知道其質量有問題,效率的低下可想而知。所以當我看到當組織自上而下提倡寫單元測試,但大家實踐中的一些怪現象時,常常會感到匪夷所思,這些現象會包括:

  • 低質量的單元測試:包括不寫 assert,到處是 print 語句,要人去驗證。
  • 不穩定的單元測試:代碼是好的,測試是失敗的,測試集無法被信任。
  • 耗時非常長的單元測試:運行一下要幾十分鐘或者幾小時。
  • 用代碼生成單元測試:對不起,我認爲這個東西除了提升覆蓋率虛榮指標外,毫無意義。

七、軟件道德觀

在微觀層面控制軟件複雜度,認真編寫單元測試以保障代碼編寫的質量反饋,對於研發效率來說是至關重要的,但同時也是耗時耗力的。而且由於這種投入對商業的價值需要很長時間才能體現出來,因此容易被研發主管所忽視。

開發者都是在生產代碼、文檔、API 服務等軟件中間產物,這些中間產物被逐漸組裝起來成爲產品,產生商業價值。軟件中間產物的質量對於研發組織的整體效率是至關重要的,而複雜度得到很好控制的代碼和系統,就是高質量的軟件中間產物;良好的軟件研發道德,或者有時候也會認爲這是良好的工程師文化,就是大家形成一種以交付高質量軟件中間產物爲榮,以交付低質量軟件中間產物爲恥的共識文化。

軟件研發的核心職責之一是關注軟件複雜度,通過開放代碼、文檔,Code Review 等方式讓軟件複雜度的信息透明,並且讓所有在增加/降低複雜度的行爲透明,並且持續激勵那些消除複雜度的行爲。唯有如此,在微觀層面的控制複雜度的方法才能得到落實。

八、系統架構對複雜度的影響

介於宏觀的技術戰略和微觀的工程師文化之間,存在着一塊重要的決策區域,也對軟件複雜度有着關鍵的影響,我稱之爲系統架構。在面對需求的時候,缺乏經驗的工程師會直接想着在自己熟悉的模塊中直接解決,而經驗豐富的工程師會先思考一下系統上下文。在《Design Docs at Google》這篇優秀的技術文檔寫作指導中,就重點提到了,設計文檔應當寫清楚系統上下文圖(system-context-diagram),這背後的原因是什麼呢?

我近期對一個遺留系統做了一個依賴鏈路的梳理分析,這個系統是負責生產環境中各類資源的管理的,包括資源的規格,版本,依賴關係等等,梳理完成後,整體的結構嚇了我一跳,這個圖大致是這樣的:

圖中藍色的部分是控制和執行的子系統(System X,Y,Z),例如控制容器的調度,控制鏡像變更的執行等等,是比較清晰的。但是其餘部分就不是這樣了(A1, A2, A3, C1, C2, S, E),它們都是在管理一個資源的運行態版本,包括鏡像的版本,容器的規格,是否有 GPU,容器的數量,關聯的網絡資源等等,但卻演進出了七個子系統,這實際上是非常高的偶然複雜度。當一個領域的概念被分散到這麼多子系統之後,就會產生一系列問題:

  • 不同子系統對於同一個概念有不同的名稱,交互的時候會涉及各種翻譯。
  • 不同子系統承擔了同一個實體的部分概念,導致修改的時候需要大範圍一起修改,且容易出錯。
  • 更高的運維成本。

仔細去分析這一複雜度形成的因素,我發現這既不是技術戰略的問題,也不是微觀層面工程師生產低質量代碼導致,而是有其他更深層次的問題。其中的最核心的因素是,這些子系統在不同時期是歸屬於不同的團隊的,有的甚至是不同部門的,具體來說,當各個部門各個團隊目標不一致的時候,而這個系統又不幸地被拆到各個團隊,那麼就不會有人會對系統整體的複雜度控制負責。當有的團隊在負責把這套系統商業化對外輸出,有的團隊在負責把這套系統從虛擬機模式演進到容器模式,有的團隊在負責資源的成本控制,有的團隊在思考全局高可用架構,而沒有一個全局的架構師從整體控制概念,控制邊界的時候,系統就自然而然地腐化成這樣的一個狀態了。

當一個問題域沒有系統架構,或者其系統架構是錯誤的時候,你就會發現不同的人在發明不同的語言,這就好比相隔幾十公里的兩個村子,常常對同一個概念有不同的用詞或者發音。日常生活中語言的不精確不是問題,因爲日常的溝通是充滿上下文的(表情,氣氛,環境等),但在計算機的世界,語言的不精確就意味着需要寫代碼翻譯,一旦翻譯錯誤軟件就會執行出錯。這也就是爲什麼領域驅動設計那麼強調統一語言,強調限定上下文。但領域驅動設計是方法論,而知道方法並不能取代系統架構角色的缺位。

這個複雜系統是康威定律的絕佳例證,康威定律說:“任何系統設計的系統,其系統結構會複製組織的溝通結構。”這句話其實還是有些抽象的,更具體的一些闡述是:

“康威定律 … 是一個合理的社會學觀察。… 除非模塊 A 和模塊 B 的設計及實現者能有效溝通,否則這兩個軟件模塊是無法正確對接的。因此軟件系統的接口結構,就必然會和生產軟件系統的社會結構及組織相對應。”

康威定律所揭示的事實,就是軟件架構在很大程度上是由組織的結構和協作模式決定的,這實際上已經不再是一個軟件技術問題了,而是一個組織管理問題。因此,解決系統架構層面的軟件複雜度問題,就必須面對組織管理的挑戰。關鍵問題域是否有唯一的負責人?當不同的團隊在同一個問題域重複建設系統的時候,如何整合團隊?當已有團隊爲了自己的生存,不斷誇大其負責系統的重要性和特殊性,如何識別這種問題?組織如何給予大家充分的安全感,讓工程師願意爲了架構的合理性,放棄自己辛苦耕作的系統模塊?

討論管理工作似乎已經超出了這篇論述軟件複雜度的文章的範疇,但很多工程師或者隱隱感覺,或者思來想去最終領悟,這是我們的軟件系統或優雅健壯或千瘡百孔的根本因素。

小結

我曾經的大老闆郭東白曾在一次 QCon 的演講中討論優秀架構師的特質,除了大家都很好理解的有眼光、善於思考、能感召等幾個特質外,還特別強調了“有良知”,他說:

有良知,這是一個架構師隨着時間的流逝,沉澱在身上最重要的品質。什麼是有良知?爲人正直,選擇做正確的事情。很多人是非常聰明的,業務理解能力強,技術實踐豐富,但他不一定爲公司或爲組織做最正確的事情。有良知是非常重要的一個事情,如果架構師沒有素質,他會讓一家公司的損失很慘重。

軟件複雜度是人的行爲引起的,無論是微觀層面的重視質量和工程師文化,在系統架構層面讓組織結構和溝通符合客觀問題域,還是在技術戰略層面做符合公司利益的決策,這裏都存在客觀無法改變的規律。如何認識到這些規律,並基於這些規律制定決策(可改變可影響),努力爲公司創造價值,努力讓每個工程師被尊重,是每個工程師、架構師、技術管理者所應當秉承的基本態度。本文討論軟件複雜度的初衷,就是儘量去揭示覆雜度背後的客觀規律,希望幫助大家認清現實,用更務實的態度去思考和決策,創造更有價值,也更讓自己滿足的軟件系統。

參考閱讀:

  1. Why choose Domain-Driven Design? 這篇文章清晰地解釋了本質複雜度和領域驅動設計的關係。
  2. 《人月神話》-「沒有銀彈」一篇闡述了本質複雜度和偶然複雜度的概念。
  3. 《The Lean Product Playbook》- 本書的第2章清晰地解釋了 Problem Space 和 Solution Space。
  4. Wardley Map - 分析技術戰略的絕佳工具,合理地選取商業產品可以幫助降低系統複雜度。
  5. Grokking Simplicity - 在微觀層面,使用函數式的思維降低軟件複雜度。
  6. Design Docs at Google
  7. Conway’s Law
  8. 警惕複雜度困局:關於軟件複雜度的思考

作者|曉斌

點擊立即免費試用雲產品 開啓雲上實踐之旅!

原文鏈接

本文爲阿里雲原創內容,未經允許不得轉載。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章