軟體工程的最大難題

阮一峰發表於2021-05-10

一、引言

大學有一門課程《軟體工程》,研究如何組織和管理軟體專案。

說實話,這門課不適合本科生,因為學生可能體會不到,課程到底要解決什麼問題。只有親身參與過大專案的開發,經歷過大團隊,才能感受為什麼軟體工程很重要,又很難做對。

軟體開發有一個難題,叫做"擴充套件"(scaling),即怎樣服務更多的使用者。 你有10000個併發使用者,跟你有10個併發使用者,這是完全不同的概念,哪怕功能完全相同,背後的實現是完全不一樣的。併發使用者數上升一個數量級,軟體就必須重構,大量問題隨之產生。

大專案的技術難度高,管理難度更高,而且大團隊的生產率往往很低,行動緩慢。 《軟體工程》就是研究,如何擴充套件軟體和團隊,適應大專案的需要。

國外有很多專著,研究這個問題。前些日子,我讀到一篇文章,推薦了兩本書。第一本叫做《加速:構建和擴充套件高效能技術組織》,第二本叫做《規模:生物,城市和公司的普遍法則》。

我看了這兩本書的介紹,覺得很有啟發,下面就做一些摘錄。

二、大專案的困境

一個典型的大型軟體專案,開發過程通常是下面這樣。

最開始的時候,它是一個小專案,開發人員就是兩三個人,甚至可能只有一個人。產品比較簡單,功能很有限。

第一版釋出後,拿給客戶使用,反響不錯。客戶要求的新功能,能夠很快開發出來,Bug 修補也很快,因為早期客戶往往可以與開發人員直接溝通,快速反饋。

公司於是決定投入更多人員,開發這個專案。團隊慢慢變大了,軟體開始變得複雜,開發速度逐漸變慢了,2.0 版花費的時間比預期要長一點。Bug 的修復難度開始增加。總之,新功能的開發日程變久了。

公司的自然反應是進一步擴充團隊。但是更多的新成員其實會降低其他人的生產率,一個普遍現象是團隊規模越大,每個人的平均生產率越低。

幾年以後,程式碼逐漸老化,複雜性不斷增加,專案開始停滯不前。某些極端的情況下,軟體的維護成本變得非常高昂,並且幾乎不可能進行更改。

最終,這個專案成為技術債務,等待被新專案替換。

三、為什麼大專案的開發效率低?

大專案就像一頭大象,讓人望而生畏。可是一旦需要奔跑,大象就會步履蹣跚,被獵犬遠遠甩在後頭。它快不起來的原因有兩個。

(1)程式碼複雜度

隨著程式碼量的增長,單個開發者想要理解整個程式碼庫,變得越來越困難。如果團隊超過五個人,每個人負責一個功能,那麼單個人幾乎不可能追蹤系統的所有工作進度。

當你中途加入團隊,整個專案是一個緊密耦合的大型系統,你又不理解系統的每一個工作細節。這時,你就不太敢修改以前的程式碼,因為不知道隨之而來的全部影響。

你不真正理解系統,也就不會感到自己是程式碼的主人。你會很猶豫要不要重構,過時的程式碼開始累積,技術債務就這樣出現了。長此以往,開發變得越來越不愉快和令人無法滿意,最終導致人才流失。後面接手的新人,更難於重構那些遺留下來的程式碼。

(2)團隊原因

隨著團隊成員的增加,交流成本開始指數式上升。如果整個團隊有 n 個程式設計師,為了瞭解其他人的工作,你需要跟 n - 1 個人逐一交流(口頭或者書面),那麼整個團隊的交流路徑總數就是 n * (n - 1) / 2。這意味著,交流成本的增長速度是人員增長速度的平方,團隊人數越多,協同的難度就越大。

大團隊保持扁平化管理,也會越來越困難,必須拆分成較小的群體。這時,對等的交流會被自上而下的交流所取代。團隊成員會感覺,自己從平等的利益相關者,轉變為普通的工作人員,工作動機受到了影響,責任感和主人翁意識都會淡漠。

管理層為了解決問題,會嘗試組建新團隊和新的管理架構。但是,不管怎麼做,大型組織都很難保持所有成員的積極參與。

四、解決方法:程式碼解耦

大專案的開發效率不高,把這個問題歸咎於程式設計師的技術水平低和管理不善,都是沒用的。 根本原因是軟體規模的增長,必然使得程式碼和團隊變得笨重。 除非很早就認識到問題的根據,採取緩解對策,否則前面描述的情況,遲早都會出現。

解決這個問題,要從程式碼和團隊兩方面著手。

程式碼層面的解決方法是,將軟體解耦,拆分成元件或者模組,防止各個部分緊密地耦合在一起。每個元件和模組,都可以獨立開發,通過公開的介面被其它部分呼叫。

這樣的話,就大大減輕了開發者的負擔,只需要負責自己的程式碼即可,不需要關心其他部分的實現。每個部分都可以單獨重構,不擔心影響到其他部分。

五、解決方法:團隊解耦

除了程式碼解耦,團隊層面也需要解耦,要把人員分開。

這可以參考網際網路的架構。網際網路是迄今為止最成功的大型軟體解耦例項,它之所以能夠擴充套件,是因為它由一個個獨立的節點組成。為了防止節點之間互相依賴,各個節點都遵循以下規則。

  • 遵守公開的通訊協議。
  • 不需要了解其它節點的內部實現,就可以與之通訊。
  • 節點之間不直接共享狀態,只通過通訊交換資料。
  • 每個節點單獨開發和部署。

大團隊也應該遵循類似的原則,進行解耦。

  • 每個子團隊都可以獨立運作,不依賴外部人員。
  • 子團隊內部的運作,不需要被外部知道。
  • 子團隊之間的協調,應該按照公開的協議和規則,最好能夠自動完成,避免私下協商。

六、團隊解耦的注意點

團隊解耦有一些注意點。

(1)子團隊的人數不宜過多,每個子團隊最好不要超過5個人。

(2)子團隊應該是一個小型的全功能軟體開發組織。

很多大團隊按照人員角色分組,比如架構組、開發組、DBA 組、測試組、工程組等等,這是錯誤的。這樣完全沒有解耦,還是瀑布式流程,各組之間依然互相依賴,工作時必須與別組單獨協商。而且,可能會產生利益衝突,比如,開發組希望儘快交付,而測試組希望多一點時間測試。

正確做法是按照軟體的業務功能分組,每組負責一個全流程的軟體大功能,設計、編碼、測試、部署、支援等人員都在同一組。這樣才能做到解耦,以及獨立的交付和重構。每組內部使用什麼工具、如何實現某個功能,都是自己決定,各組之間不需要共享內部細節,也不依賴別組的工作。

(3)大團隊應該保障子團隊的自主權,按照子團隊提供的功能和商業價值,進行資源分配。

(4)軟體架構師的角色很重要。

軟體架構師的關注點,不應該是團隊使用的工具和技術,而是各種服務與整個系統執行狀況之間的協議和通訊,保證程式碼和團隊可以正確解耦。

(5)程式碼解耦和團隊解耦的關係。

理想情況下,程式碼解耦與團隊解耦保持一致,形成一對一的關係,一個子團隊負責一個獨立的模組。實際運作中,一個子團隊負責幾個模組也可以,但是一個模組不能由多個子團隊來參與。

(6)通訊(模組之間的、子團隊之間的)儘量規範化,爭取做到過程簡單、文件充分,最好有規範的 API,這樣無需任何人員交流,就能建立通訊。

(完)

相關文章