l
顯示具有 持續整合 標籤的文章。 顯示所有文章
顯示具有 持續整合 標籤的文章。 顯示所有文章

2018年1月8日 星期一

關西機場的持續改善

January 08 10:07~11:23

r1900074119sq1324ps

▲照片節錄自 https://kknews.cc/travel/22qnoz.html


去年(2017)12月Teddy到日本考察,由關西機場入境。出發前想起2016年4月賞櫻季節入境關西機場人山人海的情景,足足排隊等了快兩小時才完成入境手續,此行雖然並非賞櫻季節,但每年到日本的旅客越來越多,還是有點擔心冗長的入境等待時間影響到後續行程安排。

到達關西機場,人潮比起賞櫻季節少很多。隊伍前方排了很多韓國人,跟著排隊人龍慢慢往前移動,來到接近入境審查關卡之前,動線突然轉個彎,隊伍被引導到一個新的關卡,類似下圖所示。


▼圖片節錄自〈日本3機場祭科技新法寶 旅客審查時間「縮短33%」〉,原圖節錄自日本法務省

日本入境省查


新關卡由好幾台如下圖所示的指紋掃描與拍照機器所組成的「陣列」。一組設備有六台機器,三位操作人員,當天現場有2~3組這樣的機器。每組機器就好像有三個核心支援hyper-threading的CPU,同時間可以處理六位旅客的指紋掃描與拍照工作。


▼圖片節錄自〈日本關西機場啟用信息採集車縮短入境審查時間〉,原圖取自日本共同社

ro8000p46949r9q7011


原本入境日本是不需要指紋掃描與拍照,但因為防恐的原因在約10年所增加的手續。這些工作,本來由入境審查面試官負責,因此拉長了入境審查的時間。根據新聞報導,把指紋掃描與拍照切割出來獨立成另一關卡,有效減少33%的入境審查時間。實際上,Teddy覺得節省的時間可能不只33%,因為有些旅客,尤其是上了年紀第一次到日本的旅客,不太清楚指紋掃描與拍照的流程,所以經常出現面試官需要重複告知與重試的情況,更拉長了入境審查的時間。

***

順利入境之後,Teddy趕到關西機場二樓的JR綠色窗口準備換JR Pass順便畫位。以往這是另一個大排長龍的地方,這次發現JR綠色窗口也做了流程改善。首先,櫃台人員直接說中文,減少與旅客溝通的時間。其次,以往可以在櫃台畫多個車次指定席的座位,現在改成只能畫一次指定席,其他車次請到其他JR車站畫位。乍聽之下好像變得不方便,但其實畫位是很花時間的動作,櫃台人員畫位之後還要重複與旅客確認。藉由限制旅可一次只可畫一個車次的座位,可以加速服務旅客的時間,舒緩排隊久候的問題

***

離開關西機場,Teddy想到這不就是敏捷開發所說的把大的user story切成小的user story,或是五步驟聚焦法(請參考〈目標:簡單有效的常識管理〉、〈利用五步驟聚焦法改善你的看板團隊〉)所提到的「非瓶頸階段配合瓶頸」的改善方法嗎?

持續改善就是沒有最好,只有更好的心態。

相關相聞:

***

友藏內心獨白:好多事情可學習。

2015年9月3日 星期四

參加DevOps 2015 Day 2

Sep. 02 21:42~22:59

螢幕截圖 2015-09-02 21.43.34

 

最近沒什麼料,就偷懶一下繼續談DevOps 2015 Day 2的活動。因為Erica的推坑,Teddy老早就報名了DevOps 2015,但卻忘了把活動打到Google Calendar裡面。結果第二天下午另外又約了客戶要舉辦一個小型的workshop,所以Day 2只聽了早上的場次。

昨天用比較正面的態度來談這個活動,今天從另一個角度,談談有沒有可以改善的地方。先整理一下第二天早上聽到的內容:

  • 早上一開場09:30 - 09:50由iThome 新聞主編王宏仁先生談「DevOps:IT人的新技能、新文化」。聽完之後發現王主編想講一個觀念:「工具很重要,要選對工具。」這件事情,相信絕大多數與會者應該都很明瞭。專門安排20分鐘來鋪陳這一個概念,真的是用心良苦,讓人印象深刻。
  • 下一場是來自日本Google的Ian Lewis,演講的題目是「DevOps in the Cloud」。這原本是一場很多與會者都非常期待的演講,但聽完之後怎麼感覺這場是Google的Cloud場品介紹會。Teddy個人最大的收穫是讓英文不好的自己乖乖練習了50分鐘的英文聽力。
  • 接下來是趨勢科技的蔡宗城先生介紹「廚師與伺服器(Dancing with Chef)」。聽完之後的印象是Chef是一種「自動供裝」的工具,在DevOps的領域中可以解決自動化供裝與佈署的問題。也可以搭配其他工具設定一些條件,自動依據系統的使用狀況,動態增減提供服務的計算資源。蔡先生原本準備了幾個現場操作的範例,只可惜因為網路太慢,展示的過程一波三折,稍微影響了演講的流暢度。
  • 早上最後一場演講是來自Yahoo 亞太區產品研發工程部的李卿澄先生,分享題目是「Yahoo行動App開發在持續整合與持續交付的經驗分享」。演講內容介紹李先生所參與的App開發所使用的build pipeline(或稱為continuous delivery pipeline亦可)。李先生看的出來也是屬於「搞笑型」的工程師,三不五時安排一些冷笑話。演講的內容屬與概念性的介紹,把講者在工作上所採用的CD流程講得很清楚,技術的議題談的比較少。

***

第二天下午的議程有比較多的工具介紹,很可惜Teddy因為自己疏忽無法參加。總的來說,這1.5天的活動,Teddy有一點點小建議與感想:

  • 不少講者在演講開始都介紹DevOps或是CI/CD的概念,有點「duplicated code」的感覺,聽到後來好想要按下快轉按鍵。
  • 第一天上午來自日本樂天的直井和久先生,雖然英文不是他的母語,但可以感受到他有花時間認真準備,令人感動。很可惜在QA的時候,與會者所問的問題,直井和久先生似乎無法當場用英文回答。台灣懂日文的朋友應該很多,如果主辦單位可以幫忙安排一位日文翻譯,讓直井和久先生用日文回答問題,相信整場演講的收尾可以好很多,也可以減少講者的尷尬。
  • 有些場次與會者問了比較技術性的問題,但演講者對於所介紹的內容,有些技術細節並不清楚(演講者可能是管理職,或是這些技術細節是由公司其他部門的人負責)。算是小小的遺憾。
  • 現場的無線網路真的很差。這不是台大的場地嗎,不懂為什麼無線網路很不穩定(而且也沒有中華電信的4G網路)。

***

活動第一天Teddy依稀聽到朋友說:「去參加新加坡的DevOps應該會比較好」,身為台灣人聽了之後有點「淡淡的哀愁」。參加了1.5天的活動還是有不少收穫,但整體來說似乎沒有聽到什麼非常特別令人難以忘懷的新體驗。整個活動的宣傳網頁搞得那麼盛大,實際的「用戶體驗」有點小落差。

講這麼多,有人願意舉辦活動還是應該給予正面鼓勵。希望像iThome這種專業的資訊出版單位也能夠持續舉辦好的活動。至於活動內容如何,觀眾的眼睛是雪亮的,與會者自有公評,也不必被像Teddy這種無聊人士的意見給影響。

***

友藏內心獨白:這一篇應該歸類到「麥甲我蓋布袋」才對。

2015年9月2日 星期三

參加DevOps 2015 Day 1

Sep. 01 20:08~20:52

螢幕截圖 2015-09-01 20.50.12

 

今天去參加iThome舉辦的DevOps 2015活動。以往參加類似的活動,心中經常會有一種「又槓龜」的感覺,導致於無心仔細聆聽講者的演講。在今天的活動中,原本心中的小惡魔又跑出來:「噯呀,怎麼好像沒有聽到很令人驚艷的內容,都是基本的觀念介紹居多。」但突然想到自己前幾天寫的〈養貓之後〉,在最後的幾句話:

相信大家都聽過這樣的說法:「滿的杯子是裝不了水的。」如果不先把水倒掉一些,何必又跑去飲水機裝水呢?

對啊,說的都比做的簡單,自己還不是一樣沒把水先倒掉。於是慢慢調整自己的心態,試著不要過度批評講者演講內容那些自己已知的部分,而是要把焦點放在自己未知的部分。整天活動下來,倒也是覺得頗有收穫。

***

今天突然有一種感覺,講者不管說的內容是什麼,其實是在某一個特定context(情境)之下,描述一個自身經驗的故事給聽眾知道。這個經驗也許無法直接解決每個聽眾內心的疑惑,但這不是重點。身為聽眾,該思考的是這個故事給自己的啟發是什麼?講者的context和自己的context有何異同之處?如果要做到故事中哪種美麗境界,我還缺少些什麼?應該要如何努力以達到這種境界?又或者是這種境界根本不是我要的,應該速速逃離,遠離一切苦厄 XD。

DevOps合起來雖然只有一個字,但拆開之後各自都是都是一個複雜的領域。Teddy相信合併之後只會讓問題更複雜,而非更簡單,所以要學的東西也更多。如果變得更複雜,為什麼要自找麻煩?因為這樣可以讓交付價值的工作流更加順暢,減少交付時間(lead time),讓企業變得更敏捷(靈活)。所以提升自己與組織的能力,以便解決更複雜的問題,也就等於提升競爭力。你做的到,別人做不到,也是一種競爭優勢。

***

今天聽了很多工具,看別人用起來好像很神。有為者亦若是,但光聽不動手,名詞永遠都只是名詞。相信任何一位認真於此道的朋友,終會達到講者所體會的那種經驗。

