引言#

軟體專業人員面臨的最重要問題是:當有人想到一個好點子,如何盡快將其交付給使用者? 本書聚焦於 build、deploy、test、release 流程——這些常被視為「邊緣」的環節,卻是實現可靠、快速、低風險軟體發佈的關鍵。

許多軟體開發方法論主要關注需求管理與開發本身,但從需求確認、設計、開發到測試完成後,如何將這些活動串聯起來,讓開發者、測試者、建置與維運人員有效協作,才是決定交付效率的關鍵。

本書的核心模式是部署管線(Deployment Pipeline)——一套自動化實作,涵蓋應用程式的 build、deploy、test 與 release 流程。每個組織的實作細節不同,但背後的原則是一致的。

Figure 1.1: The deployment pipeline

部署管線的運作方式#

對應用程式的組態、原始碼、環境或資料的任何變更,都會觸發管線的新實例。管線的初期步驟是建立二進位檔與安裝程式;後續步驟則對這些二進位檔執行一系列測試,以證明它們可以被發佈。每通過一項測試,我們對該**候選發佈版本(Release Candidate)**的信心就增加一分。若全部測試通過,即可發佈。

部署管線建立在**持續整合(Continuous Integration)**的基礎上,本質上是將持續整合的原則推展到其邏輯結論。

部署管線的三大目標#

目標說明
透明化讓所有參與者都能看到建置、部署、測試與發佈的每一個環節,促進協作
快速回饋盡早發現並解決問題
隨選部署讓團隊能透過全自動流程,將任何版本部署到任何環境

常見的發佈反模式#

軟體發佈日往往令人緊張,根源在於流程中隱藏的高風險。以下是三種極為普遍的反模式。

反模式一:手動部署軟體#

大多數現代應用程式的部署都很複雜,包含許多步驟。當這些步驟由人工逐一執行,需要在過程中做出判斷,便容易出現人為錯誤。

典型徵兆:

  • 需要撰寫大量、詳盡的部署文件來描述步驟及可能的錯誤
  • 依賴手動測試來確認應用程式是否正常運行
  • 發佈日當天頻繁打電話給開發團隊詢問問題
  • 發佈過程中需要不斷修正流程
  • 叢集中的各環境組態不一致(如不同的連線池設定、不同的檔案系統結構)
  • 發佈耗時超過幾分鐘
  • 發佈結果不可預測,經常需要回滾
  • 凌晨兩點還在螢幕前試圖讓系統運作

核心觀點: 部署應逐步走向全自動化。人類只需做兩件事——選擇版本與環境,然後按下「部署」按鈕。

為什麼自動化部署不可或缺:

  • 手動部署每次都會引入錯誤,唯一的問題是錯誤是否嚴重
  • 手動流程不可重複、不可靠,導致大量時間浪費在除錯上
  • 手動部署需要維護文件,但文件通常不完整或過時;自動化腳本本身就是最新的文件
  • 自動化腳本是透明的,促進協作;手動文件則常預設讀者的知識水準
  • 手動部署依賴特定專家,如果他休假或離職,就會陷入困境
  • 要求專家做無聊重複卻又需要高度專業的事,是確保人為錯誤的最佳方式
  • 測試手動部署流程的唯一方法就是實際執行它,既耗時又昂貴
  • 自動化流程比手動流程更可稽核——沒有什麼比一個可運作的部署腳本更具稽核性

實用建議: 自動化部署流程必須被所有人使用,且應是部署軟體的唯一途徑。使用相同的腳本部署到每個環境,這樣在正式發佈前,部署到生產環境的路徑已被演練過成百上千次。

反模式二:開發完成後才部署到類生產環境#

在這種模式下,軟體第一次被部署到類生產環境(如 staging)時,大部分開發工作已經完成。

典型問題:

  • 測試人員只在開發機器上測試過系統
  • 維運人員第一次接觸新版本是在發佈到 staging 或甚至正式環境當天
  • 類生產環境要麼太昂貴、要麼來不及準備、要麼根本沒人建立
  • 開發團隊準備的安裝程式、組態檔、資料庫遷移腳本和部署文件,全都未在類生產環境中測試過
  • 開發團隊與實際執行部署的人員之間缺乏協作

