陳 暉 李紅波 柏立悅
(浙江中控技術(shù)股份有限公司)
軟件開發(fā)周期逐漸成為相關(guān)企業(yè)考量的關(guān)鍵因素,這也是快速迭代的開發(fā)模式在如今的應(yīng)用軟件開發(fā)中越來越流行的原因之一。 對(duì)于一個(gè)工業(yè)領(lǐng)域的公司來講,軟件質(zhì)量是重中之中。 那么在工業(yè)信息化程度日漸提高的今天,保證軟件質(zhì)量與實(shí)現(xiàn)快速交付成了工業(yè)軟件公司的主要矛盾。 因此,筆者介紹了在遇到工業(yè)應(yīng)用軟件開發(fā)效率瓶頸時(shí), 一種分析和尋找根本原因的思路。工控行業(yè)一直以來的主流技術(shù)棧為C/C++,特別是應(yīng)用層的軟件主要集中在C++,因此筆者針對(duì)C++軟件開發(fā)中遇到的相關(guān)問題,分析造成這些問題的根本原因。
對(duì)軟件項(xiàng)目經(jīng)理來說,最重要的是把握軟件的發(fā)布節(jié)點(diǎn)。 關(guān)鍵點(diǎn)在于要對(duì)總?cè)蝿?wù)量和總生產(chǎn)力兩個(gè)方面進(jìn)行準(zhǔn)確評(píng)估,以確保兩者相契合。
總?cè)蝿?wù)量即完成軟件項(xiàng)目需要的全部客觀有效工作量,計(jì)算方式可簡化如下:
如果想要進(jìn)行更細(xì)致的評(píng)估,則需要采取更復(fù)雜的方式,如類比建模法[1]、階段評(píng)估法[2]等。這些方法基于以往類似項(xiàng)目的經(jīng)驗(yàn)進(jìn)行推導(dǎo),往往能較準(zhǔn)確地對(duì)項(xiàng)目的總?cè)蝿?wù)量進(jìn)行評(píng)估。
總生產(chǎn)力指的是一個(gè)團(tuán)隊(duì)在一定時(shí)期內(nèi)實(shí)際可以提供的開發(fā)成果產(chǎn)出:
由此可見,想要精確地掌握時(shí)間節(jié)點(diǎn),不僅要準(zhǔn)確評(píng)估總?cè)蝿?wù)量,還要準(zhǔn)確地評(píng)估出團(tuán)隊(duì)的總生產(chǎn)力,以確保其能覆蓋項(xiàng)目的總?cè)蝿?wù)量。 而想要準(zhǔn)確評(píng)估總生產(chǎn)力,就必須準(zhǔn)確地了解每個(gè)開發(fā)人員在特定領(lǐng)域的能力。 一旦某個(gè)人的實(shí)際生產(chǎn)效率與預(yù)估的發(fā)生較大的偏差,那么關(guān)鍵路徑就會(huì)大幅變長。 而如果在項(xiàng)目后期才發(fā)現(xiàn)偏差,那么即使投入人海戰(zhàn)術(shù)也不能對(duì)加快進(jìn)度有明顯的改善,甚至?xí)a(chǎn)生負(fù)面作用——因?yàn)樵鹊拈_發(fā)人員需要對(duì)新加入的開發(fā)人員進(jìn)行培訓(xùn),并且更多的人意味著更高的溝通成本[3]。
因此,準(zhǔn)確地評(píng)估每個(gè)開發(fā)人員可以做出的貢獻(xiàn),消除項(xiàng)目中的不穩(wěn)定因素,是保障軟件項(xiàng)目更快、更及時(shí)完成的關(guān)鍵點(diǎn)。
一個(gè)優(yōu)秀的編程者的標(biāo)志在于能夠快速寫出精簡而又清晰易懂、符合客戶需求、具有良好擴(kuò)展性的代碼。 這樣的編程者們,是一個(gè)項(xiàng)目組的核心骨干,往往可以承擔(dān)一個(gè)軟件項(xiàng)目中大量核心代碼的編寫工作。 Sackman H 等早期的研究指出,水平較差的編程者的編碼時(shí)間最多可達(dá)優(yōu)秀編程者的25 倍,調(diào)試時(shí)間則最多為28 倍[4]。
而筆者也在已開發(fā)完成的軟件項(xiàng)目中做過統(tǒng)計(jì),約20%的核心技術(shù)骨干可以完成約50%的功能開發(fā), 而考慮到這些代碼重要程度的權(quán)重,其貢獻(xiàn)率最高可以達(dá)到80%。 由此可見,每個(gè)軟件開發(fā)人員所能做出貢獻(xiàn)的差異很大,所以找到合適的評(píng)估方法就顯得尤為重要。
開發(fā)貢獻(xiàn)率的評(píng)估方法有很多,但大多是基于一個(gè)開發(fā)人員的已有開發(fā)成果進(jìn)行推斷,例如傳統(tǒng)的方法有考察日均代碼行數(shù)、千行代碼缺陷率、代碼圈復(fù)雜度,而近年來則有人嘗試更復(fù)雜的建模方法,來達(dá)到更準(zhǔn)確的評(píng)估目的。 筆者將這些方法及其缺陷整理如下:
a. 日均代碼行數(shù)。 即考察開發(fā)人員每天產(chǎn)出的代碼數(shù)量。 該方法完全忽略了代碼的質(zhì)量。
b. 千行代碼缺陷率。 即通過測(cè)試結(jié)果來考察開發(fā)人員所寫代碼在邏輯上的質(zhì)量。 但該方法忽略了開發(fā)過程的差異性——如果相同的功能,人員A 比人員B 抽象提煉能力更強(qiáng),在更好地考慮了復(fù)用性、擴(kuò)展性、簡潔性后,產(chǎn)出的代碼比人員B 產(chǎn)出的代碼數(shù)量要少,在缺陷數(shù)同樣的情況下,人員A 的千行代碼缺陷率比人員B 要高,但顯然人員A 產(chǎn)生的效益比人員B 要高。
c. 圈復(fù)雜度。 即考察一個(gè)代碼模塊的復(fù)雜度。 在沒有對(duì)照的情況下,該方法也不能很好地反映出開發(fā)人員的貢獻(xiàn)率,因?yàn)樗从车氖且粋€(gè)模塊最后實(shí)際產(chǎn)出的代碼呈現(xiàn)出的復(fù)雜度,而不是其應(yīng)有的復(fù)雜度,而一個(gè)模塊本身應(yīng)有的復(fù)雜度是很難衡量的,所以也就無從比較。 除非兩個(gè)程序員都實(shí)現(xiàn)同一個(gè)模塊的代碼,那么這兩份代碼的圈復(fù)雜度能較好地體現(xiàn)出他們技術(shù)水平的差距(圈復(fù)雜度越低的反而越好,表明其能把復(fù)雜的事情簡化)。
d. 數(shù)學(xué)建模。 以上的方法單一應(yīng)用時(shí)都有相當(dāng)大的局限性,因此近年來有人試圖使用數(shù)學(xué)建模的方法把各種因素結(jié)合在一起,并使用人工智能的方法得出一套實(shí)際可用的評(píng)價(jià)系統(tǒng)。 例如Ren J L 等基于其研究成果開發(fā)的Merico 評(píng)價(jià)系統(tǒng)將代碼的貢獻(xiàn)值分成結(jié)構(gòu)化數(shù)據(jù)和非結(jié)構(gòu)化數(shù)據(jù),并用機(jī)器學(xué)習(xí)的方法將這兩種數(shù)據(jù)擬合在一起,得出最終的貢獻(xiàn)值[5]。其中結(jié)構(gòu)化數(shù)據(jù)主要由一個(gè)函數(shù)的被調(diào)用次數(shù)決定,而非結(jié)構(gòu)化數(shù)據(jù)則由機(jī)器學(xué)習(xí)來判斷某條代碼提交的重要性。Merico 評(píng)價(jià)系統(tǒng)確實(shí)在一定程度上解決了編程貢獻(xiàn)值評(píng)估難的問題,但也有其局限性。 例如該系統(tǒng)判斷結(jié)構(gòu)化貢獻(xiàn)值的方式是統(tǒng)計(jì)一個(gè)函數(shù)的被調(diào)用次數(shù),其驗(yàn)證手段是考察3 個(gè)開源社區(qū)貢獻(xiàn)者的開源代碼來實(shí)現(xiàn)的。 由此可知其局限性在于,首先,開源社區(qū)貢獻(xiàn)者的開發(fā)水平屬于中上游,不具有全面的代表性;其次,一般地,在一個(gè)大中型公司中, 軟件的模塊劃分更為細(xì)致,基礎(chǔ)模塊(通常來說代碼質(zhì)量、穩(wěn)定性及可閱讀性等要求都相對(duì)更高)比上層的應(yīng)用模塊有更多的機(jī)會(huì)被調(diào)用到,但無法就此推斷出負(fù)責(zé)基礎(chǔ)模塊的程序員其代碼貢獻(xiàn)率就一定比負(fù)責(zé)應(yīng)用模塊的大。 而基礎(chǔ)模塊和應(yīng)用模塊通常是不同的項(xiàng)目組(例如公共組件組和產(chǎn)品項(xiàng)目組)負(fù)責(zé)的,對(duì)于同一個(gè)組的成員來說,他們負(fù)責(zé)的模塊層次是相對(duì)扁平化的,那么函數(shù)被調(diào)用的幾率實(shí)質(zhì)上是均等的。 所以實(shí)際上這是一個(gè)從結(jié)果推出條件的偽命題,因?yàn)樵降讓釉交A(chǔ)的模塊,通常會(huì)被多個(gè)產(chǎn)品線共用,其重要性不言而喻,一般會(huì)讓技術(shù)功底深厚的老員工來擔(dān)綱,那么他們編寫的代碼自然而然地具有更好的質(zhì)量,而不是因?yàn)樗麄兊慕涌诒皇褂酶嗑驼J(rèn)為他們的代碼質(zhì)量更好。
由上文可見,目前大部分方法都不能客觀準(zhǔn)確地評(píng)估貢獻(xiàn)率,實(shí)際日常操作也仍是以項(xiàng)目經(jīng)理依據(jù)對(duì)團(tuán)隊(duì)成員能力上的印象進(jìn)行判斷為主。如果不是一個(gè)參與架構(gòu)設(shè)計(jì)和日常代碼評(píng)審的項(xiàng)目經(jīng)理,很難做到準(zhǔn)確公平地評(píng)估每個(gè)組員的真實(shí)貢獻(xiàn)。 事實(shí)上這種情況在企業(yè)中很常見,由于項(xiàng)目管理流程的需要,項(xiàng)目經(jīng)理無法在具體開發(fā)工作上投入過多精力,一般會(huì)把這些任務(wù)轉(zhuǎn)交給組內(nèi)的技術(shù)專家或資深員工,這就造成項(xiàng)目經(jīng)理對(duì)開發(fā)人員的真實(shí)水平很難有準(zhǔn)確的把握。
通常在項(xiàng)目初期,項(xiàng)目經(jīng)理會(huì)較均勻地分配任務(wù),原因是出于對(duì)團(tuán)隊(duì)成員的信任,避免他們認(rèn)為被邊緣化。 而隨著開發(fā)過程的推移,在最初任務(wù)分配均等的前提下,高水平開發(fā)者的進(jìn)度一般領(lǐng)先于其他員工。 此時(shí)項(xiàng)目經(jīng)理出于項(xiàng)目進(jìn)度考慮,基本會(huì)轉(zhuǎn)移部分可分離的任務(wù)到骨干員工身上。 以上的做法往往導(dǎo)致高水平的開發(fā)人員不堪重負(fù)——不但要承擔(dān)架構(gòu)設(shè)計(jì),還要負(fù)責(zé)大部分功能的實(shí)現(xiàn)。 而低水平的開發(fā)人員只能在邊緣的功能上做文章, 對(duì)于他們的技術(shù)成長收效甚微,即便公司給予培訓(xùn)上的支持,但缺乏實(shí)踐使得知識(shí)很難得到沉淀。 如果給予他與其水平不符的任務(wù),然后花費(fèi)高水平開發(fā)者的時(shí)間來仔細(xì)評(píng)審他們的設(shè)計(jì)和代碼,確實(shí)能更快速地提高其水平,從長遠(yuǎn)來看也更有意義,但也得忍受當(dāng)前項(xiàng)目數(shù)倍的開發(fā)周期和更多的錯(cuò)誤數(shù)量。
因此,一個(gè)有時(shí)間節(jié)點(diǎn)壓力的項(xiàng)目型團(tuán)隊(duì)就容易陷入到一個(gè)不好的循環(huán)中來,整個(gè)團(tuán)隊(duì)總是疲于應(yīng)付開發(fā)任務(wù),其整體水平只能以一個(gè)很平緩的曲線上升。 這對(duì)于公司和員工來說是一個(gè)雙輸?shù)木置妫簩?duì)公司來說,一個(gè)開發(fā)團(tuán)隊(duì)的成本日益上升,但其開發(fā)水平并沒有得到相應(yīng)程度的提高,其價(jià)值其實(shí)是日益下降的;對(duì)于員工來說,在相當(dāng)長的時(shí)間內(nèi)沒有得到成長,那么對(duì)自身的發(fā)展和職業(yè)生涯是很不利的。
可以推斷,軟件項(xiàng)目周期的穩(wěn)定性主要是依據(jù)非核心開發(fā)人員。 一個(gè)項(xiàng)目的關(guān)鍵因素在核心員工身上, 但關(guān)鍵路徑卻體現(xiàn)在非核心員工身上。 如前文所述,項(xiàng)目經(jīng)理依賴于工作經(jīng)驗(yàn)豐富的核心員工來保障項(xiàng)目進(jìn)度,那么相反地,非核心員工在開發(fā)不同產(chǎn)品或不同模塊時(shí)所表現(xiàn)出的不穩(wěn)定性正是影響項(xiàng)目進(jìn)度的最大因素。 他們更容易受開發(fā)平臺(tái)、產(chǎn)品特點(diǎn)的影響,這種不穩(wěn)定性就反映在他們的開發(fā)周期和開發(fā)質(zhì)量上。
綜上所述,非核心員工的開發(fā)效率、質(zhì)量相對(duì)不穩(wěn)定,導(dǎo)致項(xiàng)目組的資源配置也不能達(dá)到最優(yōu)化,因此項(xiàng)目的進(jìn)度風(fēng)險(xiǎn)就會(huì)變大。
首先對(duì)兩個(gè)不同水平的C++開發(fā)人員所能做出的貢獻(xiàn)有巨大差距這個(gè)現(xiàn)象做出一個(gè)分析:
a. 面向?qū)ο蟊让嫦蜻^程在設(shè)計(jì)上需要更多的努力和領(lǐng)悟[6]。對(duì)于大部分C++程序員來說,在學(xué)校學(xué)習(xí)的課程以C 語言居多,或者學(xué)習(xí)過C++課程,但并未領(lǐng)悟面向?qū)ο蟮乃枷耄趧偼度腴_發(fā)工作中時(shí)仍把C++當(dāng)成C 語言來使用。 事實(shí)上領(lǐng)悟面向?qū)ο蠛茈y,也需要一定的天賦,因?yàn)槊嫦驅(qū)ο笾皇且粋€(gè)概念, 其背后蘊(yùn)含的是抽象、提取、 總結(jié)事物內(nèi)在邏輯和聯(lián)系的本領(lǐng)與技巧,即便一個(gè)有多年C++使用經(jīng)驗(yàn)的程序員都不敢宣稱完全掌握其精髓。
b. 新手往往知識(shí)面不夠豐富。對(duì)于一個(gè)核心開發(fā)人員來說,他可能兼具編程知識(shí)的廣度與深度,擁有不同項(xiàng)目的開發(fā)經(jīng)驗(yàn),負(fù)責(zé)過不同領(lǐng)域的產(chǎn)品和模塊,文件讀寫、網(wǎng)絡(luò)通信等不同方面的知識(shí)對(duì)于他們來說已經(jīng)成為常規(guī)武器。 而對(duì)于一個(gè)新手來說,處理產(chǎn)品的業(yè)務(wù)邏輯就已經(jīng)耗費(fèi)其精力,其他方面不可能以一種十分自然、毫不費(fèi)力的方式把它們處理正確。
c. 新手往往自信心不足。 新手由于缺乏項(xiàng)目經(jīng)驗(yàn),即便已經(jīng)儲(chǔ)備了很豐厚的理論知識(shí),但在實(shí)際開發(fā)過程中, 對(duì)自己寫的代碼的不自信,追求盡量少犯錯(cuò),依然會(huì)用一種比較初級(jí)但自己更熟悉的方式去編寫代碼。 但在商業(yè)化項(xiàng)目的開發(fā)中,邏輯上的正確往往是不夠的,性能、可維護(hù)性等方面都是需要考量的因素。
以上原因使新手在開發(fā)過程中會(huì)遇到很多困難和疑惑。 以下是一個(gè)案例,該段代碼用于實(shí)現(xiàn)查詢用戶列表中是否有“admin”用戶的功能:
運(yùn)行這段代碼雖然能獲得正確的結(jié)果,但可從一個(gè)代碼評(píng)審者的角度來討論其不足之處:
a. bFound 變量多余,iter 和userIdList.end()比較即可實(shí)現(xiàn)同樣的效果;
b. iter++改為++iter 更佳,避免每次產(chǎn)生一個(gè)臨時(shí)變量和賦值動(dòng)作;
c. 在if(*iter==“admin”)分 支 中 應(yīng) 該 添 加break,否則找到“admin”后仍會(huì)繼續(xù)執(zhí)行,導(dǎo)致浪費(fèi)計(jì)算機(jī)的性能;
d. for 循環(huán)終止條件中的userIdList.end(),應(yīng)該使用臨時(shí)變量進(jìn)行緩存,以避免重復(fù)的函數(shù)調(diào)用削弱性能。
一個(gè)簡單功能的實(shí)現(xiàn),卻有至少4 處改進(jìn)之處,且b、d 兩條是絕大多數(shù)新手沒有理解和掌握的。 在一些服務(wù)器端的開發(fā)中,這些缺陷可能造成一定程度的性能問題,而針對(duì)這些缺陷進(jìn)行優(yōu)化后,某些情況下性能甚至可以提升20%以上[7]。
因此, 一個(gè)新手或者低水平的開發(fā)人員,與資深開發(fā)人員的差距是全方位的,如果要求他們一開始就去處理編程的各個(gè)方面, 如數(shù)據(jù)結(jié)構(gòu)、網(wǎng)絡(luò)交互及操作系統(tǒng)等, 那么他們便會(huì)顧此失彼,從而影響編程效率和質(zhì)量,最終開發(fā)周期變長甚至成倍增長。 如果想減少這種不利的因素,那么就必須創(chuàng)造環(huán)境來盡量避免新手同時(shí)面對(duì)多個(gè)他不熟悉的領(lǐng)域,從而使其精力足以應(yīng)付一定的不確定性,并穩(wěn)步地成長。
筆者闡述了把控項(xiàng)目進(jìn)度的關(guān)鍵因素為團(tuán)隊(duì)總生產(chǎn)力的評(píng)估,介紹了評(píng)估開發(fā)人員貢獻(xiàn)率的方法及其局限性,指出開發(fā)貢獻(xiàn)率的評(píng)估難點(diǎn)是軟件項(xiàng)目發(fā)生進(jìn)度偏差的一大原因,進(jìn)一步指出造成偏差的深層原因是新手在面對(duì)多個(gè)不熟悉領(lǐng)域時(shí)所表現(xiàn)出的不穩(wěn)定性,這些為企業(yè)優(yōu)化軟件開發(fā)流程、改進(jìn)開發(fā)環(huán)境提供了一種思路。