***

友藏內心獨白:工具真的好多啊…Orz。

2014年11月17日 星期一

JCConf Taiwan 2014一日遊

Nov. 16 10:09~11:54

螢幕截圖 2014-11-16 11.37.38

廣告時間的一心二用XD。

 

Java Community Conference Taiwan(JCConf)是由社群所主辦的Java開發者年會,今年第一次舉辦。前一陣子在網路上看到議程,有幾場演講的主題還蠻有趣的,當下就報名參加。上禮拜六是活動舉辦日,一早來到會議地點中央研究院人文社會科學館,場地很棒,很適合大型的研討會活動。

很久沒參加Java研討會,JDK8也出了一陣子都沒時間去研究,剛好這次有安排講者介紹「JDK8 與模式」,去吸收一下別人的日月精華。演講內容很精彩,唯一小遺憾,程式碼打在大會的大型投影布幕上看的不是很清楚,只能從講者的口述來拼湊程式碼內容。

第二場演講是來自阿里巴巴的林昊分享 「Java 常見問題排查方法」,但講者因故無法到場改由他的同事費輝代打。雖然代打者用很平鋪直敘且毫不花俏的方式解釋了投影片的內容,但是可以感受到他們在排除Java應用程式問題上有很深厚的經驗,很有料。

兩場主題演講之後,接下來活動分成三個track。Teddy參加的是「EventBus and Reactive Programming on Android」,講者概念性介紹EventBus在Android上的應用以及所要解決的問題,算是比較輕鬆且增廣見聞的一個演講。這場演講提早五分鐘結束,讓Teddy找到一個好位置吃便當微笑

***

中午12:35之後有兩場快講,先聽了「Gradle 起步走: 以 CLI Application 為例 」。Gradle是一個類似Ant與Maven的建構工具,講者用深入淺出的方式快速介紹Gradle的概念以及如何用Gradle打包一個文字模式應用程式。接著聽了「Cassandra 2.1 簡介」,Cassandra是一種NoSQL資料庫,聽完之後只記得這一點,其他的內容忘得差不多了挑眉質疑

***

快講結束之後開始下午的議程,首先參加「淺談 Geb 網站自動化測試」,Geb是以Groovy所開發的測試工具,底層可以採用Selenium的WebDriver來測試網頁應用程式。這場演講Teddy非常喜歡,講者口條清楚而且按部就班、深入淺出介紹Geb,從一開始如何用Geb測試簡單的網頁、讀取網頁中的元素,到利用Groovy語言的特性,採用Given、When、Then的格式將驗收測試的規格直接寫在測試案例中。講者提到他正在撰寫一本有關測試的書,相當值得期待。

接著Teddy聽了「Apache Kafka: A high-throughput distributed messaging system」,這場的講者與中午快講介紹「Gradle 起步走: 以 CLI Application 為例 」是同一位。如果有學過分散式作業系統的鄉民,會對於這場演講覺得很有意思。以前念書的時候有一陣子Teddy對於分散式系統很有興趣,當時花了一點時間研究JavaSpaces,可惜這個技術沒紅。講者在這場演講中把Kafka的特性與傳統message queue的差別介紹得很清楚,蠻不錯的。

***

接著聽了「Modern Design Pattern」,講者介紹MVC(與其他兩種變形)、RESTful和CQRS(Command Query Responsibility Segregation)。雖然講者對於前兩種pattern都舉了範例,也當場執行了這些範例,但總覺得還少了些什麼沒說清楚。例如,為什麼要把這三種pattern擺在一起稱為「Modern Design Pattern」?它們是彼此競爭、合作還是沒什麼關係只是剛好可以講50分鐘的三種pattern?為什麼講題要用「Modern」這個字?MVC不用提了,幾十年前的Smalltalk時代就有了,RESTful出現也超過10年以上。至於CQRS大家比較沒聽過,看起來好像比較新一點。但區分Command和Query的觀念,至少早在10幾將近20年前Bertrand Meyer在《Object-Oriented Software Construction》這本書中就提過了,雖然和CQRS pattern所提到的應用context不見得完全一樣,但背後的精神Teddy相信應該是有許多共同點。

GoF的23個pattern出版至今剛好20年,MVC的出現超過20年,RESTful和CQRS也都分別有10幾到快20年的歷史。從時間點的角度來看,「Modern」這個字出現在標題,有點「標題殺人法」的嫌疑。

***

最後一場聽了「Docker,最接近 "Build Once, Run Anywhere" 的輕量級虛擬技術」,之前在C. C. Agile聽過「小蜜蜂」介紹過Docker,以前在開發軟體的時候也經常遇到測試環境配置與虛擬化的問題,所以對這個主題很有興趣。講者善用比喻的方法說明Docker所要解決的問題,以及如何評量Docker與傳統VM的差異。講者花了很多時間準備了很豐富的demo,可惜因為環境設定還有時間因素所以最後一個demo來不及展示。

***

雖然是由社群所主辦的Java技術研討會,但內容的深度與廣度都很洽當。大型活動有些小雷在所難免,一天下來學習到許多新知,相當值得參加的一個活動。

***

友藏內心獨白:有料比花俏或硬擠出來的笑點來的重要。

2013年9月12日 星期四

先Hold住,再求好

Sept. 10 21:14~21:40

image

 

軟體開發團隊若是有使用持續整合系統的習慣,久了之後自然會想在續整合系統上面掛上一些靜態程式碼檢查的工具,例如PMD、Checkstyle、Findbugs。使用這些工具固然可以找出一些程式碼中潛在的問題,但是實務上這些工具也經常會發出「假警報」,也就是說找出「不是問題的問題」。這還不打緊,這種工具一使用下去,如果「火力全開」(打開所有檢查規則),找出來的問題數量經常多到嚇人,少則數十個,多則數千個都可能。

俗話說「法不責眾」,用工具找出來的「嫌疑犯」那麼的多,哪有時間一一去釐清。所以雖然這些靜態程式碼檢查的工具經常被提及,但實務上要真正派上用場卻還是需要動點腦筋。

前幾天讀了一本對岸的書《精益軟件度量》,書中提到一個做法,還算有點意思,在此介紹給鄉民們參考。

***

方法其實很簡單,就是「不論找到的問題有多少個,至少先確定這些問題的數量不會增加」。簡而言之,就是要先hold住現場狀況。假設鄉民們用Findbugs找出自己的系統中有500個潛在的問題,團隊暫時沒時間也不想去處理。沒關係,但日後不管是誰簽入(check-in)程式碼到版控系統中,要確定Findbugs所找出的問題不可以超過500這條基準線。也就是說,不要讓問題繼續惡化下去。

如果鄉民們簽入的程式碼會讓Findbugs找出2個新的問題,但是你又不想處理這2個問題,那怎麼辦?很簡單,你可以選擇去修正原本程式碼中的那500個問題,只要改掉2個,這樣一增一減有問題的總數還是500,你便可以把程式碼簽入到版控系統中。

***

Teddy以前也用了好幾年的Findbugs,經常也讓那些為數眾多的「嫌疑犯」搞得很不開心。雖然一有機會便會安排時間去檢視這些Findbugs找出的問題,陸陸續續也把「嫌疑犯」的數量降低了不少。但經常又因為有人簽入新的程式碼,嫌疑犯」的數量又默默地增加。

所以,不管有沒有安排時間去檢視靜態程式碼工具所找出的問題,確保新增加的程式碼不會讓既存系統的品質變得更糟,也不失為一種使用「軟體度量」的方法。

***

友藏內心獨白:這樣也行耶。

2013年6月12日 星期三

顧問成功的秘密:自己加顆蛋,味道會更好

May 22 23:11~ May 23 00:50

image

 

有位客戶找Teddy做他們導入持續整合(continuous integration,CI)的顧問,說起持續整合Teddy從2003年做到現在也累積了10年的經驗,這樣的顧問案對Teddy而言算是很輕易的工作。某天客戶要求Teddy去幫他們安裝Jenkins與設定Android App的建構專案,正所謂「君子 顧問動口不動手」,Teddy之前已經跟客戶討論過CI的系統規劃與執行方法,至於「執行細節」應該由客戶自己動手完成。這種做法並不是要推托責任,而是《顧問成功的秘密》書中有寫,讓客戶自己動手解決問題,這樣新的制度才可以在客戶端生根,日後顧問離開之後才會持續下去

但是,有一次Teddy不小心脫口說出「如果你不會裝我可以找人來幫你們安裝」這句大逆不道的話,因此對於客戶的要求Teddy也不好拒絕,但是心中暗自盤算要找機會跟客戶溝通,請他們要投入時間自己動手。

***

出發當日Teddy找了位「小幫手」一起到客戶端去協助搞定客戶的需求,原本Teddy以為客戶把我們帶到電腦前面之後,就會離開去做自己的事,等我們把事情做完之後才過來「驗收」。沒想到客戶在我們來之前已經先把Jenkins裝好,也安裝了一個用來當作遠端建構環境的VM(虛擬機器),並且在Jenkins上設定好一個建構專案(Jenkins稱之為Job)。

我們先在會議室與客戶討論今天要執行的工作,只花了10分鐘就達成共識。接著一行人來到電腦前面,Teddy找來的「小幫手」與客戶坐在電腦前面,在接下來的過程中滑鼠與鍵盤在他們兩人的手上傳來傳去,形成「pair programming」的陣仗,只不過工作的內容不是在寫程式,而是設定CI。至於Teddy則是坐在他們兩位的後方,全程監督…不對,是全程參與。疑,這樣算起來應該不是2P,而是3P才對挑眉質疑