部署到 staging 時,常因文件遺漏重要步驟、腳本對環境做了錯誤假設而失敗。加上跨部門溝通不良(DBA、中介軟體團隊、Web 團隊各自為政),問題雪上加霜。更糟的是,設計階段對生產環境的錯誤假設可能已被「烘焙」進系統中(例如在開發者工作站上用檔案系統快取資料,但在叢集環境中行不通)。

常見陷阱: 發佈週期越長,開發團隊在錯誤假設上走得越遠,修正所需的時間也越長。開發環境與生產環境的差異越大,假設就越不切實際。

解決之道: 將測試、部署與發佈活動整合到開發流程中,使其成為日常工作的一部分。在一系列逐漸接近生產環境的測試環境中反覆演練,讓所有參與軟體交付的人員從專案一開始就協同工作。

反模式三:手動管理生產環境組態#

許多組織透過維運團隊手動管理生產環境組態——修改資料庫連線設定、調整執行緒池大小等,這些變更是直接在生產伺服器上執行的。

典型徵兆:

  • 已在 staging 成功部署多次,但部署到生產環境時失敗
  • 叢集中不同節點的行為不一致(如承載的負載不同)
  • 維運團隊準備環境耗時很長
  • 無法回退到系統的先前組態
  • 叢集中的伺服器非刻意地擁有不同版本的作業系統、第三方軟體或修補程式等級
  • 組態修改直接在生產系統上進行

核心觀點: 所有測試、staging 和生產環境的組態,都應透過自動化流程從版本控制系統中套用。你應該能夠以自動化方式精確地重建生產環境。不應允許對這些環境進行手動變更——唯一的變更途徑應是自動化流程。

所有變更應記錄在版本控制中,透過自動化流程傳播到生產環境。同時,若部署出錯,應能使用相同的自動化流程回滾到先前版本。

我們能做得更好嗎?#

實用建議: 軟體發佈可以且應該是低風險、頻繁、低成本、快速且可預測的流程。本書描述的所有實踐都已在大型企業專案和小型開發團隊中得到驗證。

書中提到一個真實案例:某客戶原本有一大組人員專責每次發佈,團隊花七天(包含整個週末)才能將應用程式部署到生產環境,成功率很低。在導入自動化的 build、deploy、test、release 系統後,最後一次發佈只花了 七秒鐘 將應用程式部署到生產環境,且若部署失敗,回滾也只需相同時間。

如何達成目標?#

作為軟體專業人員,我們的目標是:盡快將有用的、可運作的軟體交付給使用者。

速度與品質並重#

  • 速度至關重要:軟體未發佈就無法產生投資回報。減少週期時間(Cycle Time)——從決定做出變更到使用者可用的時間——是首要目標
  • 快速交付也是驗證假設的手段:客戶對功能的假設只有在使用者實際使用後才能驗證,因此需要建立有效的回饋迴路
  • 品質是有用性的重要面向:軟體必須適合其用途,但品質不等於完美——「完美是優秀的敵人」(伏爾泰)

為了同時達成低週期時間與高品質,我們需要進行頻繁、自動化的發佈

  • 自動化(Automated):手動流程每次執行結果不同、容易出錯、無法稽核。發佈軟體不該是一門藝術,而應是一門工程學科
  • 頻繁(Frequent):頻繁發佈使每次的差異變小,大幅降低風險,且更容易回滾。頻繁發佈也帶來更快的回饋

有效回饋的三個準則#

  1. 任何變更都必須觸發回饋流程
  2. 回饋必須盡快送達
  3. 交付團隊必須接收回饋並採取行動

每個變更都應觸發回饋流程#

一個運作中的軟體應用可以分解為四個組成部分:可執行程式碼、組態、主機環境、資料。任何一個的變更都可能改變應用程式的行為,因此都需要被驗證。

