程式碼審查和不良程式設計習慣

vaikan發表於2014-03-06

有時候,做為一個程式設計師,我覺得我的職業生涯會被我開發軟體使用的開發工具和技術架構明顯的分割成幾個階段。一部分是因為使用的程式語言——在大學時是Smalltalk,在Gog Creek公司是C#和Python,而另一方面是開發工具。我在Fog Creek公司裡工作了8年,在那裡,我們有一個非常固定的技術架構:bug管理、客戶支援和文件管理用FogBugz;開發管理用Trello;程式碼審查用Kiln;版本控制用Mercurial;編碼用Vim和 Visual Studio ;持續整合用我們的內部工具Mortar;隨著時間的流逝,這些工具在慢慢的變化,但變化從來都是緩慢逐步的,一個元件一個元件的。所以,我的工作流程和工作效率一直沒有巨大的變化。

大概一個月前,我加入了Knewton公司,整個技術架構一下子完全變了。Visual Studio換成了IntelliJ;Mortar換成了Jenkins;Mercurial換成了Git;FogBugz換成了JIRA

也 許你會覺得這會讓我頭大,還會有些不知所措,但事實上並不是這樣,這些工具的改變並沒有對我的工作流程產生多大的影響。我發現Git和Mercurial 驚人的相似,JIRA基本上是一個半成品的FogBugz,而IntelliJ算是和Visual Studio差不多吧。也許我需要從新學習一些快捷鍵和了解按鈕的位置,但事實上我的開發模式沒有實質的變化。

但有一個例外:我不喜歡使用Gerrit做程式碼審查。不喜歡它的原因並不是它的程式寫的很爛;不喜歡的原因是它的流程會鼓勵一種不良程式設計習慣。

Knewton公司對程式碼審查非常、非常的看重。這非常好,因為我也是這樣,而且我開發過整套關於程式碼審查的工具。所以,我的意思絕對不是反對程式碼審查。

而且,Gerrit的設計跟最初的 Kiln 原型的設計幾乎完全一致。程式碼審查的實施有兩種基本的方式:pre-merge,是指在程式碼進入主程式碼庫之前進行程式碼審查。和post-commit, 是指之後審查。新版本的Kiln對兩種方式都支援,但在2008年,當Tyler和我通過一個專案——也就是Kiln的前身——在Django Dash中取勝時,我們倆都認同pre-merge的工作流程。直接提交到主程式碼庫是不允許的;你需要先建立一個審查區,把修改的程式碼放進去,討論,然 後,等待批准,系統會自動合併這些程式碼。這一種是我最欣賞的工作流程,所以Kiln一直支援這種方式(通過“Read and Branch”許可權),而巧的事,這也是Gerrit唯一支援的方式,按理說我應該喜歡它才是。

cowbell-thumb

我差一點就喜歡它了,但問題出在一個致命問題上:程式碼審查的粒度。在Kiln中,審查是基於被修改的相關程式碼。而在Gerrit裡,審查是基於單次程式碼修改提交。在Kiln中,一個單一審查會涉及很多次程式碼提交,審查的批准和拒絕是整體的,而Gerrit裡審查的是一次孤立的提交。

這 兩種方法模式在各自的陣營裡都有大量的受歡迎的系統實現。GitHub和Bitbucket都和Kiln一樣都屬於“批量提交”審查陣營,而Review Board, Barkeep, 和 Phabricator 都加入了“單一提交”審查陣營。所以,情況並不是我所說的某一種方式、某一款軟體是對的,而其它都是錯的。但我還是要堅持說,批量審查的方式是對的,而其 餘的都是錯的,因為單一提交審查系統在鼓勵一種不良程式設計習慣。

單一提交審查系統有兩個最根本的問題:

  1. 它在向你暗示各個修改提交之間沒有關聯。經 常的,每當我實現一個新功能時,我都會有三個步驟:首先,重構現有的程式碼,讓程式碼整潔,方便新增新功能;接下來,加入新功能;最後,寫單元測試程式碼。功能 越複雜,各個步驟裡越有可能各自包含多個邏輯步驟。如果能將多個不同的提交放的一個程式碼審查中進行,那你就可以簡單的將這些修改分組提交。但如果使用的是 單一提交審查系統,那就是在迫使我將所有修改全部完成後進行一次全量提交。這樣一來,重構的程式碼,新新增的程式碼,都混在一起,讓人非常不爽,而且在審查過 程中需要我付出大量額外的精力來指出各部分程式碼都是幹嘛的。你也許會爭辯,說你可以把修改的程式碼拆分提交,每一個提交對應一次審查。但事實上這樣做會更 糟。最好的情況下,你可以把測試程式和新功能程式碼分開提交,可以把重構程式碼和後加程式碼分開提交,但真正的問題是,眾多的單一修改審查系統都慫恿對某個提交 在孤立的狀態下進行批准,這完全會和你的願望相反。於是,“一次提交一次審查”的折中就從“麻煩”變成了“危險”。的確不是一種改進。
  2. 它在慫恿你隱藏曆史記錄。 版本控制系統的最重要的功能就是告訴你程式碼演變的歷史、是如何變成今天這個樣子的。我經常會檢視昨天程式碼是什麼樣的,上週二下午2點程式碼是什麼樣的,期間 發生了什麼變化。很多時候是因為我發現程式碼以前好用而現在不行,我想知道為什麼。而更多時候,我是想知道為什麼會對程式碼做這樣的修改。關聯的上下文是什 麼?動機是什麼?如果你總是保持所有程式碼一次提交——為了審查,那我就喪失了很多歷史資訊:所有我能找到的就是一次完整軟體的一次提交,完全沒有開發過程 中的過程資訊。

這就是我為什麼對Gerrit極度失望的原因。並不是Gerrit是一個糟糕的軟體,而是他在鼓勵一種在使用版本控制時不良的開發習慣。這就是為什麼所有工具中唯獨不喜歡它的原因,是唯一讓我對放棄Kiln感到失望的系統。

相關文章