作者: 周思博 (Joel Spolsky)
譯: Chen Bin
2001年1月27日
1982年,我家定購了IBM的PC機(IBM生產的最早的個人計算機,是現代流行的標準化的個人計算機的祖宗)。我們家可能是以色列最早擁有這種PC機的幾個家庭之一。當時我們跑到了倉庫等著電腦從港口運過來。事實上,我說服我爸購買的是帶有全套附屬設備的個人計算機(譯者按:有些現在很便宜的附屬設備,那時候都是非常昂貴的),這些附屬設備包括兩個軟盤驅動器,128K內存,一個針式點陣打印機(用來快速打印草稿)和一個運轉起來發出機關槍掃射聲音的兄弟牌的雛菊輪式打印機(譯者按:原文為Daisy Wheel printer,是一種已經淘汰的打印機,原理類似于老式的機械打字機,可以產生清晰的英文字符)。
附屬的軟件也很齊全,PC-DOS 1.0(最早的PC操作系統),75美元的參考書冊,包括BIOS的完整源代碼。 一個匯編語言編譯器(Macro Assembler),非常棒的IBM單色顯示器,可以顯示80列寬的字符,而且支持小寫字母顯示。整套配置大概花了10000美元。這些錢包括以色列的荒謬的進口稅。呵呵,那時候我家可真舍得花錢啊!
因為當時“每個人”都知道BASIC是給小孩玩的語言,用這種語言只能使你寫出非結構化的垃圾代碼,然后你的的腦子也會被這種語言變成Camembert產的奶酪(Camembert cheese,法國的一種奶酪,實心,圓餅狀,灰色,有一個拳頭大小)。所以我們又花了600美元買了一個IBM公司PASCAL語言開發包,需要3張軟盤才裝的下。PASCAL編譯器運行分別需要第一號軟盤,和第二號軟盤,PASCAL鏈接器需要第三號軟盤。我寫了一個簡單的輸出文字“你好,世界”程序然后編譯鏈接這個程序,總共花了8分鐘。
嗯,8分鐘好像太長了。我寫了一個腳本程序來自動化整個過程,把時間縮減為7.5分鐘。這樣好一點了。但是我想設計一個可以玩奧賽羅的程序。(譯者按:奧賽羅原文為Othello,一種棋類游戲,規則見http://www.ugateways.com/bof4.html)這個游戲總是能打動我。我不得不花很多時間等待編譯器編譯我的程序。“就是這樣的,”一個專業程序員告訴我,“我們通常在辦公室里房放上sit-up board(譯者按:一種健身器材,可以在上面做仰臥起坐或者有氧操什么的) ,當PASCAL編譯器開始運行時,我們就開始鍛煉。我這樣編程了幾個月后,我的身材不要太棒喔!”
后來有一天,丹麥程序員寫了一個很靈的叫做Compas Pascal的程序。Philippe Kahn(Borland公司的創始人)買下了它,更名為Borland Turbo Pascal。Turbo Pascal好得簡直難以想象,因為它能做IBM Pascal能做的所有事情,但是只要33K內存。而且還額外提供一個編輯器。 這還不是最棒的。最棒的是編譯一個小程序只需要不到一秒。這就好比一個你從來沒有聽說過的公司生產了通用公司別克轎車的克隆版,這種克隆車可以每小時行駛一百萬英里,但是只消耗了一滴汽油。一只小小的螞蟻喝下這點汽油也不會撐死。
突然,我的編程效率變得高的多了
那時我開始明白了“REP循環”(Rep loop)這個概念. REP是“讀入,求值,打印(Read, Eval, Print)”的縮寫。這個概念解釋了Lisp(一種編程語言,用于人工智能)解釋器的基本原理:它“讀入”你的輸入,計算你的輸入得到結果,打印結果。下面給一個例子:我輸入一些東西,Lisp解釋器計算,然后輸出結果。
在一個稍微大點的規模上,當你寫代碼時,你也處于一個REP循環的宏版本中,這個循環就是“編碼-編譯-測試”。你編寫代碼,把代碼編譯成可執行的文件,然后測試它,看一下運行起來怎么樣。
關鍵一點是當你寫一個程序時,你的工作過程是循環的。一個循環所花時間越短,你的生產力就越高,當然最短時間不會小于編譯器運行的時間。 這就是一個程序員為什么總是要一個真正夠快的硬件而編譯器開發者們總是不斷使他們的編譯器運行更快的正式的純計算機科學角度的原因。Visual Basic的辦法是當你輸入代碼時,它就開始進行代碼的語法解析,這樣程序解釋運行時速度很快。Visual C++的辦法是增量編譯(incremental compiles),預編譯頭文件(precompiled headers)和增量鏈接(incremental linking)。
但是一個大型的團隊有多個開發人員和測試人員,你碰到了同樣的循環,可能不同點就是有更多的文檔要寫(可是這還只是草稿,天哪!)。一個測試人員發現了bug并報告,然后開發人員修復了這個bug。那么測試人員得到修正后的代碼需要多少時間?在一些軟件開發機構,這樣的報告-修正-再測試循環(Report-Fix-Retest loop)可能需要幾個禮拜。如果一個循環需要這么長的時間,通常意味著該機構生產力很低。想讓整個開發過程運轉得更平滑一點,你必須想方設法使得報告-修正-再測試循環(Report-Fix-Retest loop)花的時間更少。
一個好的辦法是每日構建(daily builds)。 每日構建意味著自動地,每天,完整地構建整個代碼樹、(譯者按:“代碼樹”,原文為source tree,意思是將整個項目源代碼的目錄,子目錄,文件的位置盡可能事先固定下來,這樣在開發過程中各個模塊間,各個文件間的相對位置都不會混亂。源代碼樹指的就是一個項目所有的已經組織好的代碼文件。通常代碼樹應該用版本控制軟件管理起來。雖然這個概念很基本,但是據我的觀察,國內還是有軟件公司在這方面做的不夠好的,所以有必要解釋一下。)
自動地 - 因為你設定代碼每天在固定的時間構建。在Unix環境下使用cron,在windows下使用“任務計劃”。
每天 - 或者更頻繁. 當然每天構建的次數越多越好啦。但是有時候構建次數還是有上限的,原因和版本控制有關系,等會兒我會談到的。
完整地 -很可能你的代碼有多個版本。多語言版本,多操作系統版本,或者高端低端版本。每日構建(daily build)需要構建所有這些版本。并且每個文件都需要從頭編譯,而不是使用編譯器的不完美的增量編譯功能。
以下是每日構建(daily build)能帶來的好處:
- 當一個bug被修正了,測試者可以很快得到最新的修正后的版本開始重新測試,以驗證bug是否真正地被修復了。
- 開發人員可以更加確定他們對代碼做的修改不會破壞1024個操作系統上的任何一個版本。驗證這一點不需要在他們的機器上安裝OS/2(IBM公司生產的PC機操作系統)。
- 那些每天將修改過的代碼導入(check in)版本控制服務器的開發人員知道,他們對一個模塊導入的修改不會拖別的開發人員的后腿。拖后腿的意思是,那些開發別的模塊的程序員使用這個修改過的模塊,出了問題,于是他們自己的模塊也沒有辦法開發下去了。每日構建則不會有人拖后腿。如果把一個開發團隊比作一臺PC機,那么團隊中的一個程序員對某個模塊的修改導致其他人無法開發別的模塊,相當于PC機發生了藍屏。當一個程序員忘記把他(她)新建立的文件導入到repository(指版本控制服務器上的代碼樹)時,這種開發過程中的“藍屏”會經常發生。因為在這個程序員自己的計算機上有這個文件,所以他(她)構建 這個程序沒有問題。但是其他程序員是從版本控制服務器上導出(check out)代碼的,由于服務器上沒有這個文件,他們遇到了鏈接錯誤(link error),無法繼續工作了。
- 外部團隊(例如市場銷售部門,進行beta測試的一些客戶)可以獲得一個比較穩定的版本,這樣對他們開展自己的工作比較有利。
- 假如你將每日構建出的二進制文件(例如一個可執行程序,一個dll等等)存檔管理,那么當你發現一個非常奇怪的,無法解決的bug時,你可以通過對這些文件進行二進制搜索(binary search)來確定什么時候這個bug第一次出現。如果有對代碼進行了完善的版本控制,你也可以找出是誰在何時對代碼進行的導入(check in)導致了這個bug。
- 當開發者修正了測試者報告的一個錯誤時,如果測試者同時報告了發現錯誤時的構建的版本,開發人員可以直接在那個版本中測試是否bug真正被修復了。(譯者按:測試者報告出現了一個bug,只是報告了一個錯誤癥狀,而錯誤的原因可能有多個,每個原因可能在不同的模塊中。前文中的方法是為了避免只修正了一個模塊中一個原因,別的模塊由于在變動,于是掩蓋了而不是修復了bug)
以下是如何進行每日構建(daily build)的具體步驟。你需要用最快的電腦建立一個每日構建服務器。寫一個腳本,可以自動從版本控制服務器中導出(check out)完整的代碼,(你當然使用版本控制,不是嗎?),然后對代碼從頭開始進行構建(build),要構建所有的版本。如果你有一個安裝打包程序,也要在腳本中自動運行。所有會賣給最終用戶的東西都要包括在構建過程中。把構建出來的版本放在各自的的目錄里,不同時間構建的相同版本也應該按日期整理好,不要相互覆蓋。每天固定的時間運行這樣的腳本。
- 最關鍵的是所有這些步驟都應該由腳本自動化完成,從導出(check out)代碼到將最終產品放在網上供用戶下載(當然在開發階段,產品是放在一臺測試服務器上的)。要保證開發過程中的任何東西的任何記錄是由文檔記錄的而不是由某個人的腦子來記錄的,這是唯一的辦法。否則你會碰到這樣的情況,產品需要發布了,可是只有Shaniqua知道如何將產品打包的,可是他剛剛被巴士撞了。在Juno公司(本文作者工作過的公司之一),要進行每日構建,你唯一需要學會的東西就是雙擊每日構建服務器桌面上的一個快捷方式。
- 如果你在發行程序的當天發現了一個小bug,沒有問題。修正它,然后重新運行每日構建腳本,現在你可以安安心心的發行程序了。當然,黃金定律是每日構建腳本應該是把所有的事情從頭做一遍,遵循這個定律就不會有什么問題。
- 將你的編譯器的警告參數設到最大(在微軟的VC中是-W4 ; 在GCC中是-Wall),當遇到任何一個最微小的警告時就應該停下來。
- 如果每日構建失敗了,可能整個開發團隊的工作會因此進行不下去。當務之急是立刻找出原因,使得每日構建能成功進行下去。某天也許你會一天運行好幾次每日構建腳本的。
- 每日構建一旦失敗,應該自動地將失敗用email通知整個團隊。提取錯誤日志中的恰當部分并包括在email正文中也不是很難。每日構建腳本也可以將當前的狀態報告整理成一個html文件,自動發布到一個所有人都可以訪問的web服務器上,這樣測試者可以很快知道那個版本的構建是成功的。
- 當我在微軟的excel團隊中工作時,我們的一個有效辦法是,誰導致每日構建(daily build)失敗,他(她)就得負責照看當日的每日構建(譯者按:微軟通常每日構建都在半夜),直到有另一個人導致構建失敗而接替他(她)。這樣做當然可以使每個開發者都小心不要因為自己代碼的錯誤破壞了構建,更重要的是團隊中的每個人都可以學會每日構建(daily build)的原理。
- 如果你的團隊在同一個時區工作,在午飯時間進行每日構建(daily build)是個不錯的主意。午飯前每個程序員導入(check in)代碼,這樣當程序員在吃飯時,構建系統在工作。當程序員吃完飯回來時,如果每日構建失敗了,所有的人也都在,可以盡快找出失敗的原因。當構建系統運作起來時,沒有人再會擔心別人導入(check in)代碼會妨礙自己的工作了。.
- 如果你的團隊同時在兩個時區工作,計劃好每日構建(daily build)的時間使得一個時區的工作不會影響另一個時區的工作。在Juno公司,紐約程序員在晚上7:00導入(check in)到版本控制服務器。如果他們的導入導致構建失敗,印度Hyderabad市(譯者按:印度科技重鎮)的程序員在紐約時間晚上8:00以后的工作幾乎無法進行下去。我們每天進行兩次每日構建(daily build),每次構建的時間都在兩地的程序員回家之前,這下就沒有問題了。
更進一步的閱讀:
- 一些關于每日構建工具的討論
- 做每日構建是很重要的,它是 獲得高質量代碼的12個步驟之一。
- Windows NT team G. Pascal Zachary關于微軟Windows NT團隊每周構建的書中有一些很有趣的東西。超級明星(譯者按:原文為showstopper,指特別受人喜愛,杰出之人或之物).
- Steve McConnell(譯者按:著名的軟件工程作家,Win2000開發組總負責人,其名著《Writing Solid Code》,《Debugging the Development Process》都有中文譯本。) 寫的關于每日構建(daily build)的文章在這里.
馬嘉楠
jianan.ma@gmail.com
posted on 2006-08-22 11:45
馬嘉楠 閱讀(499)
評論(0) 編輯 收藏