變更類型說明
程式碼變更每次原始碼變更,產生的二進位檔都必須被建置和測試。自動化建置和測試的實踐稱為持續整合(Continuous Integration)。同一份二進位檔應被重複使用於所有環境
組態變更環境間的差異應被捕捉為組態資訊,任何組態變更都應被測試
環境變更包括作業系統組態、軟體堆疊、網路組態和外部系統,整個系統都應隨之測試
資料結構變更資料結構的變更也必須被測試

回饋流程應包含的檢查:

步驟檢查項目驗證目的
1可執行程式碼的建立過程必須成功驗證語法正確
2單元測試必須通過驗證程式碼行為符合預期
3軟體應滿足品質標準如測試覆蓋率等指標
4功能驗收測試必須通過驗證業務價值的交付
5非功能性測試必須通過驗證效能、可用性、安全性等
6探索性測試與向客戶展示手動測試,由產品負責人確認功能完整性
flowchart LR
    A[程式碼變更] --> B{建置成功?}
    B -->|是| C{單元測試通過?}
    B -->|否| X[❌ 停止並修復]
    C -->|是| D{程式碼品質達標?}
    C -->|否| X
    D -->|是| E{功能驗收通過?}
    D -->|否| X
    E -->|是| F{非功能測試通過?}
    E -->|否| X
    F -->|是| G{探索性測試通過?}
    F -->|否| X
    G -->|是| H[✅ 可發布]
    G -->|否| X

回饋必須盡快送達#

快速回饋的關鍵是自動化。人工流程慢、容易出錯、不可稽核,而且無聊重複——完全不是人力的最佳運用。

提交階段(Commit Stage)的測試特性:

  • 執行速度快
  • 盡可能全面(覆蓋超過 75% 的程式碼庫)
  • 任何一個失敗代表應用程式有重大缺陷,絕對不應發佈
  • 盡可能與環境無關(不需要完全複製生產環境)

後續階段的測試特性:

  • 執行較慢,適合平行化
  • 部分失敗時,在某些情況下仍可選擇發佈
  • 應在盡可能接近生產環境的環境中執行

補充說明: 這樣的測試組織方式確保在最快、成本最低的階段就建立高度信心。未通過初期測試的候選版本不會進入後續階段,確保資源的最佳運用。

為了確保快速回饋,開發者應頻繁提交變更到版本控制系統,並透過組件化管理大型或分散式團隊。在大多數情況下,應避免分支(Branching)

交付團隊必須接收回饋並採取行動#

軟體交付過程中的所有人都必須參與回饋流程,包括開發者、測試者、維運人員、DBA、基礎設施專家和管理者。

  • 跨功能團隊應頻繁聚會,討論如何改善交付流程
  • 使用大型、可見的儀表板和通知機制確保回饋被傳遞
  • 當需要處理問題時,整個團隊應停下手邊工作,決定行動方案後再繼續

這個流程能擴展嗎?#

本書描述的技術和原則已在各種大小的真實專案中得到驗證。書中許多內容受到**精實運動(Lean Movement)**啟發——精實製造的目標是確保高品質產品的快速交付,聚焦於消除浪費和降低成本。精實並不僅適用於小型系統,它最初就是為大型組織甚至整個經濟體所創建和應用的。

效益#

採用自動化方法的首要效益是建立一個可重複、可靠且可預測的發佈流程,從而大幅縮短週期時間,更快將功能和修正交付給使用者。

賦能團隊(Empowering Teams)#

部署管線是一個拉式系統(Pull System)——測試者、維運或支援人員可以自助式地將所需版本部署到所選環境。

  • 測試者可選擇較舊版本來驗證新版本中的行為變更
  • 支援人員可部署已發佈版本來重現缺陷
  • 維運人員可選擇已知良好的建置進行災難復原演練
  • 發佈只需按下一個按鈕

團隊成員更能掌控自己的工作,工作品質提升,進而提升應用程式品質。

減少錯誤(Reducing Errors)#

這裡特別指因組態管理不當而引入生產環境的錯誤——正確的程式碼版本、正確的資料庫架構、正確的負載平衡器組態、正確的外部服務 URL 等,每一個都必須正確無誤。

