對抗軟體複雜度的戰爭

来源:https://www.cnblogs.com/88223100/archive/2022/08/23/The-war-against-software-complexity.html
-Advertisement-
Play Games

服務一個人的系統,和服務一億人的系統,複雜度有著天壤之別。本文從工程師文化、組織戰略、公司內部協作等角度來分析軟體複雜度形成的原因,並提出了一些切實可落地的解法。 ...


 

 服務一個人的系統,和服務一億人的系統,複雜度有著天壤之別。本文從工程師文化、組織戰略、公司內部協作等角度來分析軟體複雜度形成的原因,並提出了一些切實可落地的解法。

 

服務一個人的系統,和服務一億人的系統,複雜度有著天壤之別。本文從工程師文化、組織戰略、公司內部協作等角度來分析軟體複雜度形成的原因,並提出了一些切實可落地的解法。

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

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


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


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

 


02 本質複雜度和偶然複雜度
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,但是大家逐漸發現學習這些工具已經成為了一個不小的負擔。

 


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


和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。


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

 

 

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

 

 

 04 錯誤的應對方式
面對效率地不斷下降,研發團隊的管理者必須做點什麼。不幸的是,很多管理者並不明白效率的降低是由軟體複雜度的上升造成的,更沒有冷靜地去思考複雜度蔓延直至爆炸的根因是什麼,於是我們看到許多管理者其膚淺的應對方式收效甚微,甚至起到了反作用。
最常見的錯誤方式是設置一個不可更改的 Deadline,用來倒逼研發團隊交付功能。但無數經驗告訴我們,軟體研發就是在質量、範圍和時間這個三角中求取權衡。研發團隊短期可以通過加班,犧牲假期等手段來爭取一些時間(長期加班實際有百害無一利),但如果這個時間限制過於苛刻,那必然就要犧牲需求範圍和軟體質量。當需求範圍不可縮減的時候,唯一可以被犧牲的就只有質量了,這實際就意味著在很短的時間內往系統中傾瀉大量的偶然複雜度。


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


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

 


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


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

 

 

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


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


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

 


06 微觀層面的複雜度控制
正確的技術戰略能夠在巨集觀層面幫助系統控制複雜度,在微觀層面我們需要完全不同的方法。在討論方法之前我想先引用一個來自《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 語句,要人去驗證。
  • 不穩定的單元測試:代碼是好的,測試是失敗的,測試集無法被信任。
  • 耗時非常長的單元測試:運行一下要幾十分鐘或者幾小時。
  • 用代碼生成單元測試:對不起,我認為這個東西除了提升覆蓋率虛榮指標外,毫無意義。


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


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


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

 


08 系統架構對複雜度的影響
介於巨集觀的技術戰略和微觀的工程師文化之間,存在著一塊重要的決策區域,也對軟體複雜度有著關鍵的影響,我稱之為系統架構。在面對需求的時候,缺乏經驗的工程師會直接想著在自己熟悉的模塊中直接解決,而經驗豐富的工程師會先思考一下系統上下文。在《Design Docs at Google》這篇優秀的技術文檔寫作指導中,就重點提到了,設計文檔應當寫清楚系統上下文圖(sysmte-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. 警惕複雜度困局:關於軟體複雜度的思考 

 

作者 | 曉斌

本文來自博客園,作者:古道輕風,轉載請註明原文鏈接:https://www.cnblogs.com/88223100/p/The-war-against-software-complexity.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1:UEditor-plus富文本編輯器如何在vue項目中使用 備註:UEditor是由百度web前端研發部開發的所見即所得的開源富文本編輯器,由於該項目不在維護;程式員自發對其進行了維護,詳見 https://gitee.com/modstart-lib/ueditor-plus?_from=gi ...
  • 中文姓名 /^(?:[\u4e00-\u9fa5·]{2,16})$/ 英文姓名 /(^[a-zA-Z][a-zA-Z\s]{0,20}[a-zA-Z]$)/ 手機號(mobile phone)中國(嚴謹), 根據工信部2019年最新公佈的手機號段 /^(?:(?:\+|00)86)?1(?:(?: ...
  • DevCraft適用於所有.NET和JavaScript框架的功能豐富的UI組件。通過專業設計的組件和主題,構建更加美觀且現代的應用程式。 ...
  • 雖說是一個任務管理系統,但簡單地講,其實就是任務的增刪改查(CRUD)。 其中最重要的又當屬增,即創建任務,此為數據之源,刪改查都依賴於它所產生的數據。接下來就從交互設計到前端,服務端,資料庫一步步去實現任務的創建。 ...
  • 本文,將向大家介紹一種將多個 CSS 技巧運用到極致的技巧,利用純 CSS 實現拼圖游戲。 本技巧源自於 Temani Afif 的 CodePen CSS Only Puzzle game。一款完全由 CSS 實現的拼圖游戲。 我們要做的,就是將散落的圖片碎塊,複原成一幅完整的圖,像是這樣: 註意 ...
  • Vue中的插槽相信使用過Vue的小伙伴或多或少的都用過,但是你是否瞭解它全部用法呢?本篇文章就為大家帶來Vue3中插槽的全部用法來幫助大家查漏補缺。 什麼是插槽 簡單來說就是子組件中的提供給父組件使用的一個坑位,用<slot></slot> 表示,父組件可以在這個坑位中填充任何模板代碼然後子組件中< ...
  • 內聯模板 點擊打開視頻講解更加詳細 當 inline-template 這個特殊的 attribute 出現在一個子組件上時,這個組件將會使用其裡面的內容作為模板,而不是將其作為被分發的內容。這使得模板的撰寫工作更加靈活。 <my-component inline-template> <div> < ...
  • 1. MyBatis數據輸入 1.1 Mybatis總體機制概括 1.2 概念說明 註意:這裡的簡單類型不是指的基本數據類型。 1.3 單個簡單類型參數 1.3.1 Mapper介面中的抽象方法 public interface EmpMapper { /** * 通過這個方法對應Mapper配置文 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...