客戶首先拿出他事先裝好的「共用VM」讓我們使用,省去安裝作業系統的時間。接下來「小幫手」在VM上面安裝JDK、Ant、Android SDK並設定好環境變數。這個過程中出現了幾個因為權限所造成的小問題,也都在三方的合作之下算是順利的解決。在下載Android SDK的同時,我們把VM設定成Jenkins的遠端建構環境,然後在Jenkins上面新增Android App專案。接下來確定:

  • 遠端VM可以連到Jenkins
  • 在Jenkins上手動執行建構專案之後,遠端VM可以成功從版控系統下載App原始碼。
  • 透過Android SDK產生一個build.xml的Ant檔案,使用這個檔案來產生APK檔。
  • 最後,確定這個APK檔可以被回傳到Jenkins上面。

整個過程花了兩小時,比Teddy原先預計的時間快了一半。會這麼順利除了有Teddy帶去的得力小幫手協助,客戶自己投入時間親自動手參與也是很重要的一個因素很棒。安裝與設定Jenkins本身並不是重點,重點是在這個過程中因為有了客戶的參與,不只讓CI的導入可以有進度,而且因為pair programming(pair learning)的關係,客戶在短時間之內也學會(而且不容易忘記)如何使用CI的許多技巧。

***

最近讀了溫伯格寫的《顧問成功的秘密》,書中提到顧問不應該斷絕客戶親手解決他們自身問題的機會。溫伯格舉了一個例子,美國的家庭主婦很喜歡自己做蛋糕,有廠商推出「蛋糕粉」這種產品,只要加水然後放進烤箱,就可以做好一個蛋糕。但是這樣的產品卻乏人問津,因為如果烤蛋糕這麼偉大的工作到頭來變成「只要加水就好」,會讓家庭主婦身為「蛋糕師傅」的角色與重要性受到嚴重的挑戰與貶低。

後來有人將「使用蛋糕粉製作蛋糕」這件事增加了一點難度,用這種新的「蛋糕粉」做蛋糕,必須還要額外「打個蛋」。就因為「打個蛋」這個動作,讓家庭主婦與「自己動手做蛋糕」這件事產生了連結。

小孩:媽媽,這是妳自己親手做的蛋糕嗎?

媽媽:傻孩子,這當然是媽媽克服萬難自己親手做的愛心蛋糕啊。

***

寫到這裡突然想到,真的耶,泡麵加了個蛋之後,感覺就完全不一樣了耶。

***

友藏內心獨白:H7N9流行期間,加蛋記得要煮熟喔挑眉質疑

2012年12月11日 星期二

對付時好時壞的測試案例(6):Resource Leaks

Dec. 10 15:17~15:54

761067903184999

先打個廣告,12月份的C. C. Agile活動即將於12月13日(禮拜四)舉辦,還剩下少數幾個名額,有興趣參加的鄉民們請多加把握。這裡是報名網址:http://www.accupass.com/go/CCAgileSprint04

 

Resource Leak

本系列最後一集談的是第五種造成測試案例時好時壞的原因:Resource Leak(資源洩漏)。這類的問題翻成白話文就是說:資源用完沒有歸還。在軟體世界中,最常忘記歸還的資源就是記憶體,其他還包括資料庫連線、網路連線、開啟中的檔案、磁碟空間等等。

如果鄉民們的程式存在著Resource Leak的問題,就會造成測試案例時好時壞。因為這類的問題在「資源被用光之前」程式執行起來都很正常,所以在大部分的情況之下測試案例都會通過。換句話說,這類的問題通常不容易被找到。

怎麼辦

要處裡Resource Leak是一件不容易的事,Martin Fowler的建議是:限制可用資源的大小或容量,如此一來如果程式中有Resource Leak的問題,便可以增加測試失敗的機會。舉個例子,假設鄉民們的程式需要連線資料庫,但是有時候在系統跑了幾天之後,會發生連線失敗的錯誤。這看起來很像是一個Resource Leak的問題。鄉民們可以嘗試:

  1. 將連線資料庫的程式改用connection pool。
  2. 在測試程式中,將connection pool的大小設很小,例如2。
  3. 開始執行測試程式,因為connection pool的大小被設定為一個很小的值,因此如果系統中有忘記釋放connection的程式,就比較容易被暴露出來(迷之音:使用connection pool不是會自動管理connection嗎?挑眉質疑)。

再舉一個Martin Fowler提到的例子,假設鄉民們的系統會使用到很多暫存檔,這些檔案使用完畢之後需要主動刪除以免佔用硬碟空間。在測試程式中,鄉民們可以讓產生暫存檔的程式永遠都產生相同的檔案,這樣一來如果程式中忘記刪除暫存檔,則會立刻就造成測試案例失敗。

***

友藏內心獨白:Resource Leak真的是很難找到的一種bug啊。

2012年12月5日 星期三

對付時好時壞的測試案例(5):Time

Nov. 27 21:02~22:04

image

 

Time

第四種造成測試案例時好時壞的原因稱之為Time(時間),相對於前三種原因,因為時間所造成的測試案例錯誤發生機率較少,但是這種問題一旦發生有時候不太容易被發現或是不太容易解決。舉個例子,有些人會利用getTime()這樣的函數來產生ID或是序號。在某些電腦上,這種方式所產生的ID永遠都不會重複。但是相同的程式拿到速度比較快的電腦中,便有可能產生重複的ID(因為電腦跑太快了啊,連續呼叫兩次getTime()可能會傳回相同的值。這種事情,Teddy也曾經遇過)。

依據Martin Fowler的建議,程式裡面最好不要直接呼叫使用系統時鐘(system clock)的函數。因為如果直接去呼叫system clock函數,每次呼叫所得到的值都會不同,這樣的程式是很難被測試的。舉個例子,假設鄉民們的程式有一個功能需要列出「這禮拜的營業額」。鄉民們可以先在資料庫中建立好若干筆交易資料,但是現在問題來了,如果在程式中直接呼叫時間函數取得「這禮拜」的時間,哪麼每個禮拜執行這隻程式所傳回的「這禮拜」都會不一樣,因此每次得到的「這禮拜的營業額」也會不同。

看到這邊鄉民們可能會想:「我可以每次執行測試案例的時候,重新產生一次這禮拜營業額的假資料啊,這樣子測試案例就不會有問題了」。這也是一種方法,但是如果我們可以只產生一次假資料,然後在執行「這禮拜的營業額」的測試案例的可以,可以inject(注入)一個指定的時間到測試案例中,這樣子就可以避免每次都需要重新產生新的測試資料。

怎麼做

解決這個問題最簡單的作法,就是如果程式中有需要用到和時間相關的計算,考慮一下有沒有可能把時間當成參數傳進來,而不要直接寫死在程式裡面。請參考下面這個例子:

getThisWeekRevenue();  ---> 在程式寫死,自己呼叫系統時間取得這一週的日期。

getWeeklyRevenue(Date aDate); --->哪一週由呼叫者傳入



另一種情況是程式中如果真的需要直接呼叫到類似SystemClock物件中的函數,請幫這個物件打包一層,定義一個類似ISystemClock的介面(可以用Extract Interface這個refactoring方法,將SystemClock的介面抽離出來),然後再寫一個DefaultSystemClock來實作ISystemClock介面。DefaultSystemClock可以包含一個SystemClock物件,然後將所有實作直接轉呼叫原本的SystemClock物件。在測試案例中,可以自己產生一個MockSystemClock取代DefaultSystemClock,以達到控制時間的目的。



***



友藏內心獨白:不是只有上帝才可以控制時間嗎 XD。

2012年12月4日 星期二

對付時好時壞的測試案例(4):Remote Services

Nov. 27 17:42~18:37


Remote Services

第三種造成測試案例時好時壞的原因叫做Remote Services(遠端服務),還是用看圖說明比較快。

螢幕快照 2012-11-27 下午5.53.44

 

假設你的應用程式需取得「政府實價登錄網站」中的資料,那麼這個「政府實價登錄網站」對你的應用程式而言就是一個遠端服務。最近有看新聞的鄉民們應該都知道,「政府實價登錄網站」的行為是會改變的,像是價錢、地段等資料,從原本的純文字被改成圖片。除此之外,「政府實價登錄網站」還可能因為同時上線使用者太多,而導致有些人連不上去,無法使用該系統。在這種情況之下,如果鄉民的測試案例會直接呼叫「政府實價登錄網站」的服務內容,這樣的測試案例就很容易因為「政府實價登錄網站」這個遠端服務的行為改變,而造成測試案例時好時壞的現象。

 

怎麼辦

解決遠端服務所造成測試案例不穩定的問題,最常用的方法就是套用所謂的Test Double(測試替身)這個測試模式。這個概念也很簡單,既然遠端服務的行為無法控制,那麼就自己幫這個遠端服務開發一個假的「測試替身」,用來代替原本真正的遠端服務。這個為了測試用途所做出來的替身,它的行為可以是最簡單的只接受對方的呼叫並列出對方傳過來的參數(Dummy)、很簡單的只要應付特定規劃好的測試路徑所期待的結果(類似mock object),或是複雜一點,實作與實際遠端服務類似功能(stub)等等。測試替身通常和你的待測程式(也就是你的應用程式)都放在相同的環境當中,而且都受你所控制,所以可以辦免發生測試案例時好時壞的問題。

螢幕快照 2012-11-27 下午6.02.35

 

可是你沒有測試到真實的遠端服務啊

看到這邊鄉民們可能會想,使用測試替身這一招雖然可以增加測試案例的穩定性,但是這樣一來,就沒有測試到原本真正的遠端服務。舉「政府實價登錄網站」的例子,如果使用測試替身,第一版「政府實價登錄網站」的價錢、地段等資料是純文字,所以第一版的測試替身也是會回傳純文字。但是有一天「政府實價登錄網站」的價錢、地段等資料突然無預警的改成圖片,但是你的測試替身並不知道這樣的改變,所以測試案例還是通過。換句話說,這個bug(問題或是現象)並沒有被測試案例給找出來啊。