常見陷阱: 現代軟體系統中的數十億位元組資訊,人類不可能手動發現所有差異。書中舉例,一個大型系統中一個第三方二進位檔的微小版本差異就導致了嚴重的生產環境錯誤,且在所有測試環境中都無法重現。

將所有可能變更的事物納入版本控制——組態檔、資料庫建立腳本、建置腳本、測試工具、甚至開發環境和作業系統組態——讓電腦確保每一個位元組都在預期位置。將組態資訊加入版本控制是巨大的進步,下一步是消除中間人,讓電腦自動套用組態。

降低壓力(Lowering Stress)#

想像你的發佈只需按下按鈕,幾分鐘甚至幾秒鐘內完成,出問題時也能在同樣短的時間內回滾。想像你頻繁發佈,因此生產環境與新版本的差異很小。如此一來,發佈風險大幅降低。

實用建議: 第一次進行自動化會很痛苦,但會越來越容易,帶來的效益幾乎難以估量。

部署彈性(Deployment Flexibility)#

在新環境中啟動應用程式應該很簡單——配置機器或虛擬映像,建立描述環境特性的組態資訊,然後使用自動化部署流程即可。書中舉例,一個設計為運行在大型異質硬體叢集上的企業系統,竟然也能在一台筆記型電腦上運行並通過所有驗收測試。

熟能生巧(Practice Makes Perfect)#

最佳策略是無論部署目標為何,都使用相同的部署方式。不應有特殊的 QA 部署策略或特殊的生產環境部署策略。每次部署都是在確認部署機制正確運作——最終的生產環境部署,實際上在每次部署到任何目標時都已被演練過。

候選發佈版本(The Release Candidate)#

程式碼的每次變更可能可以發佈,也可能不行。是 build、deploy 和 test 流程驗證了變更是否可以發佈,而非單純的猜測。這個流程給予我們遞增的信心。

Figure 1.2: Traditional view of release candidates

傳統方法在流程末端才確定候選發佈版本,需要經過漫長且昂貴的步驟。但在積極追求建置與部署自動化、搭配全面自動化測試的環境中,不需要在專案末期花時間和金錢進行冗長的手動測試。

核心觀點: 將測試延遲到開發完成後,必定會降低應用程式品質。缺陷在引入時發現並修復,成本最低。越晚發現,修復成本越高——開發者已忘記當時在做什麼,功能可能已經改變,而且通常已沒有時間修復所有缺陷。

每次提交都是潛在的發佈#

持續整合(Continuous Integration) 將頻繁整合推到邏輯極端:每次對系統的變更都進行整合。當遵循此實踐時,軟體始終處於可運作狀態。如果測試足夠全面且在足夠接近生產的環境中執行,軟體實際上始終處於可發佈狀態

補充說明: 在軟體開發中,當某件事很痛苦時,減少痛苦的方式是更頻繁地做它,而非更少。持續整合系統的工作是反證某個候選版本不適合進入生產環境——如果無法反證,它就是可發佈的。

軟體交付原則#

以下原則是作者從大量專案經驗中提煉出的不可妥協的核心原則。

建立可重複、可靠的軟體發佈流程#

發佈軟體應該很簡單,因為你已經將流程的每個環節測試過數百次。可重複性和可靠性來自兩個原則:幾乎所有事情都自動化,以及將所有需要的東西保存在版本控制中

部署軟體最終涉及三件事:

步驟項目涵蓋範圍
1配置與管理環境硬體組態、軟體、基礎設施、外部服務
2安裝正確版本的應用程式
3設定應用程式組態包括所需的資料或狀態

幾乎所有事情都自動化#

有些事情無法自動化:探索性測試、向使用者社群展示可運作的軟體、合規審批。但無法自動化的清單比許多人認為的要小得多。驗收測試、資料庫升降級、網路和防火牆組態都可以自動化。

實用建議: 不需要一次自動化所有事情。從目前的瓶頸開始,逐步自動化。

將所有東西放入版本控制#

