The Joel on Software Translation Project:每日編譯

From The Joel on Software Translation Project

Jump to: navigation, search

每日編譯(Daily Build)是你的好朋友

作者:周思博 (Joel Spolsky)
譯:Paul May 梅普華
Saturday, January 27, 2001
屬於Joel on Software, http://www.joelonsoftware.com

1982年我家人帶了一台很早期的IBM-PC到以色列,當時我們還是跑去倉庫等PC由港口進來。我不知道用了什麼方法,說服老爸買齊完整配備,有台軟碟、128 K記憶體、再加上一台點陣式印表機(印草稿用)和一台兄弟牌印刷品質的菊輪式印表機(列印時的聲音和機關槍一樣,不過更吵)。

IBM_PC.jpg

我想我們把能買的配備幾乎都買齊了:PC-DOS 1.0、75美元的技術參考手冊和完整的BIOS原始碼列表、Macro Assembler、還有能顯示完整80行和小寫字母(!)的高級IBM單色螢幕。全部加起來大概是10,000美元(含荒謬的以色列進口稅)。真是奢侈啊!

「每個人」都知道BASIC是種小孩用的電腦語言,要讓你寫出麵條般糾纏不清的程式,還會讓你的腦袋變成和乳酪一樣都是空洞。所以我們花了600大元去買總共有三片磁片的IBM Pascal。這個編譯器的第一輪編譯要用第一片磁片,第二輪要用第二片,連結器則是在第三片。我寫了一個簡單的"hello, world"程式拿來編譯,用的時間總共是8分鐘。

嗯,這時間蠻長的。我寫了一個批次檔把整個過程自動化,把時間壓到7分半。有快一點,不過當我想寫長一點的程式(比如我的名作:一定會贏我的黑白棋)時,還是得花很長的時間等編譯完成。「沒錯,」有位專業程式師告訴我:「我們通常會在辦公室放塊板子,等待編譯完成時就去做仰臥起坐。寫了幾個月程式之後我就有腹肌了。」

有一天從丹麥傳來了一個叫Compas Pascal的絕妙程式,Philippe Kahn把這隻程式買下來並且改名為Borland Turbo Pascal。Turbo Pascal有點誇張,因為他和IBM Pascal的功能基本上相同,可是連文字編輯器一起只要33K記憶體就可以執行,這實在只能說佩服。更驚人的是一隻小程式只需不到一秒就可編譯完成。這就像一家默默無名的公司推出別克LeSabre的同型車款,可是時速有一百萬英哩而且極為省油,環遊世界一圈用的油還淹不死一隻螞蟻。

突然間我的生產力就提升了許多

我就是在這時候學到REP循環的概念。REP代表「Read, Eval, Print」,是敘述lisp直譯器一直在做的事:它會讀取你的輸入,把它求出來,然後印出結果。下面顯示一個REP循環的範例:我鍵入某些東西,lisp直譯把東西讀進去,求解,然後印出結果。

REP_Loop.jpg

以稍大一點的尺度來看,你寫程式的動作其實是一種放大版的REP循環,是一種名為編輯-編譯-測試的循環。你編輯你的程式,進行編譯,測試,然後看看能不能正常運作。

這裡有個重要的觀察結果,就是寫程式時會一再重複這個循環,所以編輯-編譯-測試的循環進行得愈快,你就愈有生產力,最快時就是能瞬間完成編譯。電腦程式師想擁有真正飛快的硬體,而編譯器開發者會無所不用其極地做出超級快的編輯-編譯-測試循環,這正是電腦科學上正式的原因。Visual Basic會在輸入每行程式時進行字彙和文法分析,所以最後的編輯超級快速。而Visual C++的方法則是提供漸進編譯、預先編譯的檔頭和漸進連結。

不過當你進入擁有多個開發人員和測試人員的大團隊時,又會遇到相同的循環,不過更大更複雜。測試人員找到程式的問題並回報問題。程式師會去修正問題。測試人員要等多久才會拿到修正的程式呢?在某些開發組織中,這種開發-修正-重測的循環要花好幾個星期。表示整個組織的運作完全沒有生產力。要讓整個開發組織運作的更平順,就得專注讓開發-修正-重測的循環更緊湊。

要達到這個目的有個好方法,就是每日編譯。每日編譯是一個自動、每天、完整的編譯動作,把全部原始程式重新編譯一遍。

自動 - 因為你會用cron(在UNIX上)或是Tash Scheduler服務(在Windows上)安排每天在固定的時間編譯程式。

每天 - 更密集也可以。連續編譯更吸引人,不過由於版本管理的問題(待會就會提到)可能做不到。

完整 - 你的程式可能有很多版本。針對不同的語言的版本,不同的作業系統,可能還有分豪華版和入門版。每日編譯必須建立所有的版本。而且必須從頭建立每個檔案,不可以依靠編譯器有潛在問題的漸進重新編譯功能。