基於上述的原因,因此有很多人堅持當應用程式有使用到遠端服務的時候,一定要真的去呼叫實際的遠端服務,不可以使用測試替身。哇,這下怎麼辦,如果不使用測試替身,那麼這樣的測試案例又可能會變成時好時壞的狀態。

這個問題的癥結在於,鄉民們無法知道「政府實價登錄網站」這個遠端服務和自己實作的測試替身,兩者的行為是否保持一致。對此,Martin Fowler建議大家要撰寫另外一種叫做「Integration Contract Test」(整合合約測試)的測試案例。

螢幕快照 2012-11-27 下午6.23.31

 

如下圖所示,Integration Contract Test先呼叫遠端服務,再呼叫測試替身,最後比較這兩者的行為是否一致(例如,傳回資料是否相同)。如果Integration Contract Test失敗,就表示遠端服務的行為已經更改了,因此你的應用程式以及測試替身也要跟著修正。

螢幕快照 2012-11-27 下午6.31.35

***

友藏內心獨白:使用測試替身與Integration Contract Test,也是一種separation of concern的做法。

2012年12月3日 星期一

對付時好時壞的測試案例(3):Asynchronous Behavior

Nov. 27 10:00~11:18
Asynchronous Behavior
今天介紹第2種造成測試案例時好時壞的問題:「Asynchronous Behavior」(非同步行為)。如下圖所示,以現在很流行的Ajax呼叫為例,當Browser透過Ajax的方式呼叫後方的某個DAO Service(資料存取服務)之後,在資料還沒有傳回Browser之前,Browser同時間還可以處理他的事情 。這種呼叫就叫做非同步的呼叫。
螢幕快照 2012-11-27 上午10.15.49

那為什麼非同步的行為會導致測試案例時好時壞呢?請參考下圖,因為呼叫者(Browser)不知道對方要執行多久才會把資料傳回來,所以在撰寫測試案例的時候,如果等待的時間太短,資料還沒有回傳,那麼測試案例就會執行失敗。
螢幕快照 2012-11-27 上午10.27.51

請看以下這段虛擬碼:
doAsyncCall();
sleep(aWhile);
readResult();
assertResult();


如果鄉民們的測試案例寫成上面這個樣子,那麼當測試案例失敗的時候,通常都是設定等待的時間(sleep)太短了,需要增加。但是,如果sleep太長,則測試案例就會跑得很慢,例如答案可能1秒就算好了,但是你卻等了60秒才去檢查,這樣也不行啊。


處理非同步行為

針對非同步行為的測試,Martin Fowler提出兩點建議:
  • 使用callback
  • 使用polling
使用callback是比較好的做法,但是需要被呼叫者配合才能夠做到。這是什麼意思?如果上圖中的DAO Service所提供的API可以允許Browser在呼叫它的時候,同時提供一個callback function(回呼函數),那麼鄉民們就可以把測試案例寫在這裡。等DAO Service傳回資料之後,自然會主動呼叫Browser所提供的callback function,這樣也就沒有「測試案例需要sleep多久再檢查回傳結果」的問題了。少數的非同步應用,例如非同步網路函數,或是最近很流行的Node.js有提供callback function。但大部分的應用可能無法使用這一招,所以只好改用樓下的這一招:polling(輪詢)。請看以下這一段虛擬碼。

doAsyncCall();
startTime = Time.now();
while(! responseReceived) {
  if (Time.now() - startTime > waitLimit) 
    throw new TestTimeoutException();
  sleep (pollingInterval);
}
readResult();
assertResult();


簡單的說,使用polling的方式,一開始先檢查資料是否已經傳回來了,如果沒有,看看是否已經等到timeout(等太久了都還沒讀到資料),如果等到timeout,則表示測試失敗,丟出一個TestTimeoutException例外。如果還沒等到timeout,則小睡片刻。採用這種方式,可以將sleep的時間設短一點,反正這次如果讀不到資料,小睡片刻之後還可以再讀一次,一直到timeout為止。如此一來可以避免一直調整sleep時間,或是將sleep設太長導致測試案例執行間太久的問題。


但是使用polling也不是完全都不需要調整時間,至少需要調整timeout的時間,因為如果timeout時間設太短,則資料還沒傳回來就已經timeout了,這樣也不行。

好難測啊

遇到這種非同步的測試,的確是滿頭大的。有一個關於測試的技巧叫做Humble Object pattern,大意是說如果鄉民們的程式邏輯處在一個不容易測試的環境當中,鄉民們就要想辦法把這個邏輯從環境中隔離(抽離)出來。換句話說,想辦法把重要的邏輯能夠用同步呼叫的方式來進行測試,那麼必須被以非同步呼叫來測試的內容或是機會就比較少了,相對地出問題的機率也會降低。

***

友藏內心獨白:有時候timeout的數值也是要調好久測試案例才會穩定啊 嚎啕大哭

2012年11月28日 星期三

對付時好時壞的測試案例(2):Lack of Isolation

Nov. 26 17:26~18:22
image

昨天提到對付時好時壞的測試案例第一步就是要先將它們從main deployment pipeline隔離,把這些測試案例先暫時移到quarantined pipeline中。Martin Fowler接著舉出下列5種可能會導致測試案例時好時壞的問題,並介紹如何排除這些問題的方法。今天Teddy先介紹第一種Lack of Isolation(缺少隔離)。
  1. Lack of Isolation(缺少隔離)
  2. Asynchronous Behavior(非同步行為)
  3. Remote Services(遠端服務)
  4. Time(時間)
  5. Resource Leaks(資源洩漏)
Lack of Isolation
造成測試案例時好時壞的第一個兇手就是測試案例彼此互相有關係,並非全然的獨立。什麼叫做彼此有關係(缺少隔離)?例如,有A、B兩個測試案例,A檢查某個物件是否被產生,B使用A所產生的物件來驗證某些狀態。在這種情況下,A跟B就不是完全獨立的測試案例。另一個常見的例子就是好幾個測試案例共用資料庫中相同一組待測資料。理論上,每個測試案例需要重建自己的測試資料,但是有時候建立測試資料很花時間,所以為了節省時間,有時會讓好幾個測試案例共用一組測試資料。但是萬一這組測試資料不小心被哪一個測試案例給改了,其他的測試案例就可能會失敗(和公用變數所造成的問題類似)。
解決這個問題的方法有兩個常見的簡單策略:setup與clean-up,前者要求測試案例在執行之前要負責把自己所需要的測試環境建好,後者要求測試案例在執行之後,必須要將測試環境恢復到執行測試案例之前的狀態。用講得太慢,請看圖。以下兩種方法,何者比較好?執行完測試案例再恢復狀態,還是每次執行之前先建立新的狀態?
螢幕快照 2012-11-26 下午5.54.53

這兩種做法各有各的優缺點。採用setup的方式,可以確保測試案例每次執行時都有一個已知的狀態,如果測試案例執行失敗,可能是production code有問題,或是這個測試案例本身有問題(這句不是廢話喔,等一下繼續往下看就知道)。如果是測試案例有問題,有可能是因為自己的setup步驟沒有建立起正確的測試環境,或是測試案例寫錯了。採用setup的缺點,則是因為每次執行測試案例都需要產生一個新的測試環境,所以測試案例執行速度相對來講會比較慢。
若是採用clean-up的方式,在執行一群測試案例之前,只要先把測試環境設定好一次,在執行完每一個測試案例之後,只要復原剛剛有動到的測試環境即可,不需要重新建立一次全新的測試環境。換句話說,採用clean-up的方式來執行測試案例有通常會比較快。但是,clean-up有一個非常嚴重的缺點,那就是如果測試案例執行失敗,除了可能是production code有問題,或是這個測試案例本身有問題以外,還可能是別的測試案例的clean-up有問題而導致自己出錯如果是因為別的測試案例的問題導致自己出錯,那麼這樣的bug就不容易被找出來,請參考下圖。
螢幕快照 2012-11-26 下午6.13.28

換句話說,採用clean-up的方式,會導致前後緊接執行的兩個測試案例彼此有相依性,因此如果考慮到要避免產生時好時壞測試案例的機會,那麼採用setup的方式會比較好。
***
友藏內心獨白:以前經常把setup與clean-up這兩種方式混著用耶 挑眉質疑

2012年11月27日 星期二

對付時好時壞的測試案例(1):還沒痊癒,就先隔離

Nov. 26 15:33~17:08
image

