精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

阿里研究員:警惕軟件復雜度困局

開發(fā) 開發(fā)工具
對于大型的軟件系統(tǒng)如互聯(lián)網(wǎng)分布式應用或企業(yè)級軟件,為何我們常常會陷入復雜度陷阱?如何識別復雜度增長的因素?

 [[339003]]

 

阿里妹導讀:對于大型的軟件系統(tǒng)如互聯(lián)網(wǎng)分布式應用或企業(yè)級軟件,為何我們常常會陷入復雜度陷阱?如何識別復雜度增長的因素?在代碼開發(fā)以及演進的過程中需要遵循哪些原則?本文將分享阿里研究員谷樸關于軟件復雜度的思考:什么是復雜度、復雜度是如何產(chǎn)生的以及解決的思路。較長,同學們可收藏后再看。

文末福利:免費下載《2020年微服務領域開源數(shù)字化報告》。

寫在前面

軟件設計和實現(xiàn)的本質是工程師相互通過“寫作”來交流一些包含豐富細節(jié)的抽象概念并且不斷迭代過程。

另外,如果你的代碼生存期一般不超過6個月,本文用處不大。

一 軟件架構的核心挑戰(zhàn)是快速增長的復雜性

越是大型系統(tǒng),越需要簡單性。

大型系統(tǒng)的本質問題是復雜性問題。互聯(lián)網(wǎng)軟件,是典型的大型系統(tǒng),如下圖所示,數(shù)百個甚至更多的微服務相互調用/依賴,組成一個組件數(shù)量大、行為復雜、時刻在變動(發(fā)布、配置變更)當中的動態(tài)的、復雜的系統(tǒng)。而且,軟件工程師們常常自嘲,“when things work, nobody knows why”。

 

圖源:https://divante.com/blog/10-companies-that-implemented-the-microservice-architecture-and-paved-the-way-for-others/

如果我們只是寫一段獨立代碼,不和其他系統(tǒng)交互,往往設計上要求不會很高,代碼是否易于使用、易于理解、易于測試和維護,根本不是問題。而一旦遇到大型的軟件系統(tǒng)如互聯(lián)網(wǎng)分布式應用或者企業(yè)級軟件,我們常常陷入復雜度陷阱,下圖the life of a software engineer是我很喜歡的一個軟件cartoon,非常形象的展示了復雜度陷阱。

圖源:http://themetapicture.com/the-life-of-a-software-engineer/

 

做為一個有追求的軟件工程師,大家肯定都思考過,我手上的項目,如何避免這種似乎難以避免的復雜度困境?

然而對于這個問題給出答案,卻出乎意料的困難:很多的文章都給出了軟件架構的設計建議,然后正如軟件領域的經(jīng)典論著《No silver bullet》所說,這個問題沒有神奇的解決方案。并不是說那么多的架構文章都沒用(其實這么方法多半都有用),只不過,人們很難真正去follow這些建議并貫徹下去。為什么?我們還是需要徹底理解這些架構背后的思考和邏輯。所以我覺得有必要從頭開始整理這個邏輯:什么是復雜度,復雜度是如何產(chǎn)生的,以及解決的思路。

二 軟件的復雜度為什么會快速增長?

要理解軟件復雜度會快速增長的本質原因,需要理解軟件是怎么來的。我們首先要回答一個問題,一個大型的軟件是建造出來的,還是生長出來的?BUILT vs GROWN,that is the problem.

1 軟件是長出來的,不是建造出來的

軟件不是建造出來的,甚至不是設計出來的。軟件是長出來的。

這個說法初看上去和我們平時的認識似乎不同,我們常常談軟件架構,架構這個詞似乎蘊含了一種建造和設計的意味。然而,對于軟件系統(tǒng)來說,我們必須認識到,架構師設計的不是軟件的架構,而是軟件的基因,而這些基因如何影響軟件未來的形態(tài)則是難以預測,無法完全控制。

為什么這么說?所謂建造和“生長”差異在哪里?其實,我們看今天一個復雜的軟件系統(tǒng),確實很像一個復雜的建筑物。但是把軟件比作一棟摩天大樓卻不是一個好的比喻。原因在于,一個摩天大樓無論多么復雜,都是事先可以根據(jù)設計出完整詳盡的圖紙,按圖準確施工,保證質量就能建造出來的。然而現(xiàn)實中的大型軟件系統(tǒng),卻不是這么建造出來的。

 

例如淘寶由一個單體PHP應用,經(jīng)過4、5代架構不斷演進,才到今天服務十億人規(guī)模的電商交易平臺。支付寶,Google搜索,Netflix微服務,都是類似的歷程。

是不是一定要經(jīng)過幾代演進才能構建出來大型軟件,就不能一次到位嗎?如果一個團隊離開淘寶,要拉開架勢根據(jù)淘寶交易的架構重新復制一套,在現(xiàn)實中是不可能實現(xiàn)的:沒有哪個創(chuàng)業(yè)團隊能有那么多資源同時投入這么多組件的開發(fā),也不可能有一開始就朝著超級復雜架構開發(fā)而能夠成功的實現(xiàn)。

 

也就是說,軟件的動態(tài)“生長”,更像是上圖所畫的那樣,是從一個簡單的“結構”生長到復雜的“結構”的過程。伴隨著項目本身的發(fā)展、研發(fā)團隊的壯大,系統(tǒng)是個逐漸生長的過程。

2 大型軟件的核心挑戰(zhàn)是軟件“生長”過程中的理解和維護成本

