我們將倚賴簡單實踐之間的綜效——這些實踐往往在數十年前就被當成不切實際或天真而遭到揚棄。
我們手上的原料#
這套新軟體開發紀律的原料是:
- 學開車的故事
- 四項價值——溝通、簡單、回饋、勇氣
- 那十來條原則
- 四項基本活動——寫程式、測試、傾聽、設計
我們的工作是把這四項活動結構化。不只要結構化,還得在那一長串有時相互矛盾的原則之下完成;同時還得把軟體開發的經濟表現改善到「會有人願意聽」的程度。
本章快速說明每項實踐涉及什麼;下一章〈這樣為什麼行得通〉再檢視實踐之間的連結——一項實踐的弱點,如何被其他實踐的強項覆蓋掉。
十二項實踐一覽#
- 規劃遊戲(The Planning Game)——結合業務優先序與技術估算,快速決定下一次發布的範圍。當現實超車了計畫,就更新計畫。
- 小發布(Small releases)——快速把一套簡單系統投入正式運行,然後以極短週期發布新版本。
- 隱喻(Metaphor)——用一個關於「整個系統如何運作」的簡單共享故事,引導所有開發工作。
- 簡單設計(Simple design)——系統在任何時刻都應被設計得盡可能簡單。多餘的複雜度一經發現就移除。
- 測試(Testing)——程式設計師持續撰寫單元測試,這些測試必須完美通過,開發才能繼續。客戶撰寫測試,證明功能已完成。
- 重構(Refactoring)——程式設計師在不改變行為的前提下重組系統,以移除重複、改善溝通、簡化或增加彈性。
- 結對程式設計(Pair programming)——所有正式程式碼都由兩位程式設計師在一台機器前寫成。
- 集體所有權(Collective ownership)——任何人在任何時候都能改動系統中任何地方的任何程式碼。
- 持續整合(Continuous integration)——一天整合並建置系統許多次,每完成一項任務就做一次。
- 每週 40 小時(40-hour week)——原則上每週工時不超過 40 小時。絕不連續兩週加班。
- 現場客戶(On-site customer)——團隊中納入一位真實的、活生生的使用者,全職待命回答問題。
- 編碼標準(Coding standards)——程式設計師依照一套強調「透過程式碼溝通」的規則來寫所有程式碼。
規劃遊戲#
業務人員決定四件事:
- 範圍——系統要解決多少問題,上線後才有價值?業務人員最能理解多少算不夠、多少算過頭。
- 優先序——如果一開始只能有 A 或 B,你要哪一個?業務人員判斷這件事的能力遠勝程式設計師。
- 發布的組成——要做多少或多少就夠,才能讓「有這套軟體」比「沒有」對業務更好?程式設計師對這問題的直覺可能錯得離譜。
- 發布的日期——有哪些重要日子,是「軟體(或部分軟體)存在」會造成巨大差別的?
技術人員決定四件事:
- 估算——一項功能要花多久實作?
- 後果——有些策略性業務決策,必須在知悉技術後果之後才該做。資料庫選型就是好例子:業務端也許寧可跟大公司合作而非新創,但 2 倍的生產力差距或許讓那份額外風險與不適值得承受——也或許不值得。開發端有責任解釋後果。
- 流程——工作與團隊要怎麼組織?團隊必須貼合它所處的文化,但你應該把軟體寫好,而不是保存外圍文化的不理性。
- 細部排程——在一次發布之內,哪些故事先做?程式設計師需要優先排程風險最高的開發段落的自由,以降低專案整體風險。在那個限制下,他們仍傾向把業務優先序高的東西往前挪,以減少重要故事在發布末期被砍掉的機會。
小發布#
每次發布都應該盡可能小,只包含最有價值的業務需求。但發布必須整體上說得通——你不能為了縮短發布週期,就實作半個功能然後出貨。
一次規劃一兩個月,遠好過一次規劃半年或一年。
出貨笨重軟體給客戶的公司,也許沒辦法發布得這麼頻繁。但他們仍應盡可能縮短自己的週期。
隱喻#
每個 XP 軟體專案都由單一的統攝性隱喻引導。有時這個隱喻很「天真」,例如一套合約管理系統就用合約、客戶、背書這些詞來談;有時隱喻需要一點解釋,例如說電腦應該看起來像一張桌面,或說退休金計算就像一張試算表。
這些都是隱喻——因為我們並非字面上指「系統是一張試算表」。隱喻只是幫助專案上每個人理解基本元素與它們之間的關係。
用來指稱技術實體的字詞,應該一致地取自所選的隱喻。隨著開發推進、隱喻成熟,整個團隊會從檢視這個隱喻中找到新的靈感。
把系統的「一萬公尺視角」稱為架構,問題在於架構不必然把系統推向任何凝聚感——架構就是一堆大方塊與連線。
你可以說:「架構做得爛當然爛。」但我們需要強調的是架構的目的:給每個人一個連貫的故事作為工作的框架,而且這個故事能輕易在業務與技術人員之間共享。要求一個隱喻,我們比較可能得到一個容易溝通與細化的架構。
簡單設計#
在任何時刻,軟體的正確設計就是那個滿足以下四點的設計:
- 通過所有測試
- 沒有重複的邏輯——當心隱藏的重複,例如平行的類別階層
- 陳述了所有對程式設計師重要的意圖
- 擁有盡可能少的類別與方法
系統中每一段設計,都必須以這些條件證明自己的存在正當性。
圖形設計師塔夫特(Edward Tufte)有個練習:隨你怎麼設計一張圖表,然後在「不移除任何資訊」的前提下盡量擦掉東西。當你再也擦不掉時,剩下的就是這張圖表的正確設計。
簡單設計正是如此——在不違反規則 1、2、3 的前提下,拿掉任何你拿得掉的設計元素。
這與你通常聽到的建議恰好相反:「為今天實作,為明天設計。」
如果你相信未來是不確定的,也相信你能便宜地改變主意,那麼基於臆測而放入功能就是瘋了。需要什麼,等到需要的時候再放進去。
測試#
- 程式設計師寫單元測試,好讓他們對程式運作的信心成為程式本身的一部分
- 客戶寫功能測試,好讓他們對程式運作的信心也成為程式的一部分
結果是一支隨時間越來越有信心的程式——它變得更有能力接受變更,而不是更沒有。
你不必為你寫的每一個方法都寫測試,只需為那些可能會壞掉的正式方法寫。有時候你只是想知道某件事可不可行:那就去探索半小時,確認可行,然後把程式碼扔掉,帶著測試重新開始。
重構#
實作一項功能時,程式設計師總是先問:有沒有辦法改變既有程式,讓加上這個功能變簡單? 加完功能後,程式設計師再問:現在能不能看出怎麼讓程式更簡單、同時所有測試仍然通過?這就是重構。
這意味著有時你做的工作超過「讓功能能動」的絕對必要量。但這樣工作,你才能確保下一個功能、再下一個、再下一個都能以合理的努力加上去。
如果程式設計師看到一個「一分鐘的醜辦法」能讓測試通過,也看到一個「十分鐘、但設計更簡單」的辦法,正確的選擇是花那十分鐘。所幸,即使是系統設計上相當激烈的變更,也能透過小而低風險的步驟達成。
結對程式設計#
所有正式程式碼都由兩個人看著一台機器、共用一組鍵盤與滑鼠寫成。每一對之中有兩個角色:
- 持鍵盤與滑鼠的夥伴——思考「此時此地實作這個方法的最佳方式」
- 另一位夥伴——思考得更策略性:
- 這整套做法行得通嗎?
- 還有哪些測試案例可能還過不了?
- 有沒有辦法簡化整個系統,讓當前問題直接消失?
結對是動態的。 兩個人早上結對,下午很可能各自與別人結對。如果你負責的任務落在你不熟悉的領域,你可以找一位近期有相關經驗的人來配對;不過更多時候,團隊裡任何人當夥伴都行。
集體所有權#
任何人只要看到能為程式碼任何部分增添價值的機會,就有義務在任何時候動手。
延伸:對照另外兩種所有權模型
無所有權——早年沒有人擁有任何特定的程式碼。有人想改就照自己的目的改,不管它與既有的東西合不合。
結果是混亂,尤其在物件的世界裡——這裡一行程式碼與那裡一行程式碼的關係,並不容易靜態判定。程式碼長得很快,但也很快變得不穩定。
個人所有權——為了控制這種局面,個人所有權興起了:只有官方擁有者能改動一段程式碼,其他人發現程式碼需要改,都得向擁有者提出請求。
嚴格所有權的結果是:程式碼與團隊的理解逐漸背離——因為人們不願打斷擁有者,畢竟他們需要的是現在就改,不是以後。於是程式碼保持穩定,卻沒有以應有的速度演化。然後,擁有者離職了……
在 XP 中,每個人為整個系統負責。不是每個人對每個部分都同樣熟悉,但每個人對每個部分都懂一些。如果一對正在工作、看到改善程式碼的機會,只要那讓他們的日子好過些,他們就直接動手改。
持續整合#
程式碼在幾小時之內、最多一天的開發之後就被整合並測試。
一個簡單做法是準備一台專用的整合機器:機器空著時,有程式碼要整合的一對就坐下來,載入當前發布版,載入他們的變更(檢查並解決任何衝突),然後跑測試直到全數通過(100% 正確)。
而如果我們沒辦法讓測試回到 100%,我們就該把做的東西扔掉、重新開始——因為顯然我們對那個功能的了解還不足以動手(不過現在大概夠了)。
每週 40 小時#
我想要每天早上都清爽而渴望,每天晚上都疲累而滿足。星期五,我想累得夠、也滿足得夠,好讓我對接下來兩天「去想工作以外的事」感覺良好;然後星期一,我想帶著滿腔熱火與點子回來。
這是否精確換算成「每週在工作場所 40 小時」並不特別重要。不同的人對工作的耐受度不同——有人能投入 35 個專注小時,有人 45。
但沒有人能連續好幾週每週工作 60 小時,還維持清爽、有創意、細心且自信。別那樣做。
加班是專案有嚴重問題的症狀。 XP 的規則很簡單——你不能加班第二週。加一週班,可以,衝一下、多投入些時間;但如果你星期一進來說「為了達成目標,我們又得加班了」,那你已經有了一個加更多班也解決不了的問題。
相關的議題是休假。歐洲人常常一次休兩、三、四週;美國人很少一次休超過幾天。作者說如果是他的公司,他會堅持每個人每年休兩週的假,另外還有至少一到兩週可用於較短的休息。
現場客戶#
必須有一位真實的客戶與團隊同坐,隨時能回答問題、解決爭議、設定小尺度的優先序。
所謂「真實的客戶」,指的是系統上線後真的會用它的人:如果你在建客服系統,客戶就是一位客服代表;如果你在建債券交易系統,客戶就是一位債券交易員。
對這條規則最大的反對意見是:「開發中系統的真實使用者太寶貴了,不能交給團隊。」
管理者必須決定哪一邊更有價值——是讓軟體更早、更好地運作,還是保住一兩個人的產出。如果「擁有這套系統」帶給業務的價值,還比不上「多一個人在工作」,那也許這套系統根本不該被建造。
而且,團隊裡的客戶並非做不了自己的工作——連程式設計師都沒辦法每週生出 40 小時份量的問題。現場客戶的缺點是與其他客戶在物理上分隔,但他們很可能仍有時間做自己的日常工作。
現場客戶真正的下檔風險是:他們花了數百小時協助程式設計師,然後專案被取消——那你不只損失了他們做的工作,也損失了他們原本可以做的工作。XP 盡一切可能確保專案不會失敗。
作者曾在一個專案上被勉為其難地分到一位真實客戶,而且「只能借一小段時間」。等到系統成功出貨、而且明顯有能力持續演化之後,客戶方的管理者給了他們三位真實客戶——這家公司原本可以用更多業務投入,從系統中榨出更多價值。
編碼標準#
如果你要讓這麼多程式設計師在系統的這一部分與那一部分之間切換、一天換好幾次夥伴、還不斷重構彼此的程式碼,那你絕對負擔不起好幾套不同的編碼慣例。
練習一陣子之後,應該要變得無法分辨團隊裡是誰寫了哪段程式碼。
這套標準應該:
- 要求盡可能少的工作量,並與「一次且僅此一次(Once and Only Once)」規則(不重複程式碼)一致
- 強調溝通
- 由整個團隊自願採用