軟件開(kāi)發(fā)過(guò)程中,我們經(jīng)常遭遇各種各樣的問(wèn)題,而本文就是要講解這些問(wèn)題中最棘手的12個(gè)。讀完本文后,相信讀者會(huì )對它們影響開(kāi)發(fā)效率的原委有個(gè)初步的認識。? 我們發(fā)現,有很多的文章、書(shū)籍都在闡述軟件的開(kāi)發(fā)方法,為什么呢?個(gè)人以為那是因為提高團隊的開(kāi)發(fā)效率是一場(chǎng)永無(wú)止境的戰爭。軟件的開(kāi)發(fā)技術(shù)日新月異,開(kāi)發(fā)?團隊只能不停地適應這種革新(如果這個(gè)團隊不是此項技術(shù)的領(lǐng)頭羊),否則只能消亡。而適應很大的程度表現在效率和時(shí)間上,因為客戶(hù)對軟件質(zhì)量的要求是越來(lái)越高,同時(shí)卻不希望延長(cháng)開(kāi)發(fā)周期。
我們知道,隨著(zhù)軟件外包的到來(lái),同一個(gè)團隊的開(kāi)發(fā)人員可能來(lái)自不同的國家,他們處在不同的時(shí)區,有著(zhù)不同的文化背景。和其它行業(yè)的人一樣,開(kāi)發(fā)人員往往喜歡把精力用在他們感興趣的任務(wù)上面,卻經(jīng)常忽略了更重要的事情——開(kāi)發(fā)效率。
在軟件開(kāi)發(fā)這個(gè)繁雜的世界里,作為一個(gè)項目經(jīng)理,也作為一個(gè)開(kāi)發(fā)人員,我碰到過(guò)各種各樣的有趣的問(wèn)題。這篇文章就羅列出了這些問(wèn)題中最棘手的12個(gè),并提供一些處理問(wèn)題的參考意見(jiàn)。
1.維護的開(kāi)銷(xiāo)是效率最大的敵人
軟件的維護需要很大的開(kāi)銷(xiāo),它很自然地把開(kāi)發(fā)團隊帶向低效。維護的開(kāi)銷(xiāo)往往和代碼行數成比例,尤其當代碼沒(méi)有經(jīng)過(guò)很好的測試的時(shí)候,這個(gè)情況尤為明顯。開(kāi)發(fā)團隊會(huì )發(fā)現:隨著(zhù)代碼行數的增加,bug的數目也會(huì )隨之增長(cháng),當然,bug產(chǎn)生的原因包括內部因素和外部因素。內部因素的的確確是我們的代碼的問(wèn)題,而外部因素實(shí)際上并不是程序的bug,但是無(wú)論如何必須修正。
說(shuō)得更詳細點(diǎn),外部因素可能是你調用的代碼改變了,或者是運行環(huán)境改變了。例如,一段Java代碼在JRE1.4下面工作得很好,客戶(hù)要求它升級到JRE?1.5下也能運行。這個(gè)時(shí)候,如果不能運行,你可能會(huì )聲明原因不在程序上。但是客戶(hù)可不管那么多,站在他的角度上,這就是你的程序的bug。
實(shí)際上,完美的程序是不存在的,在做項目計劃的時(shí)候必須考慮周全。因此,在開(kāi)始編碼之前,你就應該把維護的開(kāi)銷(xiāo)算到軟件開(kāi)發(fā)周期里去。甚至在思考怎樣編碼之?前就應該思考怎樣去測試。而且,你還得考慮怎樣讓服務(wù)器自動(dòng)測試你的代碼——如果一個(gè)測試不能做到完全自動(dòng)化,那么它只能算作半個(gè)測試。只有讓測試做到完?全自動(dòng)化,團隊才能在新的環(huán)境里毫不費勁的測試自己的代碼,也可以迅速高效地對測試進(jìn)行擴展。
剛剛提到的,你必須找到一種可行的方法能夠保證你的代碼能夠被自動(dòng)地測試,的確如此,但這也是在單元測試中存在的一個(gè)問(wèn)題,因為單元測試不可能面面俱到。事實(shí)上,如果對測試進(jìn)行分層,測試的效率可能會(huì )大大提高(關(guān)于分層測試,將在后面作具體的闡述)。
2.開(kāi)發(fā)人員討厭測試,他們不按照測試的規范去做
代碼在某個(gè)環(huán)境中能正常工作,就假想它在其它環(huán)境中也能正常工作,這是軟件開(kāi)發(fā)人員的通病。在現實(shí)生活中,這根本就是不可能的,可開(kāi)發(fā)人員卻生活在虛擬的完美世界中,他們認為這個(gè)世界里萬(wàn)事萬(wàn)物都是完美的,不可能遭遇任何的問(wèn)題。例如,一個(gè)J2EE程序在JBoss下工作正常,開(kāi)發(fā)人員往往理所當然地認為在WebSphere下也能正常工作,事實(shí)卻未必。所以,開(kāi)發(fā)人員有必要在所有的目標平臺上測試代碼。否則,程序就有可能無(wú)法通過(guò)。
我相信,你一定需要一個(gè)框架,使你的程序能夠在任何平臺下工作,而且能夠把測試結果保存到數據庫中。概括地講一下過(guò)程:你寫(xiě)好各種語(yǔ)言的測試代碼,它就可以?在各種平臺下自動(dòng)測試,保存測試結果。之后,你就可以查閱測試結果了。這個(gè)結果包括各個(gè)平臺下、各個(gè)版本的代碼的測試的歷史。
據我所知,某些工具支持這些功能。例如,BuildBot?開(kāi)源項目正在為之而努力,至少在不久的將來(lái)可以實(shí)現這個(gè)目標。
3.出現bug時(shí),分析原因比修正錯誤更耗時(shí)間
當開(kāi)發(fā)人員修改好代碼,并把它提交到代碼庫里的時(shí)候,你必須反應迅速,及時(shí)地測試他(她)提交的代碼,這樣,如果測試不通過(guò),開(kāi)發(fā)人員就還記得他(她)剛剛修正過(guò)的地方,也很容易找到bug的原因所在。
時(shí)間一長(cháng),他可能就忘了,Bug出現時(shí)不得不查看代碼歷史,比較不同版本的代碼,直到代碼原因分析出來(lái)。很有可能光找原因就白白的耗費了幾個(gè)小時(shí),太不值得了!
4.開(kāi)發(fā)人員在轉換項目過(guò)程中花費了太多的時(shí)間
通常來(lái)說(shuō),搭建一個(gè)項目的開(kāi)發(fā)環(huán)境是不容易的。如果對項目沒(méi)有很好的了解,不具備一定的專(zhuān)業(yè)知識,要搭建好環(huán)境幾乎是不可能的。如果僅僅是搭建環(huán)境就花費很?長(cháng)的時(shí)間,開(kāi)發(fā)人員往往更希望長(cháng)期呆在同一個(gè)項目環(huán)境中。比較理想的狀況是:開(kāi)發(fā)人員有能力毫不費力的從一個(gè)項目轉到另一個(gè)項目,不存在技術(shù)難題,“項目?文化”已經(jīng)不是個(gè)難題了。
對此,我有個(gè)建議:使用項目管理工具,比如Apache?Maven。用Maven描述的項目一般很容易在Eclipse中搭建起來(lái)。對于Java項目來(lái)說(shuō),只需要要從SVN下載代碼,鍵入“maven?eclipse”,刷新,它就可以工作了。所有與代碼對應的測試代碼也可以運行!哇,簡(jiǎn)直是太神奇了!整個(gè)項目不到30秒就建好了。
很可惜,這項技術(shù)目前還只支持Java。至于其它的語(yǔ)言,如Python、C,等等,僅僅Maven還是不夠的。
5.隨著(zhù)開(kāi)發(fā)人員的增加,Bug也增加
如果方法不得當,一般來(lái)說(shuō),開(kāi)發(fā)人員增加的同時(shí),程序的bug數目也會(huì )增多;隨著(zhù)程序的復雜性的增大,軟件的質(zhì)量也會(huì )下降。而我們必須與之作斗爭。那采取什么方法呢?很簡(jiǎn)單的理論:透明、代碼審查和自動(dòng)測試。
透明能夠確保每個(gè)人都能得到代碼,決定誰(shuí)去修改它,何時(shí)修改,或者是為什么要修改它。透明能夠確保團隊的每個(gè)人都會(huì )因為自己的代碼質(zhì)量而感到壓力,并由此而產(chǎn)生動(dòng)力。
而代碼審查呢?開(kāi)發(fā)人員對別人的代碼進(jìn)行審查,不僅能夠從別人的代碼中學(xué)到東西,而且它還確保開(kāi)發(fā)人員有效地發(fā)現代碼的問(wèn)題,對提高代碼質(zhì)量功不可沒(méi)。
至于自動(dòng)測試,我們在下面進(jìn)行詳解。
6.分層測試
不可否認,寫(xiě)出好的測試代碼的確是個(gè)很大的挑戰。例如,代碼寫(xiě)好后,對它們進(jìn)行單元測試就很困難,單元測試不可能測試到程序運行的方方面面。功能性(質(zhì)量保證)測試對程序測試相當有效,不過(guò)它通常運行慢,對問(wèn)題發(fā)生的原因給出的信息往往很不夠。
因此,你不得不對測試進(jìn)行拆分以克服這種情況。如果簡(jiǎn)單的基礎測試都無(wú)法通過(guò),那么運行更大的測試顯然是無(wú)意義的。分層測試的目的就是讓測試從易到難。
——第0層:開(kāi)發(fā)人員在自己本地環(huán)境中進(jìn)行的單元測試。
——第1層:構建服務(wù)器上的單元測試,(幾乎每個(gè))代碼提交之后開(kāi)始。
——第2.1層:集成測試,在幾個(gè)模塊整合到一起之后進(jìn)行的測試。(用真正的模塊代替掉原先的模擬模塊。例如,使用真正的XML解析器換掉原先的那個(gè)替代品)
——第2.2層:集成測試,通過(guò)入口訪(fǎng)問(wèn)系統(接口、磁盤(pán)等等,到這兒,你可以測試訪(fǎng)問(wèn)真正的系統)
——第3層:功能性測試,測試程序可見(jiàn)的部分。(用戶(hù)能夠看到的實(shí)實(shí)在在的功能,當然,這一層還不夠)
——第4層:性能測試。
——第5層:客戶(hù)體驗。(OK,這并不是一個(gè)測試層,但是這個(gè)環(huán)節的確會(huì )發(fā)現不少意料之外的bug)
這個(gè)方法的理念是同一個(gè)bug決不會(huì )再次重現。因而,對于每個(gè)你發(fā)現的bug,你都應該創(chuàng )建測試用例以保證它不會(huì )重現。這個(gè)測試可能是單元測試(這樣最好),也有可能是集成測試。
總之,問(wèn)題發(fā)現得越早越好,這意味著(zhù)我們可以更早修復它。所以如果某個(gè)問(wèn)題能夠盡量的在第0層發(fā)現,我們就不要讓它等到第3層才發(fā)現。隨著(zhù)測試代碼的增加,代碼質(zhì)量也會(huì )越來(lái)越健壯。
7.嚴格限制使用編程語(yǔ)言的數目
如果你無(wú)論完成什么任務(wù)都為它選擇最好的工具,那么當你的任務(wù)越來(lái)越多,你采用的工具也越來(lái)越多,這可不是什么好事。如果你總是為短期的目標選擇最便捷的方?法,系統的復雜性會(huì )隨之增加。所以,有時(shí)候,你不一定要選擇最好的工具。在同一個(gè)團隊里,最好只使用一門(mén)語(yǔ)言,盡量不要超過(guò)兩種語(yǔ)言,否則單單培訓開(kāi)發(fā)人?員的成本就不少。例如,Ruby?on?Rails就是個(gè)很好的工具,但是它涉及到不止一門(mén)語(yǔ)言和技術(shù)。在用它的某種技術(shù)開(kāi)發(fā)網(wǎng)站之前,必須要考慮所有的花費。如果你們的主要活動(dòng)就是開(kāi)發(fā)網(wǎng)站,那么這條規則對你并不適用,因為在這種情況下,Ruby很可能變成你們項目組的主要語(yǔ)言了。
我的觀(guān)點(diǎn)是,選擇有限數目的編程語(yǔ)言。當然,你也必須確保這種技術(shù)對你們公司有用。例如,有必要用四種網(wǎng)絡(luò )服務(wù)框架嗎?沒(méi)必要,一種足矣。
8.緊跟技術(shù)的發(fā)展和革新,不過(guò)這個(gè)比較困難
我們很容易碰到這種情況,為了實(shí)現某個(gè)功能你必須付出很大的努力,但是實(shí)際上已經(jīng)有工具可以做到這點(diǎn),但是我們卻不知道。如果我們關(guān)注著(zhù)技術(shù)的發(fā)展,包括開(kāi)?發(fā)框架的革新,方法的提出等等,就可以避免這種情況的出現。幸虧有這種關(guān)注的習慣,我發(fā)現了現在我用得幾乎所有的工具:Eclipse,?Maven,?Tomcat,?Apache,?LDAP,?CruiseControl,?TestNG,?Python,等等。它們中大多數工具都可以為我節省很多工作。有的項目本來(lái)需要幾個(gè)月的開(kāi)發(fā)時(shí)間,結果縮短到幾周。
比較困難的就是如何去選擇工具(要花費很多時(shí)間去選擇工具),而且還得避免你的工作團隊同時(shí)使用太多的技術(shù)。有個(gè)建議,當使用某項技術(shù)還在實(shí)驗之中的時(shí)候,我們最好不要用,最好等到它已經(jīng)實(shí)現了,有成熟的產(chǎn)品了。
另外一個(gè)挑戰就是評估、選擇新的技術(shù)。如果要對旗鼓相當、都是很有競爭力的框架進(jìn)行評估,并從中選擇一兩個(gè)的話(huà),這個(gè)難度一般也很大。
9.為了保持效率,最好不要被頻繁地打斷
在解決復雜的問(wèn)題的,開(kāi)發(fā)人員一般需要7到15分鐘進(jìn)入高效的狀態(tài)。如果從一個(gè)活動(dòng)轉到另外一個(gè)活動(dòng)(電話(huà),電子郵件等等)就會(huì )打斷開(kāi)發(fā)人員的進(jìn)程。例如,如果一個(gè)團隊的開(kāi)發(fā)人員每20分鐘就有個(gè)技術(shù)服務(wù)的電話(huà)騷擾他,哦,這完全是災難性的。因此,我們不應該允許用戶(hù)直接打開(kāi)發(fā)人員的電話(huà)。
取而代之,我們應該建立一個(gè)工具收集問(wèn)題、bug、需求等等來(lái)完成這些工作,這樣開(kāi)發(fā)人員就可以專(zhuān)心致志地忙他的本職工作。這樣的工具很多,包括Mantis?和Jira。
另外一點(diǎn),記住,開(kāi)發(fā)人員并不是機器,他是人。為什么項目經(jīng)理要求開(kāi)發(fā)人員記住——存儲——他們的要求?難道他就不能把他的要求寫(xiě)下來(lái)嗎?
10.定義好構架與編碼同等重要
在沒(méi)有構架的基礎上進(jìn)行編碼如同在沒(méi)有燈光的夜晚駕車(chē)。你有可能達到你的目的地,但是行程很慢,也很危險。如果有構架師,他就會(huì )經(jīng)常審核原始的產(chǎn)品,預測可能出現的問(wèn)題。缺少了構架師,系統能難有很好的框架,如果每次加進(jìn)一個(gè)新功能,系統復雜度就會(huì )大大增加。
即使編碼沒(méi)問(wèn)題,據我所見(jiàn)到的走捷徑的項目,基本上都多多少少存在些問(wèn)題。這就是產(chǎn)品構架師應該對產(chǎn)品的框架有一個(gè)全局的概念的重要原因。
11.開(kāi)發(fā)人員和產(chǎn)品使用者考慮的重點(diǎn)不同
開(kāi)發(fā)人員總是對“有趣”的任務(wù)感興趣,而產(chǎn)品的使用者總是希望產(chǎn)品的功能對他的客戶(hù)有用。開(kāi)發(fā)過(guò)程中,應該盡量地做到,至少應該使這兩個(gè)目標比較接近:讓開(kāi)發(fā)人員解決比較無(wú)趣的問(wèn)題時(shí)也讓他很高興。
據我所看到,Scrum就?是個(gè)不錯的軟件管理方法。它主張在一個(gè)開(kāi)發(fā)過(guò)程里,開(kāi)發(fā)人員準時(shí)地提交結果,這時(shí),開(kāi)發(fā)人員的自尊心就會(huì )成為一個(gè)強大的動(dòng)力。當然,當項目經(jīng)理提出某一個(gè)?用處并不大的需求時(shí),如果開(kāi)發(fā)人員很難準時(shí)提交結果,也就是說(shuō),這個(gè)功能需求在實(shí)現上困難很大,但是又沒(méi)有很大的用處,這個(gè)時(shí)候項目經(jīng)理應該在提需求之前?三思,是否有必要提這個(gè)需求。當然,有時(shí)間可以研究一下,Scrum方法的確相當不錯。
12.代碼規約很高效,必須強制執行
代碼規約把開(kāi)發(fā)人員從理解代碼的格式上解放出來(lái),讓他們有更多的時(shí)間去理解代碼的核心含義。在我工作的團隊里,我們建立了自己的規約,包括代碼存放的目錄,文件名,還有代碼本身(類(lèi)的名字、方法、變量等等)。