平常有在寫自動化測試案例並且執行迴歸測試的鄉民們,幾乎一定都會遇到一個問題:在沒有修改程式碼的情況下,為什麼有些測試案例有時候會過,有時候不會過呢?遇到這種狀況,相信鄉民們第一個念頭一定是:難道是遇到鬼,農曆七月不是已經過了?!除了遇到鬼這個可能的原因之外,應該還有其他更科學一點的解釋吧。
在還不用查看任何資料之前,大部分有寫過測試案例的鄉民們,應該隨便都可以找出幾個嫌疑犯。例如,測試案例的相依性(測試案例必須依照特定執行順序才會全部通過,只要前面的測試案例失敗,後面的測試案例就會被「帶屎(拖累)」跟著一起失敗)而導致這種時好時壞的靈異現象。還有timing也是很常見的問題,像是點選網頁上某個連結,等待若干秒之後期待會有什麼「好事(結果)」發生,但是等到天荒地老卻什麼事也沒發生 挑眉質疑
Teddy在開發軟體的時候,也經常會遇到測試案例時好時壞的問題,也累積了一些小技巧用來解決這些問題,但是從來也沒特別想過產生這種現象的根本原因可能有那些。幾個月前無意中讀到了Martin Fowler的這一篇《Eradicating Non-Determinism in Tests》(消除測試中的不確定性)文章,發現Martin Fowler已經幫大家整理好了,那我們這些後生晚輩就可以直接站在巨人的肩膀上看得更高、更遠了 微笑
***
還沒痊癒,就先隔離
當遇到這種時好時壞的測試案例,請問鄉民們的第一個反應是什麼?直接把測試案例丟掉,還是嘗試修正?換個方式來問這個問題,假設你有一個朋友得到SARS,請問鄉民們該如何處置?人道毀滅(直接丟掉),這樣太慘忍了。想辦法醫治(嘗試修正),但是一時之間又不知道要用什麼藥可以治療SARS。搞不好藥還沒找到,就先傳染給其他人,那豈不是更慘。所以,遇到這種情況,第一件要做的事應該是把病人(有問題的測試案例)給關在負壓隔離病房(居家自我隔離也OK啦)
什麼叫做「關在負壓隔離病房」?簡單的說,就是要把病人(時好時壞的測試案例)從正常人(正常的測試案例)裡面給挑出來。因為如果不這麼做,測試案例有時成功,有時失敗,久而久之開發人員就會想:啊,我們的測試案例就是不知道為什麼時好時壞,所以這一次的執行結果雖然失敗,但「此為正常現象,請安心服用」,不用管它了。原本測試案例如果執行失敗應該是要請開發人員找出程式中的問題(production code或是test code有問題),但如果把時好時壞的測試案例和正常的測試案例混在一起,時間久了之後開發人員就不再信任測試案例,因此這些測試案例就等於沒有作用。
知道了為什麼要隔離之後,接下來說明一下要如何隔離。根據Martin Fowler的說法,隔離的意思,是把時好時壞的測試案例從持續整合系統的「main deployment pipeline 」移除。這是什麼意思?用畫圖解釋比較快。
當開發人員把程式送交到版控系統(例如,SVN或Git)之後,版控系統送一個通知訊息給持續整合系統,觸發一次建構。在正常情況之下,持續整合系統被板控系統所驅動的每次建構會執行一個主要的建構流程,稱之為「main deployment pipeline 」。因為這個流程由好幾個建構工作,例如編譯、測試、靜態程式碼分析、打包程式等等所組成,整個流程串起來好像一個管線一樣,所以稱為deployment pipeline(因為持續整合的最終目的是希望能夠佈署軟體,所以叫做佈署管線)。
螢幕快照 2012-11-26 下午5.03.35
現在問題來了,如果測試案例中有那種時好時壞的測試案例,就不宜將它們留在main deployment pipeline中。但是又不能直接砍掉,所以就在持續整合系統中額外增加一個「quarantined pipeline」(隔離管線),把時好時壞的測試案例先移到這裡隔離起來。之後,可以設定讓持續整合系統在每次執行建構的時候,分別或同時執行main deployment pipeline與quarantined pipeline這兩個管線的建構工作。這種做法的大原則是,在main deployment pipeline上的測試案例全部都要通過,而在quarantined pipeline的測試案例如果不通過則可以暫時忽略。如此一來開發人員對於main deployment pipeline的測試案例還是持續保持著信心,而至於在quarantined pipeline的測試案例,則可以找時間再來修復。
***
Quarantined pipeline不是垃圾桶
把時好時壞的測試案例移到quarantined pipeline的這種作法,有兩點要特別留意:
  • 這種做法不是教大家把所有執行失敗的測試案例不分青紅皂白全部都直接移到quarantined pipeline,只有那種因為不明原因而時好時壞的測試案例才移到quarantined pipeline。關於那種每次執行都一定會失敗的測試案例,不是production code有問題,或是test code有問題,應該要找出問題加以修正。
  • 移到quarantined pipeline只是一種暫時性權宜措施,不是把時好時壞的測試案例丟進來之後就不管它們了,還是要安排時間來修護。至於何時修正,可以由開發團隊自行決定。一種常見的做法是,鄉民們可以規定,例如quarantined pipeline裡面的測試案例最多不能超過5個,一但超過5個,團隊就必須安排時間來修復這些測試案例。修復完成的測試案例,就會從quarantined pipeline移回main deployment pipeline中。
***
友藏內心獨白:隔離和砍掉是不同的做法喔。

2012年7月5日 星期四

持續整合工程師應遵循的十項要領(下)

July 04 16:26~18:10

image

昨天介紹了前五項,今天繼續介紹後五項:

6. Fail builds fast

假設鄉民們的建構工作包含編譯(需要30秒)、測試(需要2分鐘)、計算行號(需要10秒)、產生JavaDoc(需要20秒),請問哪一件工作應該要先做?答案是編譯,為什麼?因為如果編譯失敗,後續的建構工作就沒有意義,也就不需要繼續執行,所以整個建構工作最多30秒就結束了。也就是說,如果先執行編譯,開發人員最多只需要等待30秒就知道建構失敗。如果先做計算行號與產生JavaDoc,基本上這兩件事都是不太會出錯的工作,做完之後需要30秒。此時再來做編譯工作,結果失敗,因此開發人員需要等待60秒後才會知道建構失敗。所以,在安排建構工作執行順序的時候,一個很簡單的原則就是「越可能失敗的工作要越早執行」如此便可在比較短的時間內得到建構(失敗)的結果。看到這邊鄉民們可以會問:那為什麼不先執行測試,測試不是很容易失敗嗎?是啊,測試是很容易失敗,但是如果程式無法編譯,那麼就不可能去執行測試了。所以除了「越可能失敗的工作要越早執行」以外,還要考慮到「建構工作之間的相依性」。

7. Build for any environment

在設計建構腳本的時候,需要考慮到單一建構腳本要能夠支援各種所需的建構環境。例如,假設專案需要分別產生以下三種不同的安裝程式:開發人員debug、給測試人員、以及正式釋出,那麼建構腳本最好設計成只要給定不同的參數,便可達成上述要求。

  • ant –f build.xml –Denv=debug --–> 產生debug版本
  • ant –f build.xml –Denv=qa --–> 產生給測試人員的版本
  • ant –f build.xml –Denv=release --–> 產生正式釋出版本

8. Use a dedicated CI machine and a CI server

要實施持續整合,當然一定要有一套持續整合系統,例如Jenkins。由於持續整合所需要時間越短,開發人員才會願意頻繁地建構軟體,所以最好把持續整合系統安裝在一台專屬的電腦之上。這台給持續整合系統使用的電腦,最好是越高檔越好,詳情請參考「土炮跨平台自動化功能測試環境」。

9. Run fast builds

麥當勞與肯德雞這類的「速食店」,之所以稱之為「速食」,就是強調點餐之後可以很快地就拿到食物。應該很少人在速食店點餐之後還需要等超過10分鐘以上才拿到餐點。總之有些行業就是強調以快取勝,例如 快打旋風 PxHhome 24小時購物、某數字人力銀行強打的24小時必回覆(迷之音:難道是24小時一到,如果沒有回覆將會由系統自動寄出一封感謝狀?!)、以及某數字組屋網最近猛打的廣告:XXX組屋網,組屋就是快。持續整合也具有類似的特性,建構的過程一定要快,這樣開發人員才可以在很短的時間內得到回饋。至於要多短的時間才算得上快,可以參考「Ten-Minute Build」。

10. State builds

快、快、快,什麼都要快。問題是,有些建構工作就是會跑很久啊,怎麼可能在短短的10分鐘之內執行完畢。怎麼辦,難道就要放棄持續整合?當然不是。遇到這種情況,可以採取分階段執行持續整合工作的策略。這是什麼意思?很簡單,就是把建構工作分成兩段(或很多段),先執行一個輕量級的建構專案,如果成功,再接著執行要跑比較久的重量級建構專案。當第一個輕量級的建構專案(假設可以在10分鐘或是更短的時間內執行完畢)執行結束之後,雖然不保證專案完全沒有問題,但是至少可以提供團隊某種程度以上的信心。因此在持續整合系統繼續執行後續重量級建構專案的時候,開發人員便可繼續開發的工作,不用等到全部的建構工作都執行完畢。

聽起來有點抽象,舉例說明:輕量級的建構專案可以只包含編譯、執行「純單元測試」、產生安裝程式等工作。如果這樣還是無法壓縮在10分鐘之內,甚至可以只執行編譯與產生安裝程式(前提是開發人員有在本地端執行過單元測試了)。假設輕量級的建構專案成功之後,開發人員便可「大膽假設」專案目前狀況是OK的,然後看是要直接使用剛剛產生的安裝程式來做手動驗收測試,還是繼續開發功能。就在此時,持續整合系統便可繼續執行後續的重量級建構專案,例如產生測試涵蓋率報表、執行驗收測試、靜態程式碼檢查等。

***

前一陣子因為要去教人家持續整合,所以Teddy就強迫自己把《Continuous Integration》這本書的某些章節仔細地讀了一次。「開發人員應遵循的七項持續整合要領」、「持續整合工程師應遵循的十項要領(上)」以及今天所介紹的內容就是從Teddy授課的投影片中節錄出來的。其實說真的,光是讀《Continuous Integration》這本書,有時候會覺得有點無聊。為什麼?因為書中有很多敘述性的文字,要很用力去思考才可以把持續整合跟開發整個串起來。之前Teddy在讀《Continuous Integration》的時候,因為沒有授課的壓力,所以隨便翻一翻,有時候還會冒出有一種:「這本書沒講什麼啊,自己好像都知道了」的感覺。但是,仔細一看,其實這本書基本上將持續整合該注意的事情都交代了一遍,堪稱是目前市面上介紹持續整合最好的一本書。給它按個讚。