建置、部署、測試和發佈應用程式所需的一切都應放在某種形式的版本化儲存中——需求文件、測試腳本、自動化測試案例、網路組態腳本、部署腳本、資料庫腳本、應用程式堆疊組態腳本、程式庫、工具鏈、技術文件等。

新團隊成員應能在新工作站上檢出專案的版本控制儲存庫,執行單一命令就能建置並部署應用程式到任何可存取的環境。

如果很痛苦,就更頻繁地做,並提前面對痛苦#

這是最通用的原則,也是最實用的啟發式方法:

痛點解決方式
整合很痛苦?每次有人提交就整合,從專案開始就這麼做
測試很痛苦?不要留到最後,從專案開始就持續進行
發佈很痛苦?每次通過所有自動化測試就嘗試發佈
撰寫文件很痛苦?在開發功能時就撰寫,而非留到最後

補充說明: 極限程式設計(Extreme Programming)本質上就是將此啟發式方法應用到軟體開發流程的結果。

內建品質(Build Quality In)#

此原則來自精實運動,是 W. Edwards Deming 的座右銘。缺陷越早發現,修復成本越低。 持續整合、全面自動化測試和自動化部署都是為了盡早在交付流程中捕捉缺陷。

兩個重要推論:

  1. 測試不是一個階段,更不是開發完成後才開始的階段。測試留到最後就來不及修復缺陷了
  2. 測試不僅是測試人員的責任,交付團隊中的每個人都要對應用程式品質負責

完成意味著已發佈(Done Means Released)#

功能只有在為使用者帶來價值時才算真正完成。理想情況下,「完成」意味著已發佈到生產環境。退而求其次,「完成」至少意味著已在類生產環境中成功展示給使用者代表。

  • 不存在「完成 80%」這回事——事情要麼完成,要麼沒有
  • 沒有一個人能獨自完成任何事——這需要交付團隊中多人協同合作

每個人都對交付流程負責#

在太多專案中,開發者把工作丟給測試者,測試者在發佈時把工作丟給維運團隊。出問題時,人們花在互相指責的時間和修復缺陷的時間一樣多。

核心觀點: 從新專案開始就讓所有參與交付的人聚在一起。建立一個讓所有人一目了然地看到應用程式狀態、健康度、各建置版本、通過的測試及可部署環境狀態的系統。這也是 DevOps 運動的核心原則——促進軟體交付過程中所有人的協作,以更快、更可靠地發佈有價值的軟體。

持續改善(Continuous Improvement)#

應用程式的第一次發佈只是其生命週期的開始。交付流程也必須隨之演進。

  • 團隊應定期進行回顧會議(Retrospective),反思哪些做得好、哪些做得差,討論改進方案
  • 每個改進想法應有人負責推動,並在下次會議報告進展
  • 這就是戴明循環(Deming Cycle):Plan → Do → Study → Act
  • 回饋必須跨越部門界限——只在孤島內進行回饋會導致局部優化而犧牲整體優化
flowchart LR
    P[Plan\n計畫] --> D[Do\n執行]
    D --> S[Study\n研究]
    S --> A[Act\n行動]
    A --> P

總結#

傳統上,軟體發佈是充滿壓力的時刻,被視為未經驗證的手動流程,依賴臨時的組態管理技術。壓力與手動、易錯的本質密切相關。

透過採用自動化建置、測試和部署,我們獲得:

  • 驗證變更的能力,使流程在各環境中可重現
  • 大幅消除錯誤滲入生產環境的機會
  • 更快地部署變更,為業務帶來更早的商業價值
  • 自動化系統促使我們採用其他良好實踐,如行為驅動開發和全面的組態管理
  • 更多的週末時間與家人朋友共度,更少的壓力、更高的生產力

補充說明: 書中提到一個作者參與的複雜分散式系統,包含大規模資料庫資料遷移的生產環境發佈只需 5 至 20 分鐘。而一個可比較的相關系統,同樣的流程需要 30 天。自動化對軟體發佈的速度、品質和成本有深遠的影響。