Code Review 程序員的寄望與哀傷
2017-01-20 07:25
一個(gè)程序員妙啃,他寫(xiě)完了代碼肢藐,在測(cè)試環(huán)境通過(guò)了測(cè)試恕齐,然后他把它發(fā)布到了線上生產(chǎn)環(huán)境们衙,但很快就發(fā)現(xiàn)在生產(chǎn)環(huán)境上出了問(wèn)題踩官,有潛在的 bug却桶。
事后分析,是生產(chǎn)環(huán)境的一些微妙差異蔗牡,使得這種 bug 場(chǎng)景在線下測(cè)試中很難被發(fā)現(xiàn)肾扰。畢竟想要在測(cè)試環(huán)境完美的復(fù)制生產(chǎn)環(huán)境的所有情況也是不太可能的畴嘶,導(dǎo)致出現(xiàn)了疏漏。對(duì)于這類(lèi)情況集晚,我們?cè)谙胧欠窨梢酝ㄟ^(guò)在線下做一些 Code Review(代碼審查)假想線上的環(huán)境差異窗悯,通過(guò)在頭腦中的假想上線運(yùn)行來(lái)獲得一些概念驗(yàn)證,這樣是否能夠減少上線后出現(xiàn) bug 的概率呢偷拔?
感性
Code Review 是很多軟件工程理論和方法學(xué)中的重要一環(huán)蒋院,而且程序員們大多都感性的認(rèn)識(shí)到 Code Review 對(duì)于提升代碼質(zhì)量和減少 bug 有幫助,但在我過(guò)去工作的這些年里莲绰,經(jīng)歷了幾家公司欺旧,數(shù)個(gè)不同的團(tuán)隊(duì),卻幾乎沒(méi)有把 Code Review 作為必要的一環(huán)去執(zhí)行的團(tuán)隊(duì)蛤签。
過(guò)去辞友,總是在線上出現(xiàn)一些奇怪的疑難問(wèn)題后,我們一群程序員才圍坐一堆震肮,打開(kāi)相關(guān)代碼來(lái)逐行分析称龙,根據(jù)線上現(xiàn)場(chǎng)的尸檢來(lái)做事后分析和推導(dǎo),這樣的代碼審查和分析實(shí)際上根本不是 Code Review戳晌,也完全違背了 Code Review 的初衷鲫尊。Code Review 的初衷是在代碼進(jìn)入生產(chǎn)環(huán)境前經(jīng)過(guò)同行評(píng)審來(lái)減少代碼出現(xiàn) bug 的概率。這一點(diǎn)程序員都好理解沦偎,提前的 Code Review 就像雷達(dá)掃描我們重點(diǎn)關(guān)注的代碼領(lǐng)地疫向,以期發(fā)現(xiàn)或明顯或隱藏的威脅因素。
想必很多人都看過(guò)一部叫《火影忍者》的漫畫(huà)豪嚎,里面有一種忍術(shù)技能——白眼搔驼。根據(jù)忍者能力強(qiáng)弱白眼能觀察的距離不同,雖然白眼有近 360° 的觀察范圍侈询,依然存在觀察死角舌涨。不是所有的程序員都擁有類(lèi)似「白眼」的技能,我們?cè)趯?xiě)程序時(shí)力求思考的全面妄荔,不留死角或盲點(diǎn)泼菌,但實(shí)際死角或盲點(diǎn)總是存在谍肤,隨著程序員經(jīng)驗(yàn)或經(jīng)歷的成長(zhǎng)啦租,思考和認(rèn)識(shí)的越發(fā)全面(越發(fā)接近 360°),擁有了近乎「白眼」的能力荒揣,但像白眼一樣依然存在盲點(diǎn)篷角。
我們看不到自己的后腦勺,所以假如在我們的后腦勺放上一個(gè)伙伴的眼睛系任,他的視角就彌補(bǔ)了我們自己的盲點(diǎn)恳蹲。世上沒(méi)有兩片完全一樣的樹(shù)葉虐块,也許也不會(huì)有兩個(gè)認(rèn)知視角完全重疊的人。
像 Code Review 或結(jié)對(duì)編程這樣的實(shí)踐正是基于這樣的感性認(rèn)知嘉蕾,試圖找出盲點(diǎn)區(qū)域的 bug贺奠,但到底這樣的做法能降低多少出現(xiàn) bug 的概率呢?
理性
有人對(duì) Code Review 的作用進(jìn)行了更理性且量化的分析错忱,來(lái)自 Wikipedia(維基百科)儡率。
卡珀斯·瓊斯(Capers Jones)分析了超過(guò) 12,000 個(gè)軟件開(kāi)發(fā)項(xiàng)目,其中使用正式代碼審查的項(xiàng)目以清,發(fā)現(xiàn)潛在缺陷率約在 60-65% 之間儿普,若是非正式的代碼審查,發(fā)現(xiàn)潛在缺陷率不到 50%掷倔。大部份的測(cè)試眉孩,發(fā)現(xiàn)的潛在缺陷率會(huì)在 30% 左右。
一般的代碼審查速度約是一小時(shí) 150 行程式碼勒葱,對(duì)于一些關(guān)鍵的軟體(例如安全關(guān)鍵系統(tǒng)的嵌入式軟體)浪汪,一小時(shí)審查數(shù)百行程式碼的審查速度太快,可能無(wú)法找到程式中的問(wèn)題错森。代碼審查一般可以找到及移除約 65% 的錯(cuò)誤吟宦,最高可以到 85%。
也有研究針對(duì)代碼審查找到的缺陷類(lèi)型進(jìn)行分析涩维。代碼審查找到的缺陷中殃姓,有 75% 是和計(jì)算機(jī)安全隱患有關(guān)。對(duì)于產(chǎn)品生命周期很長(zhǎng)的軟件公司而言瓦阐,代碼審查是很有效的工具蜗侈。
從上面的實(shí)驗(yàn)分析結(jié)果看,Code Review 對(duì)于發(fā)現(xiàn)潛在缺陷很有用(相比測(cè)試能發(fā)現(xiàn)的缺陷率高一倍)睡蟋,但也需要投入巨大的時(shí)間成本(一小時(shí)審查 150 行代碼踏幻,再快就不利于發(fā)現(xiàn)潛在缺陷了),而且更適用于長(zhǎng)生命周期的產(chǎn)品戳杀。
所以该面,有個(gè)現(xiàn)象就容易理解了。我發(fā)現(xiàn)在同一家公司做 Code Review 較多的都是研發(fā)通用底層技術(shù)產(chǎn)品或中間件的團(tuán)隊(duì)信卡,而做業(yè)務(wù)開(kāi)發(fā)的團(tuán)隊(duì)則較少做 Code Review隔缀。一方面是底層技術(shù)產(chǎn)品或中間件的需求較穩(wěn)定,且生命周期長(zhǎng)傍菇,而業(yè)務(wù)項(xiàng)目(特別是嘗試性的新業(yè)務(wù))需求不穩(wěn)定猾瘸,時(shí)間要求緊迫,而生命周期很多都偏短。
困難
通過(guò)了理性的分析我們可以看出 Code Review 是有很大好處的牵触,但適用的場(chǎng)景和花費(fèi)的成本也需要去平衡淮悼。除了這點(diǎn),也許還有一些關(guān)于如何實(shí)施 Code Review 的困難揽思。
如果把 Code Review 作為一個(gè)必要環(huán)節(jié)引入到研發(fā)流程中袜腥,也許會(huì)引發(fā)下面一些問(wèn)題:項(xiàng)目 deadline 已定,時(shí)間緊迫钉汗,天天加班忙成狗了瞧挤,誰(shuí)還愿意搞Code Review?這是一個(gè)最常見(jiàn)的客觀阻礙因素儡湾,因?yàn)?deadline 很多時(shí)候都不是我們自己確定的特恬。
即使強(qiáng)推下去,團(tuán)隊(duì)認(rèn)識(shí)不到其好處徐钠,也不夠重視癌刽,每次走個(gè)過(guò)場(chǎng),Code Review 的效果如何能保障尝丐?如果你是一個(gè)父親显拜,為自己的孩子制定一個(gè)最簡(jiǎn)單的規(guī)則,比如說(shuō):飯前洗手爹袁。你如何保障這個(gè)規(guī)則的實(shí)施效果远荠,你當(dāng)然可以每次吃飯前檢查下孩子的手是否干凈,但你也很難每頓飯都和孩子一塊兒吃失息,所以你就檢查不到了譬淳。要是孩子有個(gè)智能的碗,當(dāng)孩子吃飯時(shí)手一接觸到碗就能檢測(cè)細(xì)菌是否超標(biāo)盹兢,然后發(fā)出提醒和拒絕措施邻梆,這樣是否就保障了這個(gè)規(guī)則與你在或不在的實(shí)施有效性。而 Code Review 顯然是個(gè)更復(fù)雜的規(guī)則绎秒,需要的智能工具支持也更復(fù)雜浦妄。
團(tuán)隊(duì)人員結(jié)構(gòu)搭配不合理,新人沒(méi)經(jīng)驗(yàn)的多见芹,有經(jīng)驗(yàn)的少剂娄。天天安排經(jīng)驗(yàn)多的少數(shù)人幫助 review 多數(shù)新人的代碼,新人或有收獲玄呛,但對(duì)高級(jí)或資深程序員又有多大裨益阅懦?一個(gè)好的規(guī)則或制度總是需要既符合多方參與者的個(gè)體利益又能滿(mǎn)足組織或團(tuán)隊(duì)的共同利益,這樣的規(guī)則或制度才能順暢的實(shí)施和運(yùn)轉(zhuǎn)把鉴。
若你的團(tuán)隊(duì)中存在一些自信超強(qiáng)大的程序員故黑,覺(jué)得自己的寫(xiě)的代碼絕對(duì)沒(méi) bug,不需要?jiǎng)e人來(lái)給我 review庭砍。這樣的人未必就很差场晶,他寫(xiě)的代碼甚至確實(shí)出 bug 的概率比普通人更低,但肯定依然存在潛在 bug怠缸。這樣團(tuán)隊(duì)成員的存在诗轻,也會(huì)成為 Code Review 的一個(gè)障礙。
路徑
Code Review 確實(shí)存在很多各種各樣的困難揭北,導(dǎo)致很多團(tuán)隊(duì)都能認(rèn)識(shí)到它的好處卻實(shí)施不下去扳炬。尤其在國(guó)內(nèi),我?guī)缀鯖](méi)聽(tīng)說(shuō)過(guò)嚴(yán)格把 Code Review 作為一項(xiàng)研發(fā)制度或規(guī)則要求的公司搔体。
但在大洋的另一端恨樟,無(wú)論是老牌大公司如 Google 或是新近崛起的創(chuàng)業(yè)公司如 Airbnb 都把 Code Review 作為上線進(jìn)入生產(chǎn)環(huán)境前強(qiáng)制且必須的一環(huán)。在一篇介紹 Google Code Review 的實(shí)踐文章中說(shuō)道疚俱,在 Google 任何產(chǎn)品劝术,任何工程的代碼,在被進(jìn)行嚴(yán)格或者明確的審查(Code Review)之前呆奕,是不允許提交的养晋。你看,Google 通過(guò)工具控制在進(jìn)行 Code Review 前甚至是無(wú)法提交代碼的梁钾。
Google 以一種強(qiáng)硬的姿態(tài)來(lái)制定關(guān)于 Code Review 的且應(yīng)用于全公司范圍內(nèi)的規(guī)則绳泉,對(duì)任何人都不例外。即使面對(duì)團(tuán)隊(duì)中超自信且強(qiáng)大的程序員也無(wú)例外姆泻,要么遵守規(guī)則零酪,要么離開(kāi)組織。這一點(diǎn)從 C 語(yǔ)言和 Unix 的發(fā)明者拇勃、圖靈獎(jiǎng)得主蛾娶、最具傳奇性的程序員 Ken Thompson 在 Google 的趣事——作為 C 語(yǔ)言發(fā)明者之一因?yàn)闆](méi)有參加 Google 的編程語(yǔ)言能力測(cè)試所以無(wú)法在 Google 提交 C 代碼——從中可以一窺 Google 規(guī)則的強(qiáng)硬性。
所以像 Google 這樣的公司對(duì)于 Code Review 屬于高度認(rèn)可且有公司制度和規(guī)則的強(qiáng)硬支持潜秋,再輔助自動(dòng)檢測(cè)和控制工具的嚴(yán)格執(zhí)行蛔琅,方能如此。這也屬于一種嚴(yán)格的同步 Code Review峻呛,所謂同步就是必須要等待 Code Review 有了結(jié)果并無(wú)異議后方能提交或上線代碼罗售。
但要實(shí)施如此嚴(yán)格的同步 Code Review 似乎對(duì)大部分國(guó)內(nèi)公司又感覺(jué)過(guò)于無(wú)奈,這需要公司制度钩述、團(tuán)隊(duì)文化和技術(shù)工具三方面的支持寨躁。而在大部分以業(yè)務(wù)目標(biāo)、KPI和績(jī)效導(dǎo)向的公司牙勘,不說(shuō)在制度和文化方面得到支持职恳,能不被反對(duì)就不錯(cuò)了所禀。關(guān)于這一點(diǎn)陳皓(@左耳朵耗子)寫(xiě)過(guò)一篇文章《從 Code Review 談如何做技術(shù)》其中寫(xiě)到了在阿里實(shí)施 Code Review 遇到的各種文化上的阻礙和反對(duì)。而阿里已是國(guó)內(nèi)頂級(jí)互聯(lián)網(wǎng)公司放钦,可見(jiàn)實(shí)施同步 Code Review 的路徑并不簡(jiǎn)單色徘。
如果實(shí)施同步 Code Review 如此困難,那么是否可以退而求其次操禀,設(shè)計(jì)一種異步的 Code Review 方式呢褂策?就像我們提升系統(tǒng)性能一樣,把一些同步的串行調(diào)用變成異步的并行調(diào)用颓屑,按這個(gè)思路 YY 了以下場(chǎng)景斤寂。
程序員完成編碼,提交測(cè)試揪惦,測(cè)試通過(guò)后就去上線發(fā)布遍搞,另一方面也組織并行的 Code Review。畢竟測(cè)試通過(guò)只能證明測(cè)試覆蓋的場(chǎng)景無(wú) bug器腋,但可能代碼實(shí)現(xiàn)并不優(yōu)化和合理尾抑,而并行的 Code Review 即使發(fā)現(xiàn)了潛在問(wèn)題依然來(lái)不及阻止本次上線,但可以為下次上線提供優(yōu)化點(diǎn)或方向蒂培。另外在制度上把 Code Review 作為工程師的日常 KPI再愈,比如:要求每周對(duì)其他同事的代碼變動(dòng)做 1~2 次 Review。
而 Review 的方式除了閱讀代碼护戳,也可以為 Review 的代碼提供 Unit Test 間接達(dá)到了結(jié)對(duì)編程的目的翎冲。為了確保代碼確實(shí)被 Review 過(guò)需要工具支持,對(duì) Review 過(guò)的代碼進(jìn)行簽名媳荒,對(duì)不同的 Review 形式(簽名表示讀過(guò)抗悍,Unit Test 表示不僅讀過(guò)還白盒測(cè)試過(guò))進(jìn)行分類(lèi)統(tǒng)計(jì),發(fā)布 Code Review 統(tǒng)計(jì)排行榜和覆蓋分析钳枕。
以類(lèi)似這樣的方式缴渊,逐步培養(yǎng)起交叉 Code Review 的文化和氛圍,同時(shí)也顯性將 Code Review 納入了程序員的工作業(yè)績(jī)之中鱼炒。一開(kāi)始不必像同步 Code Review 一樣對(duì)所有上線進(jìn)行了阻塞衔沼,帶來(lái)巨大的陣痛。當(dāng)然這也依然是一個(gè)理想化的 YY 場(chǎng)景昔瞧,實(shí)施起來(lái)依然需要克服不少困難和準(zhǔn)備前提工具指蚁。
在另外在一篇叫《谷歌是如何做代碼審查的》文章中,一位 Google 的工程師對(duì) Code Review 的認(rèn)識(shí)是:
代碼審查的最大的功用是純社會(huì)性的自晰。
還有一個(gè)非常重要的好處凝化,代碼審查能傳播知識(shí)。
而防止 bug 混入酬荞,這反倒是它最不重要的一點(diǎn)搓劫。
第一點(diǎn)是這么理解的瞧哟,如果你在編程,而且知道一定將會(huì)有同事檢查你的代碼枪向,那么你編程的姿勢(shì)和態(tài)度都會(huì)完全不同勤揩。之間的微妙差異可類(lèi)比于你是在為公司的內(nèi)部系統(tǒng)編程,還是在給開(kāi)源軟件貢獻(xiàn)代碼遣疯。這是一個(gè)很有趣的視角,它其實(shí)反應(yīng)了公司的制度和文化凿傅。
現(xiàn)實(shí)
以前嘗試過(guò)要在團(tuán)隊(duì)內(nèi)部做 Code Review缠犀,聽(tīng)說(shuō)兄弟團(tuán)隊(duì)搞得不錯(cuò),然后就一起交流經(jīng)驗(yàn)聪舒,最后交流的重心就落在了應(yīng)該選個(gè)什么好用的 Code Review 工具來(lái)做辨液,如今想來(lái)這完全是舍本逐末了。
這就像以為有了好的編輯器(或 IDE)就能寫(xiě)出好的代碼一樣箱残,事實(shí)就是有很多好用的 Code Review 工具我們依然做不好 Code Review滔迈。古龍小說(shuō)《陸小鳳》中有一段描述,記憶尤深:
西門(mén)吹雪:此劍乃天下利器被辑,劍鋒三尺七寸燎悍,凈重七斤十三兩。
葉孤城:好劍盼理。
西門(mén)吹雪:的確是好劍谈山。
葉孤城:此劍乃海外寒劍精英,吹毛斷發(fā)宏怔,劍鋒三尺三奏路,凈重六斤四兩。
西門(mén)吹雪:好劍臊诊。
葉孤城:本就是好劍鸽粉。
劍是好劍,但最好先成為像西門(mén)吹雪或葉孤城這樣的好劍客抓艳,再來(lái)居高俯視触机、吹毛斷發(fā)的談劍是否是好劍。
即使在最差的環(huán)境下玷或,完全沒(méi)有人關(guān)心 Code Review 這件事威兜。一個(gè)有追求的程序員依然可以做到一件事,自己給自己 review庐椒。就像寫(xiě)文章椒舵,我寫(xiě)完一篇文章從來(lái)不會(huì)立刻發(fā)布,而是從頭腦中放下(unload)约谈,過(guò)上一段時(shí)間(也許是幾小時(shí)笔宿、也許是幾天)再自己重新細(xì)讀一遍犁钟,改掉其中必然會(huì)出現(xiàn)的錯(cuò)別字或文句不通暢之處,甚或論據(jù)不充分或不準(zhǔn)確的地方泼橘,因?yàn)槲抑啦还芪覍?xiě)了多少文字涝动,總還會(huì)有這些 bug,這就是給自己的 review炬灭。
即使如此醋粟,有時(shí)發(fā)出去的文章還是依然存在 bug,但總會(huì)比不 review 少了些重归。程序員在估算開(kāi)發(fā)任務(wù)時(shí)也最好加上自己給自己 review 的時(shí)間米愿,給自己 review 是一種自省,自我的成長(zhǎng)總是從自省開(kāi)始鼻吮。
...
我提交了一段代碼育苟,卻沒(méi)人給我 review,稍后我自己給自己 review 了椎木,得到了一段更好的代碼和一個(gè)更好的自己违柏。
寫(xiě)點(diǎn)程序世間的文字,畫(huà)點(diǎn)生活瞬間的畫(huà)兒香椎。