***

友藏內心獨白:持續整合不是只有工具的議題而已喔。

2012年7月4日 星期三

持續整合工程師應遵循的十項要領(上)

July 03 17:11~18:45

image

昨天談到「開發人員應遵循的七項持續整合要領」,接下來繼續介紹《Continuous Integration》這本書中所提到的持續整合工程師所應注意的十項要領,今天先介紹前五項:

  1. Automate builds:持續整合顧名思義就是要經常地執行整合工作,如果整合的流程無法自動化,請問鄉民們會那麼勤勞手動的去執行整合的工作嗎?舉個例子,假設程式完成編譯之後,還需要人工介入,把編譯完成的二進制程式碼複製到某個地方,才可以執行測試。這樣的整合流程肯定久久才會被做一次,因為沒有人想當「人工介入」的那個人(附註說明:公司錢太多或是公家機關不在此限)。
  2. Perform single command builds:持續整合的工作最好是只要執行一個命令列指令就可以完成,例如ant build.xml。建構腳本(build script)本身可以很複雜,但是執行建構腳本的方式一定要很容易
  3. Separate build scripts from your IDE:在古早、古早的時代,按下IDE上面的「F7、F8還是F9」按鈕(真的是太古早了,連Teddy都忘了Turbo Pascal和Turbo C IDE上面所提供的編譯熱鍵是什麼了),就等於完成了軟體的建構工作。隨著軟體系統越來越大,建構的工作越來越複雜,除了編譯、測試以外,還包含靜態程式碼分析、測試涵蓋率分析、產生安裝程式等。如果開發團隊繼續依賴IDE來建構軟體,那麼將會使得可應用的建構工作受限於IDE的功能,大大減少持續整合的威力。這還不是最慘的,如果依靠IDE來建構軟體,經常會發生「在我的電腦明啊明就可以成功建構,但是在其他人的電腦就不行」的這種靈異現象。為什麼會這樣?因為每位開發人員的開發環境設定各不相同,再加上IDE在建構軟體的時候私底下偷偷幹了什麼好事鄉民們也不太清楚,所以持續整合會要求開發團隊撰寫專門的建構腳本。
  4. Centralize software assets:持續整合的產出物種類繁多,舉凡編譯過後的二進制程式碼、測試通過與失敗報表、測試涵蓋率報表、靜態程式碼報表、以及最重要的安裝程式等。這些產出物要找個地方統一存放起來,一般而言最簡單的方式就在放在持續整合系統的那台電腦的硬碟裡面。只要每次建構軟體所產生的產出物可以從持續整合系統的網頁上看得到就可以了。但是,硬碟空間有限,整合資料無窮。怎麼辦?以Teddy之前的經驗:
    • 安裝持續整合系統的時候,儘量購買市面上容量最大的硬碟。
    • 觀察專案產出物所需空間與成長情形,來決定需要保留多少天的資料。如果產出物容量不大,可以暫時都不刪掉也可以。
  5. Create a consistent directory structure:每個相同類型的專案(例如Web程式、桌面程式、DLL函式庫程式)最好有相似的專案目錄結構,如此不但可以簡化持續整合腳本的撰寫,也可以促進團隊合作開發。以一個Java跨平台軟體的共享元件目錄範例,這些約定好的目錄會被持續整合工程師事先建好放在版本控制系統中。開發人員將所有的native code專案建構後的結果會被放在native目錄中;團隊自行開發的專案,建構結果會被放到in-house-src與in-house中;所有的驅動程式則是放在driver目錄。有了這個共享目錄,其他專案需要相關建構結果,便可到此共享目錄中抓取。除了這個共享目錄之外,每個專案也都會有自己的目錄結構。下圖是一個非常簡單的Java專案目錄結構,一般的商業軟體的專案目錄會稍微比較複雜一點。

螢幕快照 2012-07-03 下午6.39.16

***

看到這邊鄉民們可能會說:「啊我們公司又沒有專屬的持續整合工程師」。有沒有專屬的持續整合工程師不是重點,重點是團隊有沒有真正落實持續整合。如果有,那麼在沒有持續整合工程師的情況下,這些工作就會落在某位或是某幾位開發人員身上。看完這一集與上一集,鄉民們應該可以發現,持續整合的工作,對於「開發人員」與「持續整合工程師」各有不同的要求,遇到與持續整合有關的工作時,鄉民們只要記住自己正在扮演何種身分然後再參考該身分應該要注意的事項即可。

***

友藏內心獨白:開發個軟體搞的好像在Cosplay。

2012年7月3日 星期二

開發人員應遵循的七項持續整合要領

July 02 22:30~23:25

image

 

今天沒有廢話,直接介紹《Continuous Integration》這本書中所提到開發人員應該遵守的七項持續整合要領:

  1. Commit code frequently:持續整合之所以冠上了持續這兩個字,有一個很重要的要求,那就是開發人員必須要頻繁地將程式送交到版本控制系統中。為什麼?因為持續整合系統在執行建構工作的時候,會從版本控制系統中取出最新的程式碼。如果開發人員打死都不把程式送交到版本控制系統中,那麼就算專案每天被持續整合系建構N次,也沒有任何意義。所以,唯有開發人員「持續送交程式碼」,持續整合才有機會可以發揮作用。
  2. Don’t commit broken code:所謂broken code就是會導致建構失敗的程式碼。如果鄉民們送交到版本控制系統中的程式10中有9次都會導致建構失敗,那就表示這些被送交的程式碼品質根本有很大的問題。開發人員在送交程式碼之前,必須要謹記不要因為自己的不小心就造成建構失敗。每次建構失敗就代表著專案的健康狀態出了嚴重的問題,必須要立即花費時間與人力去修復。所以,開發人員要對自己送交的程式負起責任,不要因為自己不小心而造成建構失敗。
  3. Fix broken builds immediately:不知道鄉民們有沒有聽過破窗理論(broken windows theory)?依據維基百科的解釋:「此理論認為環境中的不良現象如果被放任存在,會誘使人們仿傚,甚至變本加厲。以一幢有少許破窗的建築為例,如果那些窗不被修理好,可能將會有破壞者破壞更多的窗戶。最終他們甚至會闖入建築內,如果發現無人居住,也許就在那裡定居或者縱火。又或想像一條人行道有些許紙屑,不久後就會有更多垃圾,最終人們會視若理所當然地將垃圾順手丟棄在地上。因此破窗理論強調著力打擊罪行,以零容忍的態度面對罪案。」將破窗理論套用在持續整合上面,如果專案建構失敗(例如測試案例沒有全部通過)但是團隊都沒有人把建構失敗當作一回事,大家都認為只要編譯成功就好了。久而久之,持續整合就失去它的作用。破窗理論告訴鄉民們,要以零容忍的態度面對犯罪。什麼叫做持續整合的犯罪?任何造成建構失敗的事件就是一種「犯罪」,如果忽視它,持續整合就爛掉了。所以這一條要領告訴鄉民們,要「時時勤拂拭,不使惹塵埃」。
  4. Write automated developers tests:持續整合和自動化測試是相輔相成的好兄弟,光有持續整合,沒有自動化測試案例,就好像蓋了很多「蚊子館」一樣,空有硬體建設沒有參觀的人(有持續整合系統但沒有自動化測試案例,那麼持續整合頂多做做編譯的工作,無法藉由執行測試案例找出功能錯誤)。反之,光有自動化測試案例,沒有持續整合,就好像開放了一大堆陸客來台觀光,但是卻沒有好景點可以容納大量的陸客(測試案例寫了一堆,但是卻擺在一旁很少執行。不執行測試案例怎麼會知道系統有沒有問出題啊,那還不如不寫把時間省下來早點下班)。
  5. All tests and inspections must pass:這一點跟第3點很像,如果測試或是inspection(可以想成靜態程式碼檢查)沒有通過,但是程式可以編譯,那麼這次的建構算不算成功建構?當然不算,因為這一條要領已經說得很明白了啊。
  6. Run private builds:第2條要領要求開發人員不要送交有問題的程式,但是開發人員怎麼會知道自己送交的程式有沒有問題啊?就是不知道才要把程式丟到版本控制系統,然後驅動持續整合系統去建構看看能不能成功啊。所以程式還沒在持續整合系統上建構之前,開發人員又不是算命的,怎麼會知道正要送交的程式碼有沒有問題?答案很簡單,就是「在送交程式之前,先執行本地建構」。
  7. Avoid getting broken code:最後一條,提醒開發人員不要不小心從版本控制系統拿到無法建構的程式碼。要做到這一點,一個最簡單的方法就是,要從版本控制系統中更新程式之前,先到持續整合系統看看目前的專案是否可以被成功建構。另一個方法就是萬一有人造成建構失敗,要馬上主動通知團隊成員,請他們暫時不要更新程式碼,直到問題解決為止。

鄉民們工作上有實施持續整合嗎?如果有,以上七點要領,又落實了幾項呢?

***

友藏內心獨白:該不會有人想問「什麼是持續整合吧」…啊…說好了不打臉的XD。

2012年6月28日 星期四

當Branch遇到持續整合(4):Branch by Team

June 25 21:49~ 23:00

螢幕快照 2012-06-25 下午9.49.40

 

