本書談的是軟體架構(software architecture)。架構的一種定義是「系統或流程的結構」,在這裡指的就是軟體系統的結構。而設計架構意味著「帶著目的去設計這個結構」:我們有意識地讓軟體系統滿足特定需求。

軟體需求大致分為兩類:

  • 功能性需求(functional requirements):軟體必須完成的事,為使用者創造價值。沒有功能,軟體就沒有價值。
  • 品質需求(quality requirements,又稱非功能性需求):讓軟體在使用者、開發者與利害關係人眼中被視為高品質的特性。

本書的核心主張是:在眾多品質需求中,可維護性(maintainability)是特別的一個,甚至比功能更值得優先設計。後續章節都將圍繞著「如何運用整潔架構(Clean Architecture)與六角架構(Hexagonal Architecture)提升可維護性」展開。

可維護性到底是什麼?#

品質需求的清單很長——擴展性、彈性、安全性、可靠性、模組化、效能、互通性、可測試性、成本效益等等,不勝枚舉。身為軟體架構師,我們會針對軟體最重要的品質需求來設計:高吞吐量的交易系統可能聚焦擴展性與可靠性;處理個資的系統可能聚焦安全性。

作者認為不該把可維護性與其他品質需求並列看待,因為它具有樞紐地位

  • 若軟體可維護,代表它容易修改
  • 容易修改,就代表它有彈性、多半也模組化。
  • 容易修改通常也意味著成本效益高,因為改動便宜。
  • 一旦容易修改,需要時就能演進成更具擴展性、更安全、更可靠、更高效能的系統。

作者坦言這個推論帶有一點「先射箭再畫靶」的味道——刻意把所有品質需求都連回可維護性。但核心論點是成立的:可維護的軟體,無論在功能面或非功能面,都更容易往任何方向演進。而軟體生命週期中,「改變」幾乎是必然。

可維護性使功能成為可能#

從產品角度看,最重要的是軟體帶給使用者的價值——沒有價值,使用者不付費,商業模式就不成立。所以軟體確實必須提供價值,但不該以犧牲可維護性為代價

在一個容易修改的系統裡新增功能,比在一個必須與每一行程式碼搏鬥的系統裡輕鬆愉快得多。糟糕的可維護性會讓功能變更的成本隨時間不斷攀升:

  • 變更越來越昂貴,最終讓改動變成苦差事。
  • 產品端抱怨改動成本太高,工程端則辯稱交付新功能一向比提升可維護性優先。
  • 衝突的機率隨變更成本上升而升高。

可維護性與變更成本成反比,因此也與衝突的機率成反比。換句話說,投資可維護性本身就是在投資團隊的和諧

Figure 1.1: 可維護的系統其生命週期成本低於不易維護的系統

那些「可維護性很差卻商業成功」的系統,通常落在兩種情況:一是已接近生命終點、改動本就稀少;二是背後有財力雄厚的公司願意砸錢承擔維護稅。即便如此,這些公司多半也已啟動提升可維護性的計畫,以降低維護成本。

這是否代表動工前要做大規模的前期設計(big design up front,BDUF)、回到瀑布式開發?並非如此。我們只需要做「一點點」前期設計(作者戲稱 SDUF),在軟體中埋下一顆可維護性的種子,讓架構日後更容易演進到該有的樣子。這顆種子的一部分,就是選擇一個能界定護欄的架構風格——而本書正是要協助你判斷整潔/六角架構是否適合你的情境。

可維護性帶來開發者的喜悅#

可維護性除了直接影響變更成本,還有另一項好處:它讓開發者快樂。作者用「開發者喜悅(developer joy)」來描述這種幸福感,它也常被稱為開發者體驗(developer experience)或開發者賦能(developer enablement)。

開發者喜悅與生產力之間存在雙向關聯:

  • 開發者快樂,就會做出更好的工作。
  • 做出好工作,又會讓開發者更快樂。

這個關聯已被 SPACE 開發者生產力框架所認可。SPACE 並未給出衡量生產力的簡單答案,而是提供五個面向(滿意度與幸福感、效能、活動、溝通與協作、效率與心流),讓我們有意識地挑選涵蓋各面向的指標。其中的「S」——滿意度與幸福感——正對應本章所說的開發者喜悅。

Figure 1.2: 開發者喜悅與生產力相互影響

開發者喜悅不只提升生產力,也自然帶來更好的人才留任:享受工作的開發者會留下來。可維護性正是這條鏈的源頭——系統可維護,變更所需時間更少(生產力更高),改動過程更有效率也更有成就感(喜悅更高),而喜悅進一步帶來留任。

Figure 1.3: 可維護性直接影響開發者喜悅與生產力,喜悅再影響留任

可維護性支援日常決策#

打造軟體時,我們每天都在解決問題,而多數問題不只一種解法,必須做出選擇:要不要為新功能複製這段程式碼?自己建立物件還是用依賴注入框架?用多載建構子還是用 builder?

許多決策我們甚至是無意識地做出——直覺套用過去的模式或原則:

  • 發現重複程式碼時套用 DRY(don’t repeat yourself)
  • 用依賴注入讓程式更容易測試。
  • 引入 builder 讓物件建立更簡單。

仔細想,這些知名模式的主要效果是什麼?多半是讓程式未來更容易修改,也就是更可維護。可見可維護性早已內建在我們每天自動做出的眾多決策裡。

面對更棘手、無法直接套用既有模式的抉擇時,可以用同一把尺:選擇最能讓程式未來容易修改的那個選項。不必再為不同方案糾結,挑可維護性提升最多的即可。當然這是一種通則,特定情境下正確決策也可能犧牲可維護性——但作為預設的退路,它能大幅簡化日常決策。

Figure 1.4: 可維護性影響決策

維持可維護性#

我們怎麼知道對程式碼的改動有沒有提升(或至少沒有降低)可維護性?如何長期管理可維護性?

答案是:建立並維護一個讓人容易寫出可維護程式碼的架構。好的架構讓人容易在程式庫中穿梭——修改既有功能或新增功能都輕而易舉,元件之間的依賴清楚而不糾纏。換言之,好架構提升可維護性,並進一步提升開發者喜悅、生產力、留任與決策品質。

Figure 1.5: 軟體架構影響可維護性

這個關聯意味著我們應該為「如何組織程式碼」投入一些思考:

  • 如何把程式碼檔案分組成元件?
  • 如何管理元件之間的依賴?
  • 哪些依賴是必要的,哪些應該被勸阻,好讓程式庫保持容易變更?

本書介紹的是「整潔/六角架構」的一種實作方式,但它不是解決所有軟體問題的銀彈。如同第 15 章所述,它並不適合所有類型的應用程式。建議你吸收書中的想法、動手玩玩、加以改造成自己的版本,放進工具箱,在合適的情境下取用。

每一章結尾都有一節〈這如何幫助我打造可維護的軟體?〉,總結該章重點,協助你為當前或未來專案的架構做出決策。