軟體開發是充滿不確定性的工作,而不確定性通常來自於兩大方面:
上述的不確定性在新開發專案時,往往特別明顯。
新開發專案有時候並沒有讓真實的用戶來參與,因為要等到專案上線了之後,才會得到真實的用戶。換言之,最初無論寫了多詳細的規格,這些規格全都是依賴對用戶的想象而寫的,真實用戶的意見依然充滿了不確定性。
新開發專案有時也會順勢更改一些既有專案的作法:換語言、換架構、換資料庫等等。又或者是需要開發一些前所未有的新功能、整合新的函式庫、布署到新的平台上。上述的情況都會應用到新的零組件,而新的零組件很有可能文件寫得語焉不詳、缺乏範例;組件可能有 bug ,又或是依賴於某個函式庫,會與系統的其它部分產生衝突。
仔細思考不確定性的本質,其實就是缺乏關鍵的資訊,且這種資訊往往是反饋型的資訊。換言之,如果我們可以調整開發的順序或是方式,讓重要的資訊可以更快取得,這就可以讓不確定性快速地降低。
開發新專案時,Walking Skeleton 是一種可以快速降低不確定性的作法,它的概念是:「先將帶有副作用 (IO) 的零組件都整合好,之後再來開發核心邏輯。」更進一步的 Walking Skeleton 則是還包含將一行指令布署也在最初時就一併完成。
採取這個開發順序的話,一方面由於將不確定性高的零組件整合工作提前了,系統總體含有的不確定性會在專案開啟時就快速減少。更重要的的是,一旦系統有了 UI ,非開發者的協作者就可以加入專案來給予反饋;如果還可以搭配布署,那連軟體真實的用戶或是客戶的反饋也有機會提前蒐集了。
Walking Skeleton 雖然是極為簡單的作法,卻可以讓不確定快速下降,大幅減少專案的風險。
很多系統在規畫時,會發現某些需要的模組不存在,且需要的模組在別的平台上存在。這種時候,我們就可以採用移植來做為降低不確定性的作法。
移植別的平台上的模組,可以說是快速地降低『需求方向』的不確定性。一般我們談論需求時,需求一詞常有兩個意涵:一個是直接來自真實使用者的需求。另一個意涵則是由於外在的使用者需求因而需要的合理模組介面。設計合理的模組介面其實非常難,寫在書裡的一些經典介面設計,許多都是無數前人的嘗試、失敗、改善之後才取得的成果。
採取移植的作法,可以讓不確定性集中在軟體的實作與依賴關系,大幅減少不確定性。
發現某些需要的模組不存在時,除了自己重新開發之外,也可以考慮移植。
另一方面,在軟體開發的過程之中,很多時候,我們也有第三方的函式庫可以使用,然而,第三方模組雖然可以使用,卻很難用。
這會發生在什麼情況呢?比方說,使用 Clojure 這種主要支援函數式編程的語言,當我們要用到比較少見的使用情況時,就會用到第三方函式庫,且是純 Java 的函式庫。然而,Java 函式庫的特色就是會有一大堆的型別。如果直接引用的話,就會讓 Clojure 的程式碼裡充滿了 Java 的型別。
這種時候,比較好的作法是剝皮接枝,引用 Java 的函式庫之前,先一層一層地向下剝,直到找到某一層,它還沒有將資料轉化為一個又一個的型別。將該模組的上層直接剝去,並且用 Clojure 語言去將上層直接重寫過。
用這種作法既可以利用第三方的函式庫 (減少不確定性),又可以讓第三方的函式庫不會汙染整個系統。
剝皮接枝法出自一則 Arne Brasseur 的 twitter :
Much of Clojure development is peeling back layers and layers of Java libraries to get to the bit that actually matters, then wrapping that in a tiny API layer over plain data.
這些應對不確定性的模式,其實都呼應了複雜系統學家 Stuart A. Kauffman 提出的「Adjacent Possible」(鄰近可能)概念。
Kauffman 認為,生物系統(以及人類的經濟、文化和技術系統)的演化並非隨機跳躍,而是從當前狀態,朝向其「鄰近可能」空間逐步探索與擴展。這個「鄰近可能」是基於系統當前所擁有的所有組件和配置,所能達到的下一個可能狀態的集合。
例如,在生物演化中,新的基因突變或性狀,通常是基於現有的基因和環境,而不是憑空出現的。同樣地,在軟體開發中,我們可以將當前的技術棧、團隊知識和使用者反饋,視為我們的「當前狀態」。
在這種視角下,上述的開發模式就可以這樣理解:
Walking Skeleton 是一種透過盡早建立一個可運作的基礎,來快速探索「鄰近可能」的方法。它不追求一步到位,而是先將不確定性最高的環節(I/O、部署)穩定下來,創造出一個新的、可供探索的穩定「當前狀態」。這個新的狀態讓團隊能夠更安全地探索接下來的功能,並從早期反饋中找到新的「鄰近可能」方向。
Porting 和 Peel and Wrap 則是利用外部資源來擴展「鄰近可能」的邊界。移植(Porting)是將其他領域已經驗證過的解決方案,引入到我們的「鄰近可能」空間,從而跳過漫長的探索和試錯過程。剝皮接枝(Peel and Wrap)則是在當前系統的基礎上,巧妙地整合外部組件,將其轉化為符合自身系統風格的新工具,這讓我們的系統能有效利用外部資源,同時保持內部的一致性,從而避免「鄰近可能」空間被混亂的外部依賴所污染。
總結來說,這些策略並非單純的技術實踐,而是系統性地管理不確定性,並加速在「鄰近可能」空間中有效探索的方法。它們的核心思想都是:先建立一個穩定的基石,然後以有組織、有目標的方式,逐步擴展到下一個可行的狀態,而非盲目地向未知領域邁進。
軟體開發充滿了不確定性,主要來自於使用者需求和技術挑戰。這篇文章介紹了三種應對這種不確定性的模式:
這些模式並非單純的技術技巧,而是對不確定性的系統性管理,其核心理念與複雜系統學家 Stuart A. Kauffman 提出的「鄰近可能」(Adjacent Possible)概念不謀而合。這些策略都是透過建立一個穩定的基石,然後有組織、有目標地逐步探索和擴展,最終加速在可行的範圍內找到解決方案。