這一集要介紹四個「分支/合併模式」的最後一個Branch by Team(依團隊而分支)。Branch by Team的主要目的是要讓一個很大的團隊(數十人以上)可以同時開發一個專案或產品,但是同時還要維持trunk上的程式碼達到隨時可釋出的狀態。其主要做法為:

  • 將一個大團隊分成數個人數在九個人左右的較小團隊。
  • 每個團隊都是一個feature team,也就是說,整個專案依功能被切成若干的功能模組,而每個團隊負責開發至少一個功能模組。例如,假設有一個48人的團隊要開發一個圖書館管理系統,整個系統被分成讀者管理模組、書籍管理模組、借還書模組、期刊管理模組、電子書管理模組、報表管理模組等六大模組。整個團隊被分成六個小組,每個小組剛好八個人。這六組人馬就被指派分別開發上述那六大模組。
  • 為每一個團隊產生一個branch,每一個團隊就在自己所屬的branch上開發。
  • 當branch上的程式碼達到穩定的狀態時,branch才可以被merge回trunk。
  • 每次merge回trunk的程式碼必須要立即在merge到其他團隊的branch之中。

同樣是做branch,Branch by Team和Branch by Feature有一點很大的不同,那就是前者的branch是很「長壽」的branch,只要團隊還在,這個branch就存在。而後者的branch是很「短命」的branch,只要功能完成之後將branch的程式被merge回trunk之後該branch的生命也就跟著結束了。由於Branch by Team的branch生命週期很長,所以開發團隊就可以為每一個branch在持續整合系統上面建立一個建構專案也就是說,持續整合的工作在branch與trunk上同時執行

***

看完這四種「分支/合併模式」的介紹,還有應用時機和持續整合系統之間的關係,鄉民們應該可以發現,一個看起來很簡單的版本控制系統與持續整合的實務做法,其實箇中還是有著奧妙的學問。開發團隊必須依據專案與團隊的特性來選擇適合自己的「分支/合併模式」。不過還好扣除掉Branch for Release這個為了軟體釋出而存在的模式,真正用在開發上的模式只有三個。而這三個也很容易選擇:

  • 團隊人數很少:
    • 團隊成員集中:Develop on Mainline或是Branch by Feature
    • 團隊成員分散:Branch by Feature
  • 團隊人數很多:Branch by Team
  • 開放原始碼專案:Branch by Feature
  • 需要經常實施持續整合:Develop on Mainline或Branch by Team

***

友藏內心獨白:看完這四個模式,心中的疑惑又少了一個。

2012年6月27日 星期三

當Branch遇到持續整合(3):Branch by Feature

June 25 16:17~17:50

螢幕快照 2012-06-25 下午4.11.40

 

這一集為各位介紹Branch by Feature(依功能而分支)這個模式。這個模式的目的很簡單,就是希望trunk上面的程式碼永遠保持著可以被釋出的品質。為了達到這個目的,開發人員就必須要:

  • 開發任何一個新功能的時候,就必須產生一個新的branch。假設鄉民們採用Scrum,在這個sprint裡面有五個story要完成。當開發人員要開始針對某一個story開始施工的時候,就必須先產生一個branch,然後在這個branch上面做開發
  • Branch上的程式必須被驗證後才可merge到trunk中。當開發人員在branch上面完成某一個功能之後,該功能必須要先通過測試人員的驗證,之後才可以merge到trunk中

Branch by Feature的概念就這麼簡單。但是要運作的好,還有幾件事情要留意的:

  • Trunk上面的異動每天必須要被merge到每一個branch中:看到這一點鄉民們可能覺得怪怪的,不是說所有的開發活動都必須在branch上面進行嗎,那為什麼trunk上面還會有程式異動需要merge到branch中呢?有,當某一個branch開發完成且merge回trunk之後,此時trunk的異動便需要merge回其他branch之中。以上圖為例,專案一開始同時間有Story 1與Story 2這兩個branch。當Story 1開發完成並且merge回trunk之後,trunk上的程式碼便需要儘快merge到Story 2 中。這個要求鄉民們應該已經清楚了,但是為什麼呢?每個story所代表的branch不是各自獨立開發嗎,那為什麼某個branch送交回trunk的結果要儘快merge到其他尚在開發中的branch呢?答案很簡單,為了避免之後其他branch送交回trunk造成broken build
  • Branch不可以活太久:如果開發團隊採用為期兩周的Scrum,每一個branch的生命週期才能才幾天而已。也就是說幾天之內就必須要完成一個story。如果一個branch活太久,例如超過一個月,就代表某個功能橫跨了兩個sprint才完成。這通常是story太大,必須要再細分的跡象。此外,branch活得太長,表示某項功能要很久之後才會被整合一次(假設持續整合系統只針對trunk上的程式進行整合)。當然看到這邊鄉民們可能會說,為什麼不幫每一個branch在持續整合系統上都設定一個建構專案?因為branch的生命週期很短,如果持續整合系統要針對每一個開發功能的branch都設定一個「短命專案」,那麼持續整合的工作量將會大大的增加,不見得每一個團隊都願意如此做。
  • 最大同時進行開發活動的branch數量不可大於每一個開發週期預計完成的story數量:簡單的說,同時進行開發的branch數量不可大於團隊所約定的最大WIP(Work in Progress)的數量,否則將會產生一大堆開發中但是都沒有被做完的工作,也就是Teddy經常說的半成品
  • Refactorying要小心:如果開發人員產生branch的目的是為了refactoring,那麼refactoring的結果要盡早merge回trunk,以避免產生merge衝突。
  • 配置守門員:如果可能的話,每次merge回trunk的程式碼都需要經過專案技術經理或是technical lead的檢視與批准。

***

基本上對於Branch的使用可以分成兩大類,第一類是覺得merge很麻煩,所以儘量不使用branch,這就造成了主要開發工作都在trunk上的Develop on Mainline。另一類可以看成有「潔癖」,認為trunk上的程式碼必須隨時保持可釋出的狀態。所以,所有的開發活動都必須要在branch上進行。近年來很流行的分散式版本控制系統(distributed version control system),像是Git,用branch的頻率簡直就跟喝開水一樣,非常適合用來支援Branch by Feature模式。

***

Branch by Feature讓trunk維持隨時都可以釋出的狀態,所有的開發都在branch上面進行,開發人員可以毫無後顧之憂地修改程式碼而不用怕一不小心就弄壞了trunk。看起來很棒,也很酷(不曉得為什麼使用很多branch的開發團隊感覺起來就比較酷一點XD)。但是從持續整合的角度來看,Branch by Feature是比較不好的一種方法。為什麼?原因Teddy剛剛已經說過了,什麼,沒看到…哇哩勒!請再看一次:

Branch如果活得太長,表示某項功能要很久之後才會被整合一次(假設持續整合系統只針對trunk上的程式進行整合)。那為什麼不幫每一個branch在持續整合系統上都設定一個建構專案?因為branch的生命週期很短,如果持續整合系統要針對每一個開發功能的branch都設定一個「短命專案」,那麼持續整合的工作量將會大大的增加,不見得每一個團隊都願意如此做。

看到這邊鄉民們應該只剩下最後一個疑問:哪種專案適合採用Branch by Feature?

Branch by Feature很適合使用於開放原始碼開發專案,此類專案的開發人員通常散布於全世界各地,而且不是任何阿貓阿狗都可以直接把程式碼貢獻到trunk中。通常開放原始碼專案會有一個人或是一個小團隊,負責審核「義工」所貢獻進來的程式碼(可能以patch的方式遞交給專案負責人),通過審核之後才會進到專案的trunk之中。而這些「義工」在開發某項功能,或是解決某個bug的時候,可以先在自己的本地端產生一份專案的branch,然後在此branch上面開發。

除了開放原始碼專案外,有些採用Kanban(看板)系統的團隊也會使用Branch by Feature。由於Kanban沒有像Scrum有開發週期(sprint)的概念,採行Kanban系統的團隊,可以更有彈性的決定釋出軟體的時間(這一點跟開放原始碼專案還挺像的)。因此保持trunk上的程式碼隨時都處於可釋出的狀態就顯得非常重要。在Scrum中,只要sprint結束時有一個潛在可釋出的軟體便可。從這個角度來看,採行Scrum的團隊並不需要嚴格要求「trunk上的程式碼隨時隨地都處於可釋出狀態」。換句話說,如果是小型的Scrum團隊,採用Develop on Mainline就挺合適的。什麼,你問那大型的Scrum團隊要採用哪種模式?等下一集你就知道了。

等一下,故事還沒結束。看了上一段不要以為採用Kanban的團隊就一定要使用Branch by Feature。根據Teddy讀了《Continuous Delivery》的感覺,除了使用在開放原始碼專案外,這本書的作者似乎不是很欣賞Branch by Feature這種模式,因為採用這種模式和持續整合的精神有所衝突。至於要不採用這種模式,就請鄉民們自行判斷一下吧。

***

友藏內心獨白:不是每種branch模式都是對持續整合很友善滴。

2012年6月26日 星期二

當Branch遇到持續整合(2):Branch for Release

June 25 14:29~15:17

螢幕快照 2012-06-25 下午1.43.37

 

