本篇,讓我們嘗試用科學(xué)的方法進(jìn)行一些討論,為什么工作量往往難以估計(jì),為一個(gè)Delay的項(xiàng)目加人通常是下策,為什么重構(gòu)有用,以及為什么好的設(shè)計(jì)至關(guān)重要。
定性分析
研發(fā)效率 R 隨著代碼量 C 的增長(zhǎng)而遞減
-- John Adhoc
定量分析
研發(fā)效率本身是難以度量的,但為了討論起見(jiàn),我們不妨定義幾個(gè)變量,并試著加以討論。
-
C 當(dāng)前的代碼總量,業(yè)界通常以行數(shù)計(jì)。
注:此處不考慮不同編程語(yǔ)言的差異,假設(shè)有統(tǒng)一的語(yǔ)言(PHP除外) - Ce 當(dāng)前的有效代碼總量,指實(shí)際產(chǎn)生價(jià)值的有效代碼。
打個(gè)形象的比方,C 好比一袋薯片,包裝完好,膨脹十足。 Ce 好比打開(kāi)了包裝,瞬間泄氣,發(fā)現(xiàn)包裝里一半是空氣,其真正有效的薯片總量遠(yuǎn)遠(yuǎn)少于包裝給你的預(yù)期。之所以嚴(yán)格區(qū)分, C 和 Ce,本質(zhì)就在于通常人們直接見(jiàn)到的工作量由C 決定,那就像人月神話一文中提及的,碼農(nóng)的工作產(chǎn)出其實(shí)是非線性的,即真實(shí)的勞動(dòng)價(jià)值無(wú)法用直接觀測(cè)可得的C決定。
由此,我們可以定義研發(fā)效率函數(shù) R = d(Ce) / d(C) ,即假設(shè) Ce 是相對(duì)于 C 的函數(shù),則 R 對(duì)應(yīng)其導(dǎo)函數(shù),基于我們的定性假設(shè),R 是一個(gè) 減函數(shù),即隨著C的增加,研發(fā)效率是遞減的。
- Rc 表示當(dāng)前代碼規(guī)模 C 下的生產(chǎn)效率,即在當(dāng)前規(guī)模下,每增長(zhǎng)單位代碼行數(shù),所能實(shí)際產(chǎn)生的有效代碼行數(shù)。
舉例來(lái)說(shuō),假設(shè)以10000行代碼為基本生產(chǎn)單位。下表是一個(gè)具體的例子(注意到研發(fā)效率隨代碼規(guī)模增加而遞減),假設(shè) R = 10000 / (10000 + C) 的情況(這是一個(gè)假設(shè),實(shí)際中,也較難衡量R函數(shù)的實(shí)際曲線,在此我們使用一個(gè)雙曲線函數(shù)來(lái)估計(jì),從筆者的經(jīng)驗(yàn)來(lái)看,這不妨是一種可能合理的近似):
C | Ce | 當(dāng)前研發(fā)效率:Rc | 每再增加10000行代碼能獲得的有效代碼 |
---|---|---|---|
0 | 0 | 10000 / (10000 + 0) = 1.0 | 10000 * 1.0 = 10000 |
10000 | 10000 | 10000 / (10000 + 10000) = 0.5 | 10000 * 0.5 = 5000 |
20000 | 15000 | 10000 / (10000 + 20000) = 0.33 | 10000 * 0.33 = 3300 |
30000 | 18300 | 10000 / (10000 + 30000) = 0.25 | 10000 * 0.25 = 2500 |
40000 | 20800 | 10000 / (10000 + 40000) = 0.2 | 10000 * 0.2 = 2000 |
提升研發(fā)效率的方法
我們換個(gè)思路,將上表的最后一列置換成:增加10000行 有效代碼 所需要的代碼量:
C | Ce | Rc | 在當(dāng)前規(guī)模下,增加10000行 有效代碼 所需要新增的代碼量 |
---|---|---|---|
0 | 0 | 1.0 | 10000 |
10000 | 10000 | 0.5 | ~30000 |
40000 | 20800 | 0.25 | ~70000 |
我們注意到一個(gè)算不上驚人的事實(shí),假設(shè)一個(gè)碼農(nóng)的生產(chǎn)力為 10000行代碼 / 月,注意到在項(xiàng)目初期,代碼規(guī)模較小的情況下,完成10000行有效代碼的工作時(shí)間為 10000 / 10000 = 1 人月。 之后再增加10000行有效代碼,工作時(shí)間為 30000 / 10000 = 3 人月。 突然暴增3倍。 進(jìn)一步,如果在此基礎(chǔ)上,再增加10000行有效代碼, 則需要工作時(shí)間為7人月,幾乎一定會(huì)導(dǎo)致項(xiàng)目Delay。
在現(xiàn)實(shí)生活中,我們把每10000行代碼視作產(chǎn)品的一個(gè)新增功能。 則對(duì)應(yīng)每增加一個(gè)新增功能,對(duì)應(yīng)的工作量遠(yuǎn)遠(yuǎn)超出直覺(jué)(項(xiàng)目經(jīng)理/項(xiàng)目經(jīng)理的預(yù)期)。 造成這一困擾的主要原因只有一個(gè),即 R 隨著 C 增長(zhǎng)而下降。
避免這一困境(項(xiàng)目要延期)的方案:
- 在 Ce 不變的情況下,減少當(dāng)前的 C ,這相當(dāng)于間接提升 R
- 提升 R
- 提升 C 的增加速度
對(duì)應(yīng)現(xiàn)實(shí),方案1通常意味著功能回歸情況下,代碼重構(gòu),清除冗余。 方案2通常意味著架構(gòu)變更,模塊解耦,使得開(kāi)發(fā)能夠并行。方案3,意味著加人,但相對(duì)而言是性價(jià)比較低的策略。顯然,1,2是上策。 3是下策。如果非要選擇3,建議和1,2中的某一個(gè)選擇并行。
為什么重構(gòu)是必要的
勞動(dòng)人民在田間勞作,每年收獲農(nóng)作物。土地也需要恢復(fù),肥力會(huì)下降。所以有經(jīng)驗(yàn)的農(nóng)民往往會(huì)交叉種植不同的農(nóng)作物,并適當(dāng)修養(yǎng)土地,才能保證來(lái)年更好的收成。在業(yè)界,這一現(xiàn)象,叫做重構(gòu)。
C | Ce | 當(dāng)前研發(fā)效率:Rc | 每再增加10000行代碼能獲得的有效代碼 |
---|---|---|---|
0 | 0 | 10000 / (10000 + 0) = 1.0 | 10000 * 1.0 = 10000 |
10000 | 10000 | 10000 / (10000 + 10000) = 0.5 | 10000 * 0.5 = 5000 |
20000 | 15000 | 10000 / (10000 + 20000) = 0.33 | 10000 * 0.33 = 3300 |
30000 | 18300 | 10000 / (10000 + 30000) = 0.25 | 10000 * 0.25 = 2500 |
40000 | 20800 | 10000 / (10000 + 40000) = 0.2 | 10000 * 0.2 = 2000 |
40000 | 22800 | 0.20 | 此時(shí)選擇重構(gòu),縮小代碼規(guī)模至 23000,生產(chǎn)效率恢復(fù)到0.30 |
40000 | 22800 | 0.30 | 3000 (如果不重構(gòu),則為2000, 提升50%效率) |
上表展示了重構(gòu)造成的差異,本質(zhì)上是通過(guò)減少代碼規(guī)模,使得研發(fā)效率函數(shù)右移(對(duì)應(yīng)的是效率回到了之前小規(guī)模代碼的程度)。 定量的計(jì)算,使得重構(gòu)的價(jià)值可以被衡量,注意,在日常工作中,經(jīng)理們往往不容易被碼農(nóng)的經(jīng)驗(yàn)直接說(shuō)服,通過(guò)一些看似“科學(xué)”的計(jì)算方式,可能更容易爭(zhēng)取到空間,這背后的動(dòng)機(jī)我們放到之后的章節(jié)中再討論。
由此表也可以看出,重構(gòu)的時(shí)間是非常關(guān)鍵的,在不同的 C 規(guī)模下,選擇重構(gòu),對(duì)應(yīng)的生產(chǎn)效率的提升百分比差異是相當(dāng)大的(有興趣的讀者可以自己計(jì)算,在表格不同行選擇重構(gòu)意味著什么)。
最后,請(qǐng)注意這里討論的假設(shè):
- 重構(gòu)總是順利的(事實(shí)上總不是),將代碼規(guī)模下降其實(shí)是一件遠(yuǎn)比增加代碼困難得多的事情。
- 重構(gòu)其實(shí)是需要代價(jià)的,這就是研發(fā)需要平衡的,即重構(gòu)需要的工作量和帶來(lái)的生產(chǎn)效率的比較,上表中,50%的效率提升,如果對(duì)應(yīng)重構(gòu)需要的工作量?jī)H為3人天,則顯然是劃算的,反之,如果對(duì)應(yīng)重構(gòu)的工作量就需要1人月,則不如將這1人月繼續(xù)投入到水深火熱的勞作中去,帶來(lái)的收益更大(即重構(gòu)可以推遲)
為什么設(shè)計(jì)如此重要
最后強(qiáng)調(diào)一下設(shè)計(jì)的重要性。
設(shè)計(jì)的本質(zhì),就是降低系統(tǒng)協(xié)作的復(fù)雜性。
好的設(shè)計(jì),可以使得 研發(fā)效率曲線 R 下降的更慢。這是設(shè)計(jì)好壞的有且唯一的評(píng)價(jià)指標(biāo)。
-- John Adhoc
再舉例來(lái)說(shuō),假設(shè)以10000行代碼為基本生產(chǎn)單位。下表是一個(gè)具體的例子(注意到研發(fā)效率隨代碼規(guī)模增加而遞減),假設(shè) R = 10000 / (10000 + C / 2) 。(這次我們聘請(qǐng)了經(jīng)驗(yàn)豐富的設(shè)計(jì)師,做了充分且有效的架構(gòu)設(shè)計(jì),使得研發(fā)效率下降變慢)
C | Ce | 當(dāng)前研發(fā)效率:Rc | 每再增加10000行代碼能獲得的有效代碼 |
---|---|---|---|
0 | 0 | 10000 / (10000 + 0) = 1.0 | 10000 * 1.0 = 10000 |
10000 | 10000 | 10000 / (10000 + 10000 / 2) = 0.67 | 10000 * 0.67 = 6700 |
20000 | 16700 | 10000 / (10000 + 20000 / 2) = 0.5 | 10000 * 0.5 = 5000 |
30000 | 21700 | 10000 / (10000 + 30000 / 2) = 0.4 | 10000 * 0.4 = 4000 |
40000 | 25700 | 10000 / (10000 + 40000 / 2) = 0.33 | 10000 * 0.33 = 3300 |
50000 | 29000 | 10000 / ( 10000 + 50000 / 2) = 0.28 | 10000 * 0.28 = 2800 |
60000 | 31800 | 注意到,之前完成30000行有效代碼,需要花費(fèi)110000行代碼,現(xiàn)在僅需要 60000行,效率提升接近1倍 |
從上表可以看到,僅僅是修正研發(fā)效率曲線 R , 從 R = 10000 / (10000 + C) => R = 10000 / (10000 + C / 2),即使得研發(fā)效率在生產(chǎn)30000行有效代碼時(shí),提升接近100%(有興趣的讀者可以繼續(xù)計(jì)算,越往后差異越大)。 這就是 設(shè)計(jì)的力量。