彈性研發(fā)團隊的探索
1.背景
1.1困境
團隊內(nèi)一位測試者對接多位開發(fā)者,開發(fā)者的需求提測速度遠大于測試者的測試速度,導(dǎo)致開發(fā)者提測的需求堆積待測試,無法及時上線,團隊測試資源匱乏的問題愈加凸顯,直接影響團隊的需求交付速度。
圖1-開發(fā)工作流
測試資源匱乏的問題在支付組中尤為嚴重,且支付項目業(yè)務(wù)復(fù)雜、上手周期長,要求開發(fā)者與測試者盡可能的穩(wěn)定,短期內(nèi)引進新人也很難解決問題。因此團隊的目標(biāo)是在現(xiàn)有資源供給下,不進行人員變動,而通過優(yōu)化團隊內(nèi)部的質(zhì)量保證工作量的分配,既保證團隊穩(wěn)定與研發(fā)質(zhì)量,又提升需求交付速率。
1.2剖析
?1.2.1 概念
在解構(gòu)團隊困境之前,首先定義若干概念,建立一個簡單的數(shù)學(xué)模型進行分析。
(1) 開發(fā)側(cè)
? 開發(fā)資源:團隊內(nèi)部的開發(fā)者人數(shù),例如10人;
? 單位提測速率:每個開發(fā)者平均每天提測的需求數(shù),例如2個/人天;
? 提測速率:團隊平均每天提測的需求數(shù),顯然有:提測速率=單位提測速率×開發(fā)資源=2×10=20個/天。
圖2-提測速率公式
(2) 測試側(cè)
? 測試資源:團隊內(nèi)部的測試者人數(shù),例如2人;
? 單位測試速率:每個測試者平均每天測試完成的需求數(shù),例如4個/人天;
? 測試速率:團隊平均每天測試完成的需求數(shù),顯然有:測試速率=單位測試速率×測試資源=4×2=8個/天。
圖3-測試速率公式
(3) 上線
? 需求交付速率:指團隊平均每天上線交付的需求數(shù),由于需求通過測試后基本就能上線了,顯然有:需求交付速率≈測試速率=8個/天。
圖4-需求交付速率
圖5-提測速率>測試速率,需求堆積,需求交付速率受限
?1.2.2 提測速率與測試速率的大小關(guān)系
下面分析一下提測速率與測試速率的三種大小關(guān)系:
(1) 提測速率>測試速率
存在的問題:我們團隊中目前存在這種大小關(guān)系,雖然資源都被充分利用,但資源分配不合理,導(dǎo)致開發(fā)者提測的需求堆積待測試,需求無法及時交付,需求交付速率與團隊分配到的資源不匹配。
解決方案一:重新分配資源,減少開發(fā)資源,增加測試資源;
解決方案二:重新分配質(zhì)量保證工作量,將一部分工作量由測試者轉(zhuǎn)移至開發(fā)者。
(2) 提測速率<測試速率
存在的問題:資源未被充分利用,開發(fā)資源被充分利用,但測試資源出現(xiàn)閑置。
解決方案一:重新分配資源,增加開發(fā)資源,減少測試資源;
解決方案二:重新分配質(zhì)量保證工作量,將一部分工作量由開發(fā)者轉(zhuǎn)移至測試者。
(3) 提測速率≈測試速率
這是一種理想情況,資源都被充分利用,且資源分配合理,需求交付速率與團隊分配到的資源匹配。
?1.2.3 提測速率與測試速率的動態(tài)平衡
很顯然,我們團隊的現(xiàn)狀是:提測速率>測試速率,目標(biāo)是:提測速率≈測試速率,解決方案是:提升測試速率和/或降低提測速率。
圖6-現(xiàn)狀、方案與目標(biāo)
(1) 測試側(cè)
由于測試資源固定,因此要提升測試速率,只能提升單位測試速率,即提升每個測試者平均每天測試完成的需求數(shù),例如從4個/人天提升至6個/人天。
圖7-提升測試速率
問題:如何提升單位測試速率?
這里再引入幾個概念“交付能力”與“測試復(fù)雜度”:
? 測試復(fù)雜度:指平均每個需求需要消耗多少測試資源才能測試完成,例如2人天/個表示平均每個需求需要2個測試者花1天時間才能測試完成;
? 交付能力:指測試者平均每天交付工作成果的能力,交付能力與測試者的“工作能力”、“工作時長”有關(guān),因此有如下關(guān)系:
圖8-交付能力
交付能力是一個沒有單位的系數(shù),該系數(shù)越大,表示測試者的交付能力越強。
因此單位測試速率與交付能力、測試復(fù)雜度有如下關(guān)系:
圖9-單位測試速率
目前測試資源都被充分利用,工作都已飽和,工作時長無法提高;工作能力的提升也不是一蹴而就的,短期內(nèi)工作能力也是一個常數(shù),因此要提升單位測試速率只能降低測試復(fù)雜度。
圖10-提升單位測試速率
問題:如何降低測試復(fù)雜度?
最直觀的方式就是“減少測試者測試完成每個需求的工作量”,把測試承擔(dān)的一部分質(zhì)量保證工作量轉(zhuǎn)移出去,例如之前測試者平均需要為每個需求創(chuàng)建并測試10個用例,轉(zhuǎn)移出去后僅需7.5個用例,這樣測試復(fù)雜度就降低了25%。
那么應(yīng)該把這部分質(zhì)量保證工作量轉(zhuǎn)移給誰,由于目前提測速率>測試速率,很顯然把這部分質(zhì)量保證工作量轉(zhuǎn)移至開發(fā)者,下面來看一下轉(zhuǎn)移之后對開發(fā)者的影響。
(2) 開發(fā)側(cè)
對于開發(fā)者,也有交付能力這一概念,同時引入“提測復(fù)雜度”這一概念:
? 提測復(fù)雜度:指平均每個需求需要消耗多少開發(fā)資源才能開發(fā)完成并提測,例如2人天/個表示平均每個需求需要2個開發(fā)者花1天時間才能開發(fā)完成并提測。
開發(fā)者在開發(fā)過程中以及提測之前肯定會進行自測與基本的冒煙測試,因此開發(fā)者在完成一個需求時,除了編寫代碼的工作量外,還需要一定的測試工作量,因此提測復(fù)雜度由開發(fā)復(fù)雜度和測試復(fù)雜度構(gòu)成,有如下關(guān)系:
圖11-單位提測速率
由于把一部分質(zhì)量保證工作量由測試者轉(zhuǎn)移至開發(fā)者,因此開發(fā)者的測試復(fù)雜度升高了,這樣就導(dǎo)致提測復(fù)雜度升高了,最終導(dǎo)致單位提測速率降低了。
圖12-降低單位提測速率
隨著單位提測速率的降低,開發(fā)的提測速率也就降低了。
圖13-降低提測速率
在質(zhì)量保證工作量轉(zhuǎn)移之后,測試者的測試速率升高了,開發(fā)者的提測速率降低了,因此只要把握好轉(zhuǎn)移的量,就能實現(xiàn)提測速率與測試速率的動態(tài)平衡。
圖14-結(jié)果
1.3結(jié)論
經(jīng)過分析發(fā)現(xiàn),可以通過優(yōu)化團隊內(nèi)部質(zhì)量保證工作量的分配,動態(tài)地將一部分質(zhì)量保證工作量由測試者轉(zhuǎn)移至開發(fā)者,就能實現(xiàn)提測速率與測試速率的動態(tài)平衡,最終實現(xiàn)團隊需求交付速率的最大化。
這種工作量的分配優(yōu)化,其實就是團隊內(nèi)部資源的分配優(yōu)化,即使用開發(fā)資源換取測試資源,由開發(fā)者承擔(dān)更多的質(zhì)量保證工作,減少對測試資源的依賴,這樣就在不進行人員變動的情況下,解決了開發(fā)資源與測試資源分配不合理的問題。
1.4團隊動員
在當(dāng)前行業(yè)寒冬之下,看到“由開發(fā)者承擔(dān)更多的測試工作,減少對測試資源的依賴”相關(guān)字眼,大家可能會心生疑慮,彈性研發(fā)是否意味著“壓榨開發(fā)者并實行去測試化”?其實不然,下面分別從開發(fā)和測試的角度進行闡述以消除疑慮。
對于開發(fā),根據(jù)上面的分析,彈性研發(fā)確實增加了開發(fā)者的工作量,但并未要求開發(fā)者增加工作時長,最終單位提測速率也降低了,這意味著彈性研發(fā)在增加開發(fā)者工作量的同時,也相應(yīng)地拉長了開發(fā)者的開發(fā)周期,并不存在壓榨開發(fā)者。只不過要求開發(fā)者采取一定措施保證研發(fā)質(zhì)量,這樣才有信心幫助降低測試者的測試復(fù)雜度。
測試者經(jīng)歷過完整的軟件測試理論學(xué)習(xí)與技能培訓(xùn),是軟件工程中保證質(zhì)量不可或缺的一環(huán),測試者以第三者的視角審視開發(fā)者的產(chǎn)出,并使用其專業(yè)技能揪出隱藏的缺陷。在我們團隊中,測試者正經(jīng)歷長時間的高強度加班,彈性研發(fā)正是為測試者減負,絕不是使用開發(fā)者取代測試者。
2.質(zhì)量保證是系統(tǒng)性的工程
2.1概述
開發(fā)者的疑問:明明我已經(jīng)做得很好了,為什么項目上線后還是出現(xiàn)了bug或不滿足用戶訴求的情況?
質(zhì)量保證是系統(tǒng)性的工程,產(chǎn)品和測試分別是開發(fā)的上游和下游,從產(chǎn)品、到開發(fā)、再到測試,每一環(huán)缺一不可。
2.2產(chǎn)品
對于產(chǎn)品,在需求分析階段,要求產(chǎn)品準(zhǔn)確理解用戶訴求,并針對用戶訴求執(zhí)行縝密的需求分析;在產(chǎn)品設(shè)計階段,要求產(chǎn)品產(chǎn)出清晰的產(chǎn)品原型與完善的產(chǎn)品需求文檔等,這些都是開發(fā)與測試在工作時的重要參考資料;尤其需要強調(diào)產(chǎn)品架構(gòu),它是技術(shù)架構(gòu)的基礎(chǔ),因此要求產(chǎn)品在設(shè)計產(chǎn)品架構(gòu)時具備一定的前瞻性與抽象性。
2.3開發(fā)
對于開發(fā),必須準(zhǔn)確理解產(chǎn)品需求,并根據(jù)產(chǎn)品架構(gòu)設(shè)計科學(xué)合理的技術(shù)架構(gòu);深刻理解項目中使用的各種中間件與框架;編寫的代碼易于維護與閱讀、符合編碼規(guī)范、可測試;在提測前至少進行冒煙測試以及一定程度的正向自測。
2.4測試
對于測試,要求準(zhǔn)確理解產(chǎn)品需求,根據(jù)產(chǎn)品需求文檔對業(yè)務(wù)場景進行分析,編寫科學(xué)且場景覆蓋全面的測試用例,以第三者的視角審視開發(fā)者的產(chǎn)出并揪出隱藏的bug。
3.彈性研發(fā)團隊
3.1概述
彈性研發(fā)團隊主要包含兩部分,彈性和右移:
? 右移:就是讓開發(fā)者在開發(fā)工作流中盡早介入測試,這樣才能盡早發(fā)現(xiàn)并解決問題,大大降低解決軟件缺陷的成本;
? 彈性:是指可以根據(jù)團隊內(nèi)開發(fā)資源與測試資源的配比與實際交付能力動態(tài)調(diào)整轉(zhuǎn)移的質(zhì)量保證工作量,使提測速率與測試速率保持動態(tài)平衡,實現(xiàn)需求交付速率的最大化。
3.2越早發(fā)現(xiàn)問題,解決問題的成本就越低
谷歌經(jīng)過多年實踐發(fā)現(xiàn),在開發(fā)工作流中,越早發(fā)現(xiàn)問題,解決問題的成本就越低。如下圖所示,通過測試者發(fā)現(xiàn)問題再由開發(fā)者在測試環(huán)境中檢查并解決問題就已經(jīng)導(dǎo)致解決問題的成本一定程度上的增加;一旦在軟件投產(chǎn)后遇到問題,解決問題的成本更是呈指數(shù)上升。
圖片
橫坐標(biāo)從左到右是開發(fā)工作流中從早到晚的各個階段,縱坐標(biāo)是解決軟件缺陷的成本
在人工測試階段發(fā)現(xiàn)并解決缺陷的工作流大致如下圖所示,很明顯整個流程相比開發(fā)者自測更為復(fù)雜且涉及測試者與開發(fā)者之間的溝通,這些都會帶來額外的成本。
圖16-測試開發(fā)交互流程
3.3右移動態(tài)化
右移動態(tài)化可一定程度上自適應(yīng)團隊內(nèi)開發(fā)資源與測試資源的配比與實際交付能力,使團隊的提測速率與測試速率保持動態(tài)平衡,最終實現(xiàn)需求交付速率的最大化。
3.4測試即代碼
彈性研發(fā)提倡測試即代碼,指通過代碼來表達測試,例如編寫單元測試,使測試行為形成代碼資產(chǎn)。傳統(tǒng)的開發(fā)者自測行為,例如在IDE中進行debug、在HTML頁面點擊按鈕調(diào)用接口,無法形成資產(chǎn)、無法復(fù)用、無法共享。
測試即代碼要求代碼是可測試的,代碼難以測試說明代碼職責(zé)太多、依賴關(guān)系混亂、低內(nèi)聚高耦合,因此測試即代碼能夠促使代碼模塊化、高內(nèi)聚低耦合、單一職責(zé)化。
使用代碼表達測試后,就可以反復(fù)運行這些測試代碼,每次更新代碼后都可以運行這些測試代碼,避免更新代碼引入bug,因此測試即代碼使開發(fā)者對更新代碼更有信心,更利于快速迭代軟件,以適應(yīng)不斷變化的市場環(huán)境與用戶需求。
在更新代碼后,一旦無法通過舊有的測試用例,就說明更新代碼改變了業(yè)務(wù)邏輯,
這也就發(fā)出了提醒,是否需要更新相關(guān)的文檔,促使文檔與代碼保持同步。
3.5測試自動化
在踐行測試即代碼后,就可以持續(xù)自動運行這些代碼化的測試用例,例如在把代碼合并至master分支時。自動化運行測試成本低、效率高,因此可頻繁運行,持續(xù)對系統(tǒng)進行測試。
3.6建立質(zhì)量文化
彈性研發(fā)提倡開發(fā)者更多地盡早地參與質(zhì)量保證,提升開發(fā)者的質(zhì)量意識,增強開發(fā)者的質(zhì)量保證參與感,強調(diào)代碼寫完并不意味著開發(fā)完成。
彈性研發(fā)促使開發(fā)者編寫模塊化的、可測試的代碼,可推動開發(fā)者使用更合理更科學(xué)的設(shè)計方案,對于建立團隊的質(zhì)量文化有重要意義。
3.7與測試左移的比較
傳統(tǒng)測試左移的工作流是,測試者創(chuàng)建一定數(shù)量的基礎(chǔ)測試用例,開發(fā)者開發(fā)完成后必須進行自測且通過這些測試用例之后方可提測,基本思想也是讓開發(fā)者盡早參與質(zhì)量保證,盡早發(fā)現(xiàn)并解決問題以降低解決問題的成本。
傳統(tǒng)測試左移的模式相對固定,通過測試用例來驅(qū)動開發(fā),有相對固定的流程,一定程度上限制了開發(fā)者的主觀能動性。而彈性研發(fā)提倡開發(fā)者通過實踐來發(fā)掘任何能保證研發(fā)質(zhì)量的手段,堅信成功的思想或手段是會傳播開的,并最終被廣泛接受。
4.如何彈性右移
4.1概述
彈性研發(fā)提倡開發(fā)者更多地盡早地參與質(zhì)量保證,降低測試復(fù)雜度,幫助建立質(zhì)量文化,因此彈性右移不拘泥于固定的形式,只要開發(fā)者在實踐彈性右移后有信心保證研發(fā)質(zhì)量即可。
為了避免“當(dāng)局者迷”與“思維定式”,建議由代碼作者以外的第三者參與進來保證質(zhì)量,機器或人工皆可,以下著重描述若干可供參考的實踐方向。
4.2靜態(tài)代碼掃描
靜態(tài)代碼掃描是指使用程序分析源代碼以發(fā)現(xiàn)潛在的問題,如bug、反模式及其他不需要運行代碼就能發(fā)現(xiàn)的問題,例如大家熟知的SonarQube以及IDEA插件Alibaba Java Coding Guidelines。在AI大行其道的當(dāng)下,也可考慮使用相關(guān)AI工具進行靜態(tài)代碼掃描。
4.3組內(nèi)代碼評審
組內(nèi)代碼評審是由與代碼作者同組的另一名成員負責(zé)代碼審核,同組成員意味著平時負責(zé)開發(fā)的業(yè)務(wù)與項目完全相同或相似,這有助于降低代碼評審的門檻,同時有助于提升巴士因子。
巴士因子是軟件開發(fā)中的一個術(shù)語,用于衡量軟件項目的相關(guān)知識在團隊成員中的共享程度。一個項目一旦有超過閾值的關(guān)鍵成員無法參與(例如被巴士撞傷了)就會導(dǎo)致項目無法推進甚至失敗,這個閾值就是巴士因子。巴士因子強調(diào)項目的有關(guān)知識不應(yīng)該僅被極少數(shù)人所掌握。組內(nèi)代碼評審促使同組至少有2名成員掌握所有業(yè)務(wù)需求與代碼。
程序員由于崗位的特殊性,要求其把主要時間和精力花在編寫好代碼上,而不是整天與人溝通,因此組內(nèi)代碼評審也是一種促進組內(nèi)同事學(xué)習(xí)和溝通的渠道。
目前我們團隊定期進行團隊范圍的代碼走查,這也是一種形式的代碼評審,有助于形成團隊的統(tǒng)一編碼規(guī)范與風(fēng)格。
4.4開發(fā)者驅(qū)動的自動化測試
?4.4.1 編寫測試代碼
彈性研發(fā)團隊提倡測試即代碼與測試自動化,因此如何編寫測試代碼非常關(guān)鍵。
好的測試代碼是不變的、健壯的、明確的:
? 不變的測試:除非被測代碼的需求改變了,否則測試代碼一旦編寫完成后就不再修改;
? 健壯的測試:在被測代碼所實現(xiàn)的需求沒變的情況下,測試代碼總是能夠運行通過;
? 清晰的測試:在測試代碼運行失敗的情況下,能夠明確知道是哪出問題了。
(1) 編寫不變且健壯的測試代碼
在實踐中,可以把開發(fā)者對代碼的修改分為四類:
第一類是對代碼進行重構(gòu):由于被測代碼的需求沒變,因此對代碼進行重構(gòu)不應(yīng)該使測試代碼運行失敗。若測試代碼運行失敗,則說明重構(gòu)影響了系統(tǒng)的行為,或測試代碼的被測代碼太過底層。
第二類是修復(fù)bug而修改代碼:bug的存在說明現(xiàn)有的測試代碼覆蓋的用例不夠全,在修復(fù)bug后,應(yīng)該新增測試用例對應(yīng)的測試代碼,但不需要修改已有測試代碼。
第三類是添加新需求而新增代碼:在編寫完新需求代碼后,應(yīng)該新增針對這部分代碼的測試代碼,但不需要修改已有測試代碼。
第四類是因需求改變而修改代碼:由于需求改變了,因此現(xiàn)有的測試代碼肯定會運行失敗,此時必須針對變更后的需求修改測試代碼。
綜上所述,理想情況下,只有在現(xiàn)有需求改變了,測試代碼才會運行失敗,才需要修改測試代碼,這樣一來維護測試代碼的成本就大大降低了。
編寫不變且健壯的測試代碼的目標(biāo)是:只要需求沒有變,測試代碼就始終能夠運行通過,這樣也就不需要修改測試代碼。達到這一目標(biāo)最簡單最直接的測試方法就是按照用戶調(diào)用系統(tǒng)的方式來測試系統(tǒng),也就是說,針對系統(tǒng)的公共API進行測試,而不是系統(tǒng)的實現(xiàn)細節(jié)。
那么哪些代碼才能稱得上公共API呢?
僅供一個類內(nèi)部使用的private輔助方法不是公共API,應(yīng)該通過測試那些使用該private輔助方法的代碼來測試它,這是因為private輔助方法過于底層,可能被修改的概率很大。
項目中的Service方法與通用工具類都可視為公共API,由于它對整個項目甚至所有其他項目公開,因此具備一定的抽象性與穩(wěn)定性,即使內(nèi)部的實現(xiàn)細節(jié)可能被修改,但對外呈現(xiàn)的行為應(yīng)該是相對穩(wěn)定的。
項目對外暴露的接口是公共API,由于它直接對外公開,因此具備很高的抽象性與穩(wěn)定性。
(2) 編寫清晰的測試代碼
清晰的測試是指測試代碼存在的目的以及運行失敗的原因都很明確,這樣在遇到測試代碼運行失敗時才能高效地進行修復(fù)。
下面以測試轉(zhuǎn)賬Service方法為例來描述何謂清晰的測試,轉(zhuǎn)賬Service方法有如下簽名:
// code=0表示轉(zhuǎn)賬成功,code!=0表示轉(zhuǎn)賬失敗及其原因
public R<?> transfer(Account from, Account to, double amount);
清晰的測試應(yīng)該是完整且簡潔的,完整是指測試代碼包含讀者理解測試的所有相關(guān)信息,簡潔是指除此之外測試代碼不包含其他信息,更不能包含任何邏輯,例如字符串拼接或流程控制等。
清晰的測試應(yīng)該測試方法的行為而不是方法本身,應(yīng)該為方法的每一種行為分別編寫測試方法,這樣測試方法就不會隨著被測方法行為的膨脹而膨脹,也不需要修改能夠運行通過的測試代碼。以轉(zhuǎn)賬方法為例,其行為包括轉(zhuǎn)賬成功、因出款戶余額不足導(dǎo)致的轉(zhuǎn)賬失敗、因出款戶不存在導(dǎo)致的轉(zhuǎn)賬失敗等,因此要分別為這些行為編寫測試方法。
面向行為的測試代碼由三部分組成,“given”定義預(yù)設(shè)的系統(tǒng)環(huán)境,“when”定義對系統(tǒng)執(zhí)行的操作,“then”驗證結(jié)果。測試方法也應(yīng)該由測試行為命名。
下面展示一個測試轉(zhuǎn)賬成功的測試方法:
@Test
public void testTransferSuccess() {
// given
Account from = new Account("account1", 50.00);
Account to = new Account("account2", 40.00);
// when
R<?> r = transfer(from, to, 20.00);
// then
Assert.assertEquals(0, r.getCode());
Assert.assertEquals(30, from.getBalance());
Assert.assertEquals(60, to.getBalance());
}
最后聲明一點,一旦你覺得需要額外編寫測試來驗證測試代碼,就表明測試遠遠不夠清晰。
?4.4.2 自動運行測試代碼
可在把代碼push到遠程倉庫時,在本地執(zhí)行maven的test命令即可自動運行測試代碼,也可在A-One編譯構(gòu)建項目時自動運行測試代碼。
4.5AI自動化測試
目前我們團隊有使用IDEA插件GitHub Copilot,它由OpenAI提供支持,基于數(shù)十億行開源代碼構(gòu)建的模型,可實現(xiàn)根據(jù)代碼注釋編寫代碼、創(chuàng)建單元測試與靜態(tài)代碼掃描。這里也推薦大家使用這款插件,利用AI進行自動化測試肯定是未來的趨勢,這里也提倡大家多多實踐多多分享。
作者簡介
張超
■服務(wù)端研發(fā)部-服務(wù)端買用技術(shù)團隊-交易平臺組
■ 2021年加入汽車之家,目前主要負責(zé)權(quán)益中臺的建設(shè)與后端開發(fā)工作,熱衷探索與分享。