今天要介紹跟「Develop on Mainline」經常一起搭配使用的Branch for Release(為了釋出而分支)。如果鄉民們有看前一集的介紹並且瞭解了Develop on Mainline模式,哪麼Branch for Release就很簡單了。以下是這個模式的特點:

  • 功能都是在trunk上開發,也就是說Branch for Release必須要跟Develop on Mainline搭配使用。在這邊Teddy先偷跑說明一下,Branch for Release是可以跟其他三個模式(Develop on Mainline、Branch by Feature、Branch by Team)一起搭配使用。
  • 當trunk上面的程式碼功能已經完整到可以釋出的時候,而且鄉民們還要繼續開發新的功能就為這次的釋出產生一個branch。產品釋出後還需要持續開發新功能這一點很重要,如果產品功能到達可以釋出的階段,但是鄉民們並沒有打算為此產品繼續開發新的功能,那麼就不一定需要為此次的釋出而產生一個branch,直接釋出trunk上的版本即可。
  • 當產品釋出之後,針對每一個釋出版本所做的bug fix直接送交到該版本的branch中,確認這些修正沒有問題之後在merge回trunk。以上圖為例,1.0.x版的bugfix會先merge回trunk中,然後在merge到1.1.x版中。由此圖可看出來,每個版本的bugfix要先merge回trunk,然後再判斷是否要進一步merge到其他各個release的branch。如果一個軟體釋出的版本越多,這種merge的時機點越需要小心謹慎,以免發生沒merge沒事,一merge之後整個軟體反而無法建構的問題(Teddy內心獨白:難道這就是傳說的中越補越大洞的靈異現象?!)。
  • 最後一點,也是與持續整合最相關的一點,原本trunk上的專案在持續整合系統上一定會有一個相對應的專案這一點鄉民們應該都了解了。一旦為了釋出而產生一個branch之後,鄉民們也要幫這個釋出版本在持續整合系統上新增一個專案,這樣日後才有「人」可以幫這個釋出版本所做的修改或是bugfix做整合與建構的工作。如上圖所示,版本控制系統中有一個trunk和兩個釋出(Release 1.0.x與Release 1.1.x),而持續整合系統上也有三個專案,分別用來建構trunk、Release 1.0.x與Release 1.1.x。

***

就這樣,Branch for Release可以算是一個「輔助模式」,因為它跟軟體的主要開發活動無關,而是用來保持不同釋出軟體版本之用。最後一個送分題:何種開發情境適合Branch for Release?如果鄉民們看到這邊還不知道就要打屁股了,答案很簡單,就是軟體要釋出的時候…XD。

***

友藏內心獨白:最後一句難道就是傳說中的廢話?!

2012年6月25日 星期一

當Branch遇到持續整合(1):Develop on Mainline

June 25 10:49~12:33

螢幕快照 2012-06-25 上午10.58.27

 

上週Teddy提到最近讀了《Continuous Delivery》這本書,書中介紹了四種很有用的「分支/合併模式」(branch/merge pattern),並且討論這四種pattern與持續整合之間的關係。接下來就分幾天把這四個pattern介紹一下,哪四個?

  1. 在主幹上開發(Develop on Mainline)
  2. 為了釋出而分支(Branch for Release)
  3. 依功能而分支(Branch by Feature)
  4. 依團隊而分支(Branch by Team)

Develop on Mainline

第一種模式叫做「在主幹上開發」,顧名思義就是說:開發人員主要的開發活動都在主幹上進行,沒事不要使用分支(插花說明一下,這裡的主幹指的是版本控制系統,像是CVS或SVN上面的Trunk,又稱作Mainline)。這種方法聽起來有點怪怪的,很多介紹版本控制系統的書不是都鼓勵開發人員要使用分支,為什麼這個模式反而建議大家沒事不要使用分支?

要回答這個問題之前,要先說明一下版本控制系統的使用情境,基本上可以從兩個角度來探討:

  • 程式開發:從程式開發的角度來看版本控制系統,為了避免提交到trunk裡面的程式碼有問題,導致軟體專案無法成功建構,產生所謂的broken build,因此有些實務做法會建議開發人員頻繁地使用分支(branch)功能。這種想法的出發點是:開發人員在分支上開發新的功能,一旦新的功能通過測試確定都沒有問題之後,才可以把分支中的程式合併(merge)回trunk,這樣子便可常保trunk上的軟體永遠都保持在可以隨時釋出的狀態。聽起來很好,但是頻繁使用branch有兩個主要的缺點:
    • 版本控制系統中的專案結構變得比較複雜,因此增加merge的成本。
    • 降低持續整合系統的功效。開發人員可能花費好幾天的時間在branch上開發程式,之後才把完成的程式merge回trunk,此時才會觸發持續整合系統執行一次建構。換句話說,持續整合系統就變得不是那麼的「持續」了(好幾天才建構一次,就不能說自己有在實施持續整合了)。
  • 持續整合:為了建構,每一個存在於持續整合系統中的專案上都必須要指定一個版本控制系統來源位置(code repository)。因此,從持續整合的角度來看版本控制系統,如果所有的開發活動都在trunk上進行,那麼持續整合系統中便只需要指定到trunk便可建構一個專案。假設有一個專案同時在trunk與branch上開發,而團隊又想要實施持續整合,那麼至少需要在持續整合系統上為此專案設定兩個持續整合專案。

看完上面的分析之後,鄉民們應該知道Develop on Mainline這個模式的用意了。開發團隊如果要採用這個模式,需要搭配幾點配合措施方可成功:

  • 頻繁地簽入(check-in or commit)程式碼到版本控制系統中:每次有程式碼被簽入至版本控制系統中便會驅動持續整合系統執行一次建構。因此,如果開發團隊採用Develop on Mainline,便需要經常性地將手邊的程式碼送交到版本控制系統中,否則就無法達到持續整合的目的。至於何謂「頻繁」?理想上完成一小段程式碼,例如寫完一個method與該method的單元測試便可送交程式碼。如果一開始無法做到這麼極端,最低限度一天至少也要送交一次(通常是下班前)。
  • 允許trunk上存在短暫的broken build:由於開發人員直接將程式碼送交到trunk上,因此難免有時候送交進去的程式碼可能會造成建構失敗,此時trunk裡面的程式碼就是「不健康的程式碼」。如果有其他開發人員在這個時候不小心checkout或是update了trunk上的程式碼,那麼這位倒楣的開發人員就拿到一套有問題的程式碼。如果該開發人員沒有注意到這個問題繼續開發程式,最糟的情況可能導致做白工,或是要花很多時間才可以把自己個程式碼merge回trunk。看到這邊鄉民們一定會想:「這樣不是很危險?」是啊,當發生broken build而沒有人發現是一件很危險的事。Develop on Mainline這個模式的出發點是:由於開發人員非常頻繁地送交程式碼,所以專案也會非常頻繁地被建構與整合。如果送交的程式碼不幸引起broken build,至少能夠被持續整合系統給發現,這也正是實施持續整合的目的:早期發現,早期治療。只要團隊成員可以清楚地從持續整合系統中知道目前專案的健康狀態,那麼短暫的broken build也就沒有那麼的可怕。
  • 指派專人關照專案健康狀態:有時候造成broken build的原因不明,或是開發人員沒有意識至自己送交的程式碼已經造成了broken build。長此以往如果都沒有人負責去關注broken build發生的原因並追蹤修復的進度,則持續整合的效果有做等於沒做一樣。因此,團隊需要指派專人來關注是否有broken build發生。此人不是要負責去修復broken build,而是要通知團隊目前發生了broken build,並找到或是協調相關開發人員立即在最短的時間內修復broken build。

***

看到這這邊鄉民們可能會問:難道Develop on Mainline就完全不能用branch嗎?也不是說不能用,只是說「沒事不要用」。好,沒事不要用,那什麼才叫做有事可以用哩?

  • 釋出軟體的時候:這一點等Teddy寫到Branch for Release之後再見分曉。
  • 做實驗的時候:有時候鄉民們想要對專案做一些實驗,例如換掉某個元件,或是嘗試一些新的功能,但是又怕實驗風險太大而把trunk搞亂了。此時便可產生一個branch然後在branch上做實驗。

在Develop on Mainline的情境下所產生的branch有一特點,就是基本上branch是不會被merge回trunk的,這樣才符合「所有的開發活動都在trunk進行」的宗旨。

***

最後還有一點要補充的,開發什麼樣的專案適合採用Develop on Mainline呢?答案很簡單:

  • 所開發的軟體還沒有釋出之前。
  • 如果同一時間提供給客戶的軟體版本只會有一個,那麼就很適合使用。例如,公司內部的資訊管理系統,或是網站系統。
  • 團隊人數不是很多的時候,例如9人以內。

***

友藏內心獨白:開車要走大馬路,不要走小徑。

2012年1月14日 星期六

安裝先行

January 14 11:52~12:24


Test-driven development 或是 test-first development 中文有人翻譯成測試先行」,大意是說還沒開始寫 production code (就是一般所謂的程式)之前,先把 test code 給寫出來。奇怪了「待測程式(production code)」都還沒個影子,如何寫 test code 去測試它(不存在的東西如何測試)箇中細節暫且不談,總之「測試先行」的目的是希望開發人員能夠先從「end users(可能是最終的客戶,或是呼叫你的程式的其他程式或是其他開發人員)」的角度來看看自己等一下所準備撰寫的程式應該長成什麼樣子,之後再實際逐一把這些程式實做完成。


Teddy 今天要談的是另一個類似的觀念,姑且稱之為「安裝先行」。傳統採用 waterfall 開發流程的專案或是產品,安裝與佈署的問題通常都是最後才被考慮的問題。為什麼?因為東西都還沒開發完畢,幹麼去考慮安裝和佈署的問題啊(東西都還不能用啊)。但是如果團隊採用的是 agile methods(敏捷方法),那麼「理論上」每個 iteration 結束時,都應該要有一個可以執行的軟體(running software),所以對於敏捷團隊來說,在專案初期便應該需要考慮軟體安裝與佈署的問題。


關於這個問題 Teddy 和指導教授曾經發表過一篇持續整合的論文,其中有介紹這樣的觀念(如下圖所示)。






這兩天 Teddy 把 97 Things Every Programmer Should Know 這本書翻了一下,發現書中第 40 頁也有提到類似的觀念。


Deploy Early and Often
Starting your project with an installation process will give you time to evolve the process as you move through the product development cycle, and the chance to make changes to the application code to make the installation easier.


有興趣的鄉民們可以參考一下。
**


友藏內心獨白:當遇到防弊與興利的抉擇時,大部分的人還是會選擇比較不會被質疑的防弊