時間可以說是軟體的大敵。
只要過了一小段時間,就會有無數的外力壓迫軟體系統進行修改。使用者的需求可能會改變、某個應用的套件可能會發現安全性風險,必須替換、某個依賴的第三方服務可能下線了、要滿足新的效能需求可能需要替換資料庫等等。
從一般人的視角來看,修改軟體是靠打打電腦就可以完成,理論上應該要比更換馬桶簡單的多,至少不用接水、接電、接糞管、抺水泥。然而,由於修改往往會傳播,有時一個函數的修改,因為整個系統的多個部分都依賴於該函數,所以依賴於此的部分都得跟著修改,形成了骨牌效應,進而導致修改軟體的難度遠比許多實體的工程更加困難。
我們沒有辦法讓壓迫軟體修改的外力消失,然而,我們可以設法讓修改不易傳播。仔細思考修改傳播的概念,我們會發現,所謂的傳播是指:「修改透過模組的邊界,感染到其它的模組。」
正因如此,我們在設計模組的邊界時,應為防止修改傳播而做設計。可防止修改傳播的邊界通常有兩種形式:
無論是上述哪一種,都可以稱之為鬆耦合機制。我們要防止修改傳播,以增加軟體的穩定性、減少重新學習的成本,就是要善用鬆耦合機制。
以下探討一些常見的鬆耦合機制。
當我們使用一個傳輸協定(例如 HTTP、gRPC)作為不同服務或模組之間的介面時,它提供了一個天然的隔離層。服務 A 只需要知道如何按照約定的傳輸協定與服務 B 溝通,它不需要關心服務 B 內部是如何實現的。
如果今天服務 B 因為效能問題需要從 Node.js 換成 Go 語言來重寫,只要它提供給服務 A 的傳輸協定沒有改變,那麼服務 A 就完全不需要做任何修改。這就是「對消費端而言,邊界容許替換生產端」的最佳範例。服務 B 的內部實作改變了,但對服務 A 來說,邊界(傳輸協定)是穩定的,修改沒有傳播過來。這種機制在微服務架構中尤其常見,它使得各個服務可以獨立開發、部署和擴展。
這個概念不僅限於服務之間的通訊。在單體應用程式中,介面(Interface)也可以起到同樣的作用。當一個模組(消費端)依賴於介面,而非依賴於具體的類別實作(生產端)時,你就可以在不影響消費端的情況下,自由替換底層的實作。這正是依賴注入(Dependency Injection)的結構基礎。
EDN(Extensible Data Notation)是一種可擴展的資料格式,常被用於 Clojure 生態系中。它類似於 JSON 或 XML,但最關鍵的優勢在於,它允許你用標籤(tag)來擴展資料的種類。
舉例來說,你可以在 EDN 格式中自訂一個 #date
標籤來表示日期,當傳輸資料時,會呈現類似 #date "2025-09-24"
的格式。接收端看到這個標籤,就知道這是一個日期,並可以將其還原成對應的資料型態。
這個機制完美體現了「對生產端而言,邊界容許注入新的實作」的概念。
假設你的系統今天需要處理一種新的資料型態,你不需要修改既有的架構或資料格式。你只需要在 EDN 格式中定義一個新的標籤,並在接收端增加處理這個標籤的程式碼即可。如此一來,新的資料型態就可以在不大幅改動系統架構的情況下透過微小的更動而加入,這種方式讓你可以在不大改舊系統的前提下逐步擴展功能,有效防止修改傳播。
類似的概念也出現策略模式(Strategy Pattern)裡。在這種模式中,系統的核心部分(例如支付處理模組)定義了一個介面,但具體的支付方式(如信用卡、PayPal、Apple Pay)則作為不同的實作類別。當需要增加新的支付方式時,你只需要新增一個實作類別,而無需修改核心處理模組的程式碼。
塔雷伯在《反脆弱》一書中提出,系統在面對衝擊時,可能是脆弱(Fragile)、穩健(Robust),或是反脆弱(Antifragile)。其中,反脆弱的系統不僅能承受衝擊,還能透過小規模的干擾而逐步強化。
若將軟體視為一個長期演化的系統,那麼「修改」正是它必然承受的衝擊。若設計不當,修改會快速傳播,系統因而顯得脆弱;若邊界設計得宜,修改則能在局部被吸收,甚至成為進一步強化系統的契機。
這種強化機制的核心,正是在於「錯誤是一種資訊」。這些錯誤,例如測試失敗、小型 Bug 或重構中的挑戰,提供了關於系統哪裡脆弱、什麼設計行不通的寶貴知識。因此,反脆弱性鼓勵主動擁抱這些資訊,從小錯誤中學習並優化。
反脆弱性的觀點提醒我們:一味迴避小的修改,反而容易導致大規模的崩潰。 軟體歷史上常見的「全系統重寫」,往往是因為缺乏對局部修改的容納能力,長期壓抑所累積的結果。相反地,許多抵抗修改傳播的設計,並不是一開始就完整存在,而是在一次又一次地從錯誤中獲取資訊並進行重構的過程中逐步演化出來的。
這也呼應了塔雷伯所說「時間是脆弱性的過濾器」。經過長期使用與演化仍然存活的協定或資料格式,往往比具體實作更具韌性,因為它們已通過時間的篩選。軟體工程師在技術選型上的一大智慧,正是要在「新技術發展」與「經歷時間考驗過的方案」之間取得平衡:
因此,若以反脆弱性的視角來看,修改不應只被視為風險,而更是系統成長的養分。小規模、受控的修改傳播,使得系統能夠在持續演化中逐步累積適應力;而技術選型的抉擇,也是在這個過程中不斷測試、篩選,最終留下那些能在時間裡存活的結構。
許多的軟體專案最終走向崩潰,正是因為缺乏對「修改傳播」的有效管理。本文闡述的鬆耦合機制,提供了兩條關鍵路徑來限制變動的影響範圍:
從反脆弱性的角度來看,這些機制的作用,是讓系統能夠吸收每次修改的衝擊,並將其轉化為局部優化,避免累積成最終的大規模崩潰。一個能夠長期演進的軟體系統,其核心並不在於零修改,而在於具備處理小規模修改、持續演化的能力。
因此,我們在設計模組邊界時,必須有意識地賦予其鬆耦合的特性。唯有精心的設計,才能確保系統能從不斷的變動中獲益,而非受害。