下面列出每日編譯眾多好處中的幾項:

    1. 當某個問題修好之後,測試人員可以很快就拿到新版本重測,看看問題是否真的修好了。
    2. 開發人員可以比較放心自己的修改不會破壞到要出貨的1024種版本,不必真的自己準備一套OS/2來測。
    3. 在每日編譯開始前把程式存入版本管理系統的開發人員會比較安心,因為知道自己不會因為放入某些會「破壞(break)」編譯的東西而干擾到別人。所謂破壞編譯就是讓編譯無法進行。這對整個程式團隊來說就等於Windows的藍色當機畫面,當某個程式師忘記把新增的檔案放入版本管理系統時常常出現。在他們的機器上都正常,不過等別人由版本管理系統拿程式出來編譯時,就會遇到連結錯誤,然後什麼事都不能繼續做。
    4. 行銷部門及beta測試者之類的外部人員都必須用到尚未完成的產品,這時候就可以選一個已知相當穩定的版本暫時用一陣子。
    5. 如果有維護一個存放所有每日編譯結果的檔案庫,當你發現很奇怪的新問題又搞不懂原因時,可以利用二分法搜尋過去的檔案,找出問題問題第一次出現的時間。再配合良好的版本管理,或許就能找出哪一些更動造成這個問題。
    6. 當某個測試人員回報一個程式師認為已修正的問題時,測試人員可以說在哪一版看到這個問題。然後程式師可以回頭查查自己什麼時候把修正存入,就知道是否真的修好了。

接下來是實施的方法。你需要一台每日編譯的伺服器,它可能會是你能拿到最快的電腦。然後寫一個腳本命令,由版本管理系統取出所有最新的程式碼(你有在用版本管理系統吧?),然後從頭編譯會出貨的所有版本。如果有安裝程式或設定程式,也要重新編譯。所有會出給客戶的東西都應該由每日編譯程序產生。把每次編譯的結果存在依日期編碼的專屬目錄裡。每天在固定時間執行你的腳本命令。

    1. 建立最終編譯結果的所有動作都必須由每日編譯腳本完成,這一點非常重要。由取得原始碼開始,一直到把結果放到網站上適當位置供人下載(在開發過程中當然會有一個測試伺服器),所有過程全部都包含在內。想要確保整個編譯製作過程中沒有任何步驟只有某個人知道,這也是唯一的方法。這樣子就絕對不會發生由於某人被車撞,而且只有他會做安裝程式,所以不能發行產品的鳥事。在Juno的團體中,只要知道編譯伺服器在什麼地方以及雙擊「每日編譯」的圖示,就能從頭建立一套完整編譯結果。
    2. 以下是絕不能容許的行為:在準備發行程式時發現一隻小蟲,於是你就在每日編譯伺服器上直接把它修好然後發行。每日編譯有條黃金定律:只有從完整取出程式碼開始,並經過完全重新的每日編譯所製作的程式才可以發行。
    3. 把你的編譯器的警告等級開到最高(在微軟的世界裡就是-W4,用gcc的話就是-Wall),並且設定成即使遇到最小的警告都要停止編譯。
    4. 如果每日編譯失敗,就要冒險停止整個團體的作業。全部動作都停下來重新編譯,直到問題修正為止。有時候一天之內會做很多次每日編譯動作。
    5. 你的每日編譯腳本應該用電子郵件把失敗狀況回報給整個開發團隊。用grep把"error"或"warning"記錄抓出來再放入電郵裡也不是難事。腳本也要能把狀態報告附加到大家都能看到的HTML網頁上,這樣程式師和測試人員就能很快地知道哪個編譯結果是成功的。
    6. 在微軟的Excel團隊有一個很有效的規則:破壞編譯的人必須開始負責照顧每日編譯,直到有其他新的人破壞才換手。這樣做能讓大家有強烈動機維持編譯正常運作,而且幾乎每個人都會輪流照顧編譯動作,所以大家都會知道編譯結果是怎麼產生的。
    7. 如果你的團體成員都在同一個時區工作,午餐時間會是個進行編譯的好時間。這樣子大家都在午餐前把最新的程式放入版本管理,大家吃飯時就進行編譯。等人吃完午餐回來,如果編譯失敗大家就來解決問題。只要編譯正常完成,大家就可以取出最新的版本繼續作業,不必擔心會被編譯錯誤卡住。
    8. 如果你的團體分散在兩個時間,每日編譯的時間得適當安排,以確保第一個時區的人不會卡到另一個時區。在Juno的團體中,紐約的開發人員會在下午7點把程式放入版本管理然後回家。如果他們破壞了編譯,在印度海得拉巴的團隊正要工作(大約是紐約時間下午8點)卻因此卡住一整天。於是我們開始實施兩次每日編譯,分別在兩邊下班前一小時進行,就完全解掉這個問題了。

進階閱讀:

    1. 有些針對每日編譯工具的討論
    2. 實施每日編譯非常重要,所以列入邁向高品質的12個步驟
    3. 在G. Pascal Zachary的書Showstopper裡有很多關於Windows NT團體編譯製作(每週一次)的趣事。
    4. Steve McConnell在這裡談每日編譯。

這些網頁的內容為表達個人意見。
All contents Copyright © 1999-2006 by Joel Spolsky. All Rights Reserved.


Personal tools