復雜軟件系統(tǒng)最核心的特征是有成百上千的工程師開發(fā)和維護的系統(tǒng)(軟件的本質是工程師之間用編程語言來溝通抽象和復雜的概念,注意軟件的本質不是人和機器溝通)。如果認同這個定義,設想一下復雜軟件是如何產(chǎn)生的:無論最終多么復雜的軟件,都要從第一行開始開發(fā)。都要從幾個核心開始開發(fā),這時架構只能是一個簡單的、少量程序員可以維護的系統(tǒng)組成架構。隨著項目的成功,再去逐漸細化功能,增加可擴展性,分布式微服務化,增加功能,業(yè)務需求也在這個過程中不斷產(chǎn)生,系統(tǒng)滿足這些業(yè)務需求,帶來業(yè)務的增長。業(yè)務增長對于軟件系統(tǒng)迭代帶來了更多的需求,架構隨著適應而演進,投入開發(fā)的人員隨著業(yè)務的成功增加,這樣不斷迭代,才會演進出幾十,幾百,甚至幾千人同時維護的復雜系統(tǒng)來。

大型軟件設計核心要素是控制復雜度。這一點非常有挑戰(zhàn),根本原因在于軟件不是機械活動的組合,不能在事先通過精心的“架構設計”規(guī)避復雜度失控的風險:相同的架構圖/藍圖,可以長出完完全全不同的軟件來。大型軟件設計和實現(xiàn)的本質是大量的工程師相互通過“寫作”來交流一些包含豐富細節(jié)的抽象概念并且相互不斷迭代的過程[2]。稍有差錯,系統(tǒng)復雜度就會失控。

所以說了這么多是要停留在形而上嗎?并不是。我們的結論是,軟件架構師最重要的工作不是設計軟件的結構,而是通過API,團隊設計準則和對細節(jié)的關注,控制軟件復雜度的增長。

  • 架構師的職責不是試圖畫出復雜軟件的大圖。大圖好畫,靠譜的系統(tǒng)難做。復雜的系統(tǒng)是從一個個簡單應用 一點點長出來的。
  • 當我們發(fā)現(xiàn)自己的系統(tǒng)問題多多,別怪“當初”設計的人,坑不是一天挖出來的。每一個設計決定都在貢獻復雜度。

三 理解軟件復雜度的維度

1 軟件復雜度的兩個表現(xiàn)維度:認知負荷與協(xié)同成本

我們分析理解了軟件復雜度快速增長的原因,下面我們自然希望能解決復雜度快速增長這一看似永恒的難題。但是在此之前,我們還是需要先分析清楚一件事情,復雜度本身是什么?又如何衡量?

代碼復雜度是用行數(shù)來衡量么?是用類的個數(shù)/文件的個數(shù)么?深入思考就會意識到,這些表面上的指標并非軟件復雜度的核心度量。正如前面所分析的,軟件復雜度從根本上說可以說是一個主觀指標(先別跳,耐心讀下去),說其主觀是因為軟件復雜度只有在程序員需要更新、維護、排查問題的時候才有意義。一個不需要演進和維護的系統(tǒng)其架構、代碼如何關系也就不大了(雖然現(xiàn)實中這種情況很少)。

 

既然 “軟件設計和實現(xiàn)的本質是工程師相互通過寫作來交流一些包含豐富細節(jié)的抽象概念并且不斷迭代過程” (第三次強調了),那么,復雜度指的是軟件中那些讓人理解和修改維護的困難程度。相應的,簡單性,就是讓理解和維護代碼更容易的要素。

“The goal of software architecture is to minimize the manpower required to build and maintain the required system.” Robert Martin, Clean Architecture [3].

因此我們將軟件的復雜度分解為兩個維度,都和人理解與維護軟件的成本相關:

  • 第一,認知負荷 cognitive load :理解軟件的接口、設計或者實現(xiàn)所需要的心智負擔。
  • 第二,協(xié)同成本Collaboration cost:團隊維護軟件時需要在協(xié)同上額外付出的成本。

我們看到,這兩個維度有所區(qū)別,但是又相互關聯(lián)。協(xié)同成本高,讓軟件系統(tǒng)演進速度變慢,效率變差,工作其中的工程師壓力增大,而長期難以取得進展,工程師傾向于離開項目,最終造成質量進一步下滑的惡性循環(huán)。而認知負荷高的軟件模塊讓程序員難以理解,從而產(chǎn)生兩個后果:(1) 維護過程中易于出錯,bug 率故障率高;(2) 更大機率 團隊人員變化時被拋棄,新成員選擇另起爐灶,原有投入被浪費,甚至更高糟糕的是,代碼被拋棄但是又無法下線,成為定時炸彈。

2 影響到認知負荷的因素

認知負荷又可以分解為:

  • 定義新的概念帶來認知負荷,而這種認知負荷與 概念和物理世界的關聯(lián)程度相關。
  • 邏輯符合思維習慣程度:正反邏輯差異,邏輯嵌套和獨立原子化組合。繼承和組裝差異。

(1)不恰當?shù)倪壿嫀淼恼J知成本

看以下案例[7]:

A. Code with too much nesting

  1. response = server.Call(request) 
  2.   
  3. if response.GetStatus() == RPC.OK: 
  4.   if response.GetAuthorizedUser(): 
  5.     if response.GetEnc() == 'utf-8'
  6.       if response.GetRows(): 
  7.         vals = [ParseRow(r) for r in 
  8.                 response.GetRows()] 
  9.         avg = sum(vals) / len(vals) 
  10.         return avg, vals 
  11.       else
  12.         raise EmptyError() 
  13.     else
  14.       raise AuthError('unauthorized'
  15.   else
  16.     raise ValueError('wrong encoding'
  17. else
  18.   raise RpcError(response.GetStatus()) 

B. Code with less nesting

  1. response = server.Call(request) 
  2.   
  3. if response.GetStatus() != RPC.OK: 
  4.   raise RpcError(response.GetStatus()) 
  5.  
  6. if not response.GetAuthorizedUser(): 
  7.   raise ValueError('wrong encoding'
  8.  
  9. if response.GetEnc() != 'utf-8'
  10.   raise AuthError('unauthorized'
  11.   
  12. if not response.GetRows(): 
  13.   raise EmptyError() 
  14.  
  15. vals = [ParseRow(r) for r in 
  16.         response.GetRows()] 
  17. avg = sum(vals) / len(vals) 
  18. return avg, vals 

比較A和B,邏輯是完全等價的,但是B的邏輯明顯更容易理解,自然也更容易在B的代碼基礎上增加功能,且新增的功能很可能也會維持這樣一個比較好的狀態(tài)。

而我們看到A的代碼,很難理解其邏輯,在維護的過程中,會有更大的概率引入bug,代碼的質量也會持續(xù)惡化。

(2)模型失配:和現(xiàn)實世界不完全符合的模型帶來高認知負荷

軟件的模型設計需要符合現(xiàn)實物理世界的認知,否則會帶來非常高的認知成本。我遇到過這樣一個資源管理系統(tǒng)的設計,設計者從數(shù)學角度有一個非常優(yōu)雅的模型,將資源賬號 用合約來表達(下圖左側),賬戶的balance可以由過往合約的累計獲得,確保數(shù)據(jù)一致性。但是這樣的設計,完全不符合用戶的認知,對于用戶來說,感受到的應該是賬號和交易的概念,而不是帶著復雜參數(shù)的合約。可以想象這樣的設計,其維護成本非常之高。

 

(3)接口設計不當

以下是一個典型的接口設計不當帶來的理解成本。

  1. class BufferBadDesign { 
  2.  
  3.   explicit Buffer(int size);// Create a buffer with given sized slots 
  4.   void AddSlots(int num);// Expand the slots by `num` 
  5.   // Add a value to the end of stack, and the caller need to 
  6.   // ensure that there is at least one empty slot in the stack before 
  7.   // calling insert 
  8.   void Insert(int value); 
  9.  
  10.   int getNumberOfEmptySlots(); // return the number of empty slots 

希望我們的團隊不會設計出這樣的模塊。這個問題可以明顯看到一個接口設計的不合理帶來的維護成本提升:一個Buffer的設計暴露了內部內存管理的細節(jié)(slot維護),從而導致在調用最常用接口 “insert”時存在陷阱:如果不在insert前檢查空余slot,這個接口就會有異常行為。

但是從設計角度看,維護底層的Slot的邏輯,也外部可見的buffer的行為其實并沒有關聯(lián),而只是一個底層的實現(xiàn)細節(jié)。因此更好的設計應該可以簡化接口。把Slot數(shù)量的維護改為內部的實現(xiàn)邏輯細節(jié),不對外暴露。這樣也完全消除了因為使用不當帶來問題的場景。同時也讓接口更易于理解,降低了認知成本。

class Buffer { explicit Buffer(int size); // Create a buffer with given sized slots // Add a value to the end of buffer. New slots are added // if necessary. void Insert(int value);}

事實上,當我們發(fā)現(xiàn)一個模塊在使用時具備如下特點時,一般就是難以理解、容易出錯的信號:

  • 一個模塊需要調用者使用初始化接口才能正常行為:對于調用者來說,需要調用初始化接口看似不是大的問題,但是這樣的模塊,帶來了多種后患,尤其是當存在多個參數(shù)需要設置,相互關聯(lián)關系復雜時。配置問題應該單獨解決(比如通過工廠模式,或者通過單獨的配置系統(tǒng)來管理)。
  • 一個模塊需要調用者使用后做清理/ finalizer才能正常退出。
  • 一個模塊有多種方式讓調用者實現(xiàn)完全相同的功能:軟件在維護過程中,出現(xiàn)這種狀況可能是因為初始設計不當后來修改設計 帶來的冗余,也可能是設計原版的缺陷,無論如何這種模塊,帶著強烈的“壞味道”。

完全避免這些問題很難,但是我們需要在設計中盡最大努力。有時通過文檔的解釋來彌補這些問題是必要的,但是好的工程師/架構師,應該清醒的意識到,這些都是“壞味道”。

(4)一個簡單的修改需要在多處更新

簡單修改涉及多處更改也是常見的軟件維護復雜度因素,而且主要影響的是我們的認知負荷:維護修改代碼時需要花費大量的精力確保各處需要修改的地方都被照顧到了。

最簡單的情形是代碼當中有重復的“常數(shù)”,為了修改這個常數(shù),我們需要多處修改代碼。程序員也知道如何解決這一問題,例如通過定義個constant 并處處引用避免magic number。再例如網(wǎng)頁的風格/色彩,每個頁面相同配置都重復設置同樣的色彩和風格是一種模式,而采用css模版則是更加易于維護的架構。這在架構原則中對應了數(shù)據(jù)歸一化原則(Data normalization)。

稍微復雜一些的是類似的邏輯/或者功能被copy-paste多次,原因往往是不同的地方需要稍微不同的使用方式,而過去的維護者沒有及時refactor代碼提取公共邏輯(這樣做往往需要更多的時間精力),而是省時間情況下選擇了copy-paste。這就是常說的 Don't repeat yourself原則:

Every piece of knowledge must have a single, unambiguous, authoritative representation within a system[8]

(5)命名

軟件中的API、方法、變量的命名,對于理解代碼的邏輯、范圍非常重要,也是設計者清晰傳達意圖的關鍵。然而,在很多的項目里我們沒有給Naming /命名足夠的重視。

我們的代碼一般會和一些項目關聯(lián),但是需要注意的是項目是抽象的,而代碼是具體的。項目或者產(chǎn)品可以隨意一些命名,如阿里云喜歡用中國古代神話(飛天、伏羲、女媧)命名系統(tǒng),K8s也是來自于希臘神話,這些都沒有問題。而代碼中的API、變量、方法不能這樣命名。

一個不好的例子是前一段我們的Cluster API 被命名為Trident API(三叉戟),設想一下代碼中的對象叫Trident時,我們如何理解在這個對象應該具備的行為?再對比一下K8s中的資源:Pod, ReplicaSet, Service, ClusterIP,我們會注意到都是清晰、簡單、直接符合其對象特征的命名。名實相符可以很大程度上降低理解該對象的成本。

有人說“Naming is the most difficult part of software engineering[9][10]”,或許也不完全是個玩笑話:Naming的難度在于對于模型的深入思考和抽象,而這往往確實是很難的。

需要注意的是:

(a)Intention vs what it is

需要避免用“是什么”來命名,要用“for what / intention”。“是什么”來命名是會很容易將實現(xiàn)細節(jié)。比如我們用 LeakedBarrel做rate limiting,這個類最好叫 RateLimiter,而不是LeakedBarrel:前者定義了意圖(做什么的),后者 描述了具體實現(xiàn),而具體實現(xiàn)可能會變化。再比如 Cache vs FixedSizeHashMap,前者也是更好的命名。

(b)命名需要符合當前抽象的層級

首先我們軟件需要始終有清晰的抽象和分層。事實上我們Naming時遇到困難,很多就是因為軟件已經(jīng)缺乏明確的抽象和分層帶來的表象而已。

(6)不知道一個簡單特性需要在哪些做修改,或者一個簡單的改動會帶來什么影響,即unknown unknowns

在所有認知復雜度的表現(xiàn)中,這是最壞的一種,不幸的是,所有人都曾經(jīng)遇到過這樣的情況。

 

一個典型的unknown unknown是一部分代碼存在這樣的情況:

  • 代碼缺乏充分的測試覆蓋,一些重要場景依賴維護者手工測試。
  • 代碼有隱藏/不易被發(fā)現(xiàn)的行為或者邊界條件,與文檔和接口描述并不符合。

對于維護者來說,改動這樣的代碼(或者是改動影響到了這樣代碼 / 被這樣代碼影響到了)時,如果按照接口描述或者文檔進行,沒發(fā)現(xiàn)隱藏行為,同時代碼又缺乏足夠測試覆蓋,那么就存在未知的風險unknown unknowns。這時出現(xiàn)問題是很難避免的。最好的方式還是要盡量避免我們的系統(tǒng)質量劣化到這個程度。

上線時,我們最大的噩夢就是unknown unknowns:這類風險,我們無法預知在哪里或者是否有問題,只能在軟件上線后遇到問題才有可能發(fā)現(xiàn)。其他的問題 尚可通過努力來解決(認知成本),而unknown unknowns可以說已經(jīng)超出了認知成本的范圍。我們最希望避免的也是unknown unknowns。

(7)認知成本低要不易出錯,而不是無腦“簡化”

從認知成本角度來說,我們還要認識到,衡量不同方案/寫法的認知成本,要考慮的是不易出錯,而不是表面上的簡化:表面上簡化可能帶來實質性的復雜度上升。

例如,為了表達時間段,可以有兩種選擇:

  1. // Time period in seconds. 
  2. void someFunction(int timePeriod);  
  3. // time period using Duration.  
  4. void someFunction(Duration timePeriod); 

在上面這個例子里面,我們都知道,應該選用第二個方案,即采用Duration作time period,而不是int:盡管Duration本身需要一點點學習成本,但是這個模式可以避免多個時間單位帶來的常見問題。

3 影響協(xié)同成本的因素

協(xié)同成本則是增長這塊模塊所需要付出的協(xié)同成本。什么樣的成本是協(xié)同成本?(1)增加一個新的特性往往需要多個工程師協(xié)同配合,甚至多個團隊協(xié)同配合;(2) 測試以及上線需要協(xié)調同步。

(1)系統(tǒng)模塊拆分與團隊邊界

在微服務化時代,模塊/服務的切分和團隊對齊,更加有利于迭代效率。而模塊拆分和邊界的不對齊,則讓代碼維護的復雜度增加,因這時新的特性需要在跨多個團隊的情況下進行開發(fā)、測試和迭代。

另外一個角度,則是:

Any piece of software reflects the organizational structure that produces it.

或者就是我們常說的“組織架構決定系統(tǒng)架構”,軟件的架構最后會圍繞組織的邊界而變化(當然也有文化因素),當組織分工不合理時,會產(chǎn)生重復的建設或者沖突。

(2)服務之間的依賴,Composition vs Inheritance/Plugin

軟件之間的依賴模式,常見的有Composition 和Inheritance模式,對于local模塊/類之間的依賴還是遠程調用,都存在類似模式。

 

上圖左側是Inheritance(繼承或者是擴展模式),有四個團隊,其中一個是Framework團隊負責框架實現(xiàn),框架具有三個擴展點,這三個擴展點有三個不同的團隊實現(xiàn)插件擴展,這些插件被Framework調用,從架構上,這是一種類似于繼承的模式。

右側是組合模式(composition):底層的系統(tǒng)以API服務的方式提供接口,而上層應用或者服務通過調用這些接口來實現(xiàn)業(yè)務功能。

這兩種模式適用于不同的系統(tǒng)模型。當Framework偏向于底層、不涉及業(yè)務邏輯且相對非常穩(wěn)定時,可以采用inheritance模式,也即Framework被集成到團隊1,2,3的業(yè)務實現(xiàn)當中。例如RPC framework就是這樣的模型:RPC底層實現(xiàn)作為公共的base 代碼/SDK提供給業(yè)務使用,業(yè)務實現(xiàn)自己的RPC 方法,被framework調用,業(yè)務無需關注底層RPC實現(xiàn)的細節(jié)。因為Framework代碼被業(yè)務所依賴,因此這時業(yè)務希望Framework的代碼非常穩(wěn)定,而且盡量避免對framework層的感知,這時inheritance是一種比較合適的模型。

然而,我們要慎用Inheritance模式。Inheritance模式的常見陷阱:

(a)要避免出現(xiàn)管理倒置

即Framework層負責整個系統(tǒng)的運維(framework團隊負責代碼打包、構建、上線),那么會出現(xiàn)額外的協(xié)同復雜度,影響系統(tǒng)演進效率(設想一下如果Dubbo的團隊要求負責所有的使用Dubbo的應用的打包、發(fā)布成為一個大的應用,會是多么的低效)。

(b)要避免破壞業(yè)務邏輯流程的封閉性

Inheritance模式如果使用不當,很容易破壞上層業(yè)務的邏輯抽象完整性,也即“擴展實現(xiàn)1”這個模塊的邏輯,依賴于其調用者的內部邏輯流程甚至是內部實現(xiàn)細節(jié),這會帶來危險的耦合,破壞業(yè)務的邏輯封閉性。

如果你所在的項目采用了插件/Inheritance模式,同時又出現(xiàn)上面所說的管理倒置、破壞封閉性情況,就需要反思當前的架構的合理性。

而右側的Composition是更常用的模型:服務與服務之間通過API交互,相互解耦,業(yè)務邏輯的完整性不被破壞,同時框架/Infra的encapsulation也能保證。同時也更靈活,在這種模型下,Service 1, 2, 3 如果需要也可以產(chǎn)生相互調用。

另外《Effective Java》一書的Favor composition over inheritance有很好的分析,可以作為這個問題的補充。

(3)可測試性不足帶來的協(xié)同成本

交付給其他團隊(包括測試團隊)的代碼應該包含充分的單元測試,具備良好的封裝和接口描述,易于被集成測試的。然而因為 單測不足/模塊測試不足,帶來的集成階段的復雜度升高、失敗率和返工率的升高,都極大的增加了協(xié)同的成本。因此做好代碼的充分單元測試,并提供良好的集成測試支持,是降低協(xié)同成本提升迭代效率的關鍵。

可測試性不足,帶來協(xié)同成本升高,往往導致的破窗效應:上線越來越靠運氣,unknown unknowns越來越多。

(4)文檔

降低協(xié)同成本需要對接口/API提供清晰的、不斷保持更新一致的文檔,針對接口的場景、使用方式等給出清晰描述。這些工作需要投入,開發(fā)團隊有時不愿意投入,但是對于每一個用戶/使用方,需要依賴釘釘上的詢問、或者是依靠ATA文章(多半有PR性質或者是已經(jīng)過時,沒有及時更新,畢竟ATA不是產(chǎn)品文檔),協(xié)同成本太高,對于系統(tǒng)來說出現(xiàn)bug/使用不當?shù)膸茁蚀鬄樵黾恿恕?/p>

最好的方式:(1)代碼都公開;(2)文檔和代碼寫在一起(README.md, *.md),隨著代碼一起提交和更新,還計算代碼行數(shù),多好。

4 軟件復雜度生命周期

 

復雜度的惡化到一定程度,一定進入有諸多unknown unknown的程度。好的工程師一定要能識別這樣的狀態(tài):可以說,如果不投入力氣去做一定的重構/改造,有過多unknown unknowns的系統(tǒng),很難避免失敗的厄運了。

這張圖是要表明,軟件演進的過程,是一個“不由自主”就會滑向過于復雜而無法維護的深淵的過程。如何要避免失敗的厄運?這篇文章的篇幅不容許我們展開討論如何避免復雜度,但是首要的,對于真正重要的、長生命周期的軟件演進,我們需要做到對于復雜度增量零容忍。

5 Good enough vs Perfect

軟件領域,從效率和質量的折中,我們會提“Good enough”即可。這個理論是沒錯的。只不過現(xiàn)實中,我們極少看到“overly good”,因為過于追求perfection而影響效率的情況。大多數(shù)情況下,我們的系統(tǒng)是根本沒做到Good enough。

四 對復雜度增長的對策

每一份新的代碼的引入,都在增加系統(tǒng)的復雜度:因為每一個類或者方法的創(chuàng)建,都會有其他代碼來引用或者調用這部分代碼,因而產(chǎn)生依賴/耦合,增加系統(tǒng)的復雜度(除非之前的代碼過度復雜unncessarily complex,而通過重構可以降低復雜度),如果讀者都意識到了這個問題,并且那些識別增加復雜度的關鍵因素對于大家有所幫助,那么本文也就達到了目標。

而如何Keep it simple,是個非常大的話題,本文不會展開。對于API設計,在[5]中做了一些總結,其他的希望后續(xù)有時間能繼續(xù)總結。

有人會說,項目交付的壓力才是最重要的,不要站著說話不腰疼。實際呢?我認為絕對不是這樣。多數(shù)情況下,我們要對復雜度增長采用接近于“零容忍”的態(tài)度,避免“能用就行”,原因在于:

  • 復雜度增長帶來的風險(unknown unknowns、不可控的失敗等)往往是后知后覺的,等到問題出現(xiàn)時,往往legacy已經(jīng)形成一段時間,或者坑往往是很久以前埋的。
  • 當我們在代碼評審、設計評審時面臨一個個選擇時,每一個Hack、每一個帶來額外成本和復雜度的設計似乎都顯得沒那么有危害:就是增加了一點點復雜度而已,就是一點點風險而已。但是每一個失敗的系統(tǒng)的問題都是這樣一點點積累起來的。
  • 破窗效應Broken window:一個建筑,當有了一個破窗而不及時修補,這個建筑就會被侵入住認為是無人居住的、風雨更容易進來,更多的窗戶被人有意打破,很快整個建筑會加速破敗。這就是破窗效應,在軟件的質量控制上這個效應非常恰當。所以,Don't live with broken windows (bad designs, wrong decisions, poor code) [6]:有破窗盡快修。

零容忍,并不是不讓復雜度增長:我們都知道這是不可能的。我們需要的是盡力控制。因為進度而臨時打破窗戶也能接受,但是要盡快補上。

當然文章一開始就強調了,如果所寫的業(yè)務代碼生命周期只有幾個月,那么多半在代碼變得不可維護之前就可以下線了,那可以不用關注太多,能用就行。

最后,作為Software engineer,軟件是我們的作品,希望大家都相信:

  • 真正的工程師一定在意自己的作品:我們的作品就是我們的代碼。工匠精神是對每個工程師的要求。
  • 我們都可以帶來改變:代碼是最公平的工作場地,代碼就在那里,只要我們愿意,就能帶來變化。

Reference

[1]John Ousterhout, A Philosophy of software design

[2]Frederick Brooks, No Silver Bullet - essence and accident in software engineering

[3]Robert Martin, Clean Architecture

[4]https://medium.com/monsterculture/getting-your-software-architecture-right-89287a980f1b

[5]API設計最佳實踐思考 https://developer.aliyun.com/article/701810

[6]Andrew Hunt and David Thomas, The pragmatic programmer: from Journeyman to master

[7]https://testing.googleblog.com/2017/06/code-health-reduce-nesting-reduce.html

[8]https://en.wikipedia.org/wiki/Don%27t_repeat_yourself

[9]http://www.multunus.com/blog/2017/01/naming-the-hardest-software/

 

[10]https://martinfowler.com/bliki/TwoHardThings.html

【本文為51CTO專欄作者“阿里巴巴官方技術”原創(chuàng)稿件,轉載請聯(lián)系原作者】

 

戳這里,看該作者更多好文

 

責任編輯:武曉燕 來源: 51CTO專欄
相關推薦

2020-08-10 09:14:50

軟件測試工具技術

2020-08-11 07:45:38

軟件測試

2020-12-03 10:56:31

軟件開發(fā)反饋弧

2018-12-18 10:11:37

軟件復雜度軟件系統(tǒng)軟件開發(fā)

2021-02-21 00:18:47

惡意軟件研究職業(yè)技術

2024-04-25 08:33:25

算法時間復雜度空間復雜度

2021-01-05 10:41:42

算法時間空間

2019-01-02 05:55:30

領域驅動軟件復雜度

2009-07-09 10:45:16

C#基本概念復雜度遞歸與接口

2019-12-24 09:46:00

Linux設置密碼

2015-10-13 09:43:43

復雜度核心

2022-08-16 09:04:23

代碼圈圈復雜度節(jié)點

2020-12-30 09:20:27

代碼

2020-02-06 13:59:48

javascript算法復雜度

2022-03-02 09:53:22

計算Transforme性能

2025-09-30 01:33:00

2019-09-06 11:12:53

2014-07-01 15:49:33

數(shù)據(jù)結構

2020-06-01 08:42:11

JavaScript重構函數(shù)

2022-02-22 10:11:01

系統(tǒng)軟件架構
點贊
收藏

51CTO技術棧公眾號

日韩福利在线视频| 欧美一级黄色影院| 青青九九免费视频在线| 日韩精品色哟哟| 神马久久桃色视频| 69亚洲乱人伦| xxxxx.日韩| 亚洲一区在线播放| 午夜精品视频在线观看一区二区| 一级黄色录像大片| 亚洲视频大全| 欧美成人激情在线| av网在线播放| 激情亚洲另类图片区小说区| 欧美三级视频在线播放| 国产午夜大地久久| 黄色在线播放网站| 久久亚洲精品国产精品紫薇| 91视频免费进入| 丰满熟女人妻一区二区三| 亚洲香蕉网站| 久久精品99久久久久久久久| 91网站免费入口| 国内毛片久久| 日韩欧美一卡二卡| 五月婷婷六月丁香激情| 中文字幕成在线观看| 亚洲精品视频观看| 在线视频亚洲自拍| 欧美视频免费一区二区三区| 国产成人综合网| 国产精品男人的天堂| 久久久精品视频网站 | 精品国产成人av| 一级日韩一区在线观看| 韩国三级在线观看久| 成人av网址在线观看| 91久久国产婷婷一区二区| 欧美日韩a v| 久久精品日韩欧美| 欧美专区日韩视频| 日韩乱码一区二区| 99热精品在线| 午夜精品久久久久久久99热浪潮| a级黄色片免费看| 午夜精品一区二区三区国产| 亚洲日本成人网| 国产精品无码久久久久一区二区| 精品网站aaa| 亚洲电影中文字幕| 日韩成人av一区二区| 老牛影视av一区二区在线观看| 欧美成人a在线| 伊人影院在线观看视频| 免费精品一区二区三区在线观看| 制服.丝袜.亚洲.另类.中文| 一个色综合久久| 91视频成人| 日韩精品中文字幕在线不卡尤物| 亚洲成人激情小说| 日韩免费一级| 亚洲精品动漫久久久久| 精品人妻一区二区三区视频| 最新国产一区| 中文字幕av一区二区三区谷原希美 | 免费中文字幕在线观看| 亚洲小说区图片区| 欧美一级大片在线免费观看| 国产一级淫片a视频免费观看| 久久人人97超碰国产公开结果| 国产成人jvid在线播放| 怡春院在线视频| 国产一级精品在线| 国产欧美日韩在线播放| 青青青草原在线| 国产精品色眯眯| 四虎永久免费网站| 欧美videos另类精品| 天天影视网天天综合色在线播放| 黄色影院一级片| av在线不卡精品| 91精品国产综合久久久久久久| 久久出品必属精品| 日韩人体视频| 日韩中文字幕在线看| 毛片aaaaa| 久久精品人人| 7777奇米亚洲综合久久 | 成人91在线观看| 日韩影视精品| 女人天堂av在线播放| 欧美日韩免费一区| 欧美一级小视频| 美女一区2区| 色老头一区二区三区在线观看| 九九九久久久久| 日韩高清在线观看| 国产98在线|日韩| 黄色片视频在线观看| 亚洲精品视频一区| 9久久婷婷国产综合精品性色| 91精品久久久久久综合五月天| 亚洲人成免费电影| 久久高清无码视频| 免费高清不卡av| 精品午夜一区二区| 羞羞网站在线看| 欧美色网站导航| 粉嫩av懂色av蜜臀av分享| 99精品电影| 日韩av男人的天堂| 人妻无码中文字幕| 亚洲视频 欧洲视频| 久章草在线视频| 亚洲一级大片| 久久精品久久久久久国产 免费| 国产 日韩 欧美 在线| 国产精品乡下勾搭老头1| 日韩亚洲欧美精品| 欧美专区福利免费| 亚洲精品suv精品一区二区| 亚洲图片欧美日产| a级大片免费看| 日本天堂影院在线视频| 最新热久久免费视频| 精品99在线视频| 狠狠综合久久av一区二区| 久久久蜜桃精品| 性一交一乱一伧国产女士spa| 成人午夜一级| 亚洲欧美日韩天堂| 西西44rtwww国产精品| 成人性视频免费网站| 大地资源第二页在线观看高清版| 欧美va视频| 亚洲网址你懂得| 国产黄网在线观看| 久久久青草青青国产亚洲免观| 妺妺窝人体色777777| 亚洲精品观看| 欧美激情成人在线视频| 亚洲成人av综合| 亚洲免费在线观看| 国产乱叫456| 亚洲精品国产偷自在线观看| 成人信息集中地欧美| 免费在线看a| 欧美一区二区免费| 麻豆影视在线播放| 播五月开心婷婷综合| 野外做受又硬又粗又大视频√| 1204国产成人精品视频| 久久久视频精品| 神马午夜电影一区二区三区在线观看 | 国产日韩在线一区| 麻豆视频免费在线观看| 91精品国产综合久久精品app| 亚洲 欧美 变态 另类 综合| 国产一区二区在线影院| 热久久最新地址| 国产主播性色av福利精品一区| 97久久精品人搡人人玩| 极品白浆推特女神在线观看 | 99久久久久成人国产免费| 亚洲男人的天堂网| 欧美xxxxx精品| 久久久久久亚洲精品杨幂换脸| 日韩免费电影一区二区三区| 欧美极品在线| 欧美乱妇高清无乱码| 日韩在线观看视频网站| 欧美性高潮在线| www.日本高清视频| 国产东北露脸精品视频| 国产极品在线视频| 成人羞羞网站入口免费| 91中文字幕在线| 蜜桃麻豆av在线| 最新国产成人av网站网址麻豆| av手机免费看| 欧美日韩国产限制| 激情五月激情综合| 成人一区二区三区| 邪恶网站在线观看| 怡红院精品视频在线观看极品| 久久精品99久久| 伊人久久一区| 欧美亚洲另类制服自拍| 免费观看久久久久| 日韩电影中文字幕一区| 一级黄色大毛片| 午夜精品爽啪视频| 操她视频在线观看| 9色porny自拍视频一区二区| 国产九九在线视频| 狠狠综合久久av一区二区老牛| 欧美一区亚洲二区| 中文字幕一区日韩精品| 国产精品久久中文| а√在线天堂官网| 久久躁狠狠躁夜夜爽| 免费成人av电影| 日韩欧美国产电影| 岳乳丰满一区二区三区| 午夜久久久久久电影| 免费看一级大片| 日本一区二区免费在线| 中国极品少妇xxxx| 国产做a爰片久久毛片| 少妇高潮喷水久久久久久久久久| 香蕉久久网站| 色噜噜一区二区| 网红女主播少妇精品视频| 亚洲一区二区三区香蕉| 成人在线观看免费播放| 欧美在线视频观看免费网站| 在线黄色网页| 久久久久www| av色图一区| 亚洲欧美一区二区三区久久| 色丁香婷婷综合久久| 日韩欧美国产午夜精品| 一炮成瘾1v1高h| 欧美在线免费视屏| 亚洲天堂男人av| 欧美午夜女人视频在线| 在线免费观看毛片| 亚洲图片欧美综合| 男女免费视频网站| 亚洲乱码日产精品bd| 中国1级黄色片| 欧美高清在线一区二区| 国产sm调教视频| 久久人人爽爽爽人久久久| 182在线视频| a级高清视频欧美日韩| 制服丝袜在线第一页| 国产精品18久久久久| 久久出品必属精品| 国产成人免费在线观看| 欧美一级片在线免费观看| 国产麻豆视频一区| 性鲍视频在线观看| 国产一区二区三区观看| 亚洲精品在线网址| 国产99久久精品| 欧美一级片在线免费观看| 国产91精品入口| 无码人妻一区二区三区在线| 成人高清伦理免费影院在线观看| 中国免费黄色片| av午夜一区麻豆| 90岁老太婆乱淫| 国产亚洲欧美一区在线观看| 日本乱子伦xxxx| 国产精品夫妻自拍| 国产高清在线免费观看| 亚洲国产美国国产综合一区二区 | 色呦呦一区二区三区| 成人一二三四区| 在线不卡a资源高清| 精品美女www爽爽爽视频| 精品成人私密视频| 欧美日韩伦理片| 色偷偷av亚洲男人的天堂| 国产黄色在线观看| 久久久久久久国产精品视频| 2020日本在线视频中文字幕| 欧美在线性视频| 亚洲三级在线| 国产三区精品| 精品国产一区二区三区av片| 永久久久久久| 亚洲午夜极品| 一本久道中文无码字幕av| 久久99久久99精品免视看婷婷 | 麻豆视频久久| 国模精品一区二区三区| 国内精品久久久久久久久电影网| 一本久道久久综合| 国产综合久久| 久草福利视频在线| 国产精品18久久久久| 欧美做受高潮6| 一区二区三区高清在线| 无码人妻熟妇av又粗又大| 这里只有精品免费| 日韩福利一区二区| 久久精品国产成人| 在线视频cao| 亚洲伊人第一页| 精品中文一区| 成人免费a级片| 青青草97国产精品免费观看 | 欧美黑人经典片免费观看| 日本在线不卡视频| 免费在线观看日韩av| 久久精品亚洲国产奇米99| 2018天天弄| 欧美性猛片xxxx免费看久爱| 丁香六月天婷婷| 色爱精品视频一区| 欧美magnet| 成人午夜电影免费在线观看| 欧美一级精品| av之家在线观看| 国产一区二区三区在线观看精品| 中文字幕在线观看的网站| 亚洲精品国产品国语在线app| 黄色污污网站在线观看| 亚洲国产成人在线视频| caoporn97在线视频| 国产精品久久久久久久久| 国内精品免费| 久草视频这里只有精品| 精品一区二区三区视频| 免费福利视频网站| 欧美日韩免费看| 男人天堂av网| 毛片精品免费在线观看| 久久精品国产精品亚洲毛片| 欧美少妇一区| 99热精品在线| 波多野结衣办公室双飞| 一区二区在线观看免费| 国产乱人乱偷精品视频| 色七七影院综合| 电影天堂国产精品| 蜜桃导航-精品导航| 亚洲激情午夜| 91精品小视频| 亚洲国产一区二区a毛片| 国产激情久久久久久熟女老人av| 俺去啦;欧美日韩| 欧美黄页在线免费观看| 一区二区三区四区欧美日韩| 欧美a级一区二区| 成人性生交大片免费看无遮挡aⅴ| 欧美性猛交xxxx免费看| 欧美日韩视频精品二区| 国产不卡av在线| 国产麻豆精品久久| 欧美精品无码一区二区三区| 久久精品亚洲乱码伦伦中文 | a毛片不卡免费看片| 操人视频欧美| 亚洲精品一级| 中国黄色a级片| 91九色最新地址| jizz日韩| 91日韩在线视频| 欧美三区不卡| 日本少妇xxxx| 日本黄色一区二区| av在线电影免费观看| 国产日韩欧美在线看| 天天操综合网| 无码人妻一区二区三区免费n鬼沢| 亚洲小说欧美激情另类| 香蕉久久国产av一区二区| 欧美在线视频一区| 日本一区二区免费高清| 国产欧美一区二| 亚洲18女电影在线观看| 欧洲亚洲在线| 国产美女久久久| 国产精品久久| 三级黄色片网站| 欧美影院精品一区| 搞黄网站在线观看| 丁香五月网久久综合| 久久久久久夜| 国产精品白丝喷水在线观看| 亚洲精品一区二区三区精华液| 国产免费不卡| 超碰在线免费观看97| av一区二区久久| 曰批又黄又爽免费视频| 久久久久久亚洲精品中文字幕| 亚洲人亚洲人色久| 999久久久精品视频| 性做久久久久久免费观看欧美| 黄色美女网站在线观看| 91精品国自产在线观看| 久久国产精品亚洲77777| 人与动物性xxxx| 亚洲娇小xxxx欧美娇小| 成人久久网站| 日本中文字幕网址| 国产精品私人影院| 日本免费网站在线观看| 国产精品入口夜色视频大尺度| 欧美日韩国产免费观看| 亚洲精品91在线| 日韩欧美国产成人一区二区| 免费高清视频在线一区| 日韩 欧美 视频| 亚洲视频在线观看三级| 蜜桃视频在线播放| 丁香婷婷久久久综合精品国产|