.NET 的部署困境#
Joel 在本章提出了一個看似簡單但影響深遠的問題:為什麼 .NET 應用程式不能像傳統的 C++ 應用程式那樣,打包成一個小巧的獨立執行檔?
在 C++ 的世界裡,開發者可以使用靜態連結(static linking),將所有需要的函式庫直接打包進最終的執行檔中。結果就是一個幾百 KB 到幾 MB 的檔案,使用者下載後雙擊就能執行,不需要安裝任何額外的東西。
但 .NET 應用程式完全不是這樣運作的。
.NET Framework 的沉重包袱#
要執行一個 .NET 應用程式,使用者的電腦上必須先安裝 .NET Framework。這個 runtime 的大小在當時是數十 MB,對於那個年代的網路頻寬來說,這是一個巨大的障礙。
這意味著使用者的「第一次執行體驗」(first-run experience)變成了:
- 下載你的應用程式(可能只有幾百 KB)
- 發現無法執行,因為缺少 .NET Framework
- 被引導去微軟的網站下載數十 MB 的 runtime
- 等待漫長的下載和安裝過程
- 可能需要重新開機
- 最後終於可以執行你的程式
在軟體分發的世界裡,每多一個步驟,你就會流失大量的潛在使用者。那些原本願意「試試看」的人,在看到需要額外下載龐大 runtime 的時候,很多人會直接放棄。
連結器(Linker)的呼籲#
Joel 提出的解決方案在概念上非常直觀:給開發者一個連結器(linker),讓他們能夠只打包應用程式實際使用到的 .NET Framework 元件,而非要求使用者安裝整個 runtime。
這個想法的邏輯很清楚:
- 一個典型的 .NET 應用程式可能只使用了 Framework 中很小一部分的功能
- 如果能分析程式的相依性,只包含真正需要的 DLL 和類別,最終的部署檔案可以大幅縮小
- 使用者得到的是一個自包含的(self-contained)應用程式,不需要預先安裝任何東西
技術可行性分析
從技術角度來看,這個需求並非不可能實現:
- .NET 的中間語言(IL)和 metadata 結構使得靜態分析相依性在理論上是可行的
- C++ 的靜態連結器已經存在數十年,證明了這個概念的可行性
- 後來的技術發展(如 .NET Core 的 self-contained deployment 和 single-file publish)也證明了這條路線是正確的
微軟當時不提供連結器的原因,可能與他們希望 .NET Framework 成為作業系統標準元件的戰略目標有關。但這個戰略目標犧牲的是開發者和使用者的體驗。
部署摩擦扼殺了採用率#
Joel 深刻理解一個道理:軟體的成功不僅取決於功能的優劣,更取決於使用者能否順利開始使用它。部署摩擦(deployment friction)是 .NET 應用程式推廣的巨大阻力。
- 消費性軟體受害最深:企業環境中,IT 部門可以統一部署 .NET Framework;但面向一般消費者的軟體,每個使用者都必須自行處理 runtime 安裝問題
- 與競爭對手的劣勢:當你的 .NET 應用程式要求使用者先安裝 runtime,而競品只是一個可以直接執行的 EXE 檔,使用者的選擇不難預測
- 小型工具和實用程式幾乎不可能用 .NET 開發:一個 200 KB 的小工具,卻需要 20 MB 的 runtime 來支撐,這個比例顯然不合理
Joel 的這個批評後來被證明是極具先見之明的。微軟在十多年後推出 .NET Core 時,「self-contained deployment」成為了重要的賣點之一,允許開發者將 runtime 直接打包進應用程式中。這正是 Joel 當年呼籲的連結器功能的精神延續。
從使用者角度思考#
本章的底層訊息與 Joel 一貫的產品哲學一致:永遠從使用者的角度思考問題。
- 技術決策不能只考慮開發者的便利性,還要考慮最終使用者的體驗
- 部署和安裝流程是產品體驗的一部分,而非獨立於產品之外的「技術問題」
- 平台廠商(如微軟)的技術架構決策,會直接影響到所有在該平台上開發的產品的使用者體驗
在評估任何開發框架時,務必考慮部署故事(deployment story)。問自己:「從使用者下載到成功執行,需要幾個步驟?」步驟越少,採用的障礙就越低。