代碼複審

一、代碼複審的目的

  1、找出代碼的錯誤。如:
        a. 編碼錯誤,比如一些能碰巧騙過編譯器的錯誤。
        b. 不符合項目組的代碼規範的地方。
  2、發現邏輯錯誤,程序可以編譯通過,但是代碼的邏輯是錯的。
  3、發現算法錯誤,比如使用的算法不夠優化。
  4、發現潛在的錯誤和迴歸性錯誤——當前的修改導致以前修復的缺陷又重新出現。
  5、發現可能改進的地方。
  6、教育(互相教育)開發人員,傳授經驗,讓更多的成員熟悉項目各部分的代碼,同時熟悉和應用領域相關的實際知識。

二、代碼複審的步驟

  1  、在複審前代碼必須成功地編譯,在所有要求的平臺上,同時要編譯DeBug|Retail 版本。編譯要用團隊規定的最嚴格的編譯警告等級 (例如C/C++中的W4  )。
  2  、 程序員必須測試過代碼。什麼叫測試過?最好的方法是在DeBugger 中單步執行。
  3  、程序員必須提供新的代碼,以及文件差異分析工具。Windiff 或VSTS 自帶的工具都可以。VSTS 中可以通過Shelveset 來支持遠程代碼複審。
  4  、複審者可以選擇面對面的複審、獨立複審或其他方式。
  5  、在面對面的複審中,一般是開發者控制流程,講述修改的前因後果。但是複審者有權在任何時候打斷敘述,提出自己的意見。
  6  、複審者必須把反饋意見逐一提出。注意,複審者有權提出很多看似吹毛求疵的問題,複審者不必每一件事都要親自調查,開發者有義務給出詳盡的回答。
  7  、開發者必須負責讓所有的問題都得到滿意的解釋或解答,或者在TFS 中創建新的工作項以確保這些問題將來會得到處理。
  8  、對於複審的結果,雙方必須達成一致的意見。
        a. 打回去——複審發現致命問題,這些問題解決之前不能簽入代碼;
        b. 有條件地同意——發現了一些小問題,在這些問題得到解決或記錄之後,代碼可以簽入,不需要再次複審;
        c. 放行——代碼可以不加新的改動,簽入源碼控制服務器。

        要記住複審者是通過問這些問題來確保軟件質量的,而不是有意找碴。
       避免不必要的繁文縟節,我們做代碼複審的目的是爲了減少錯誤的發生,而不是找一個人來對着你的代碼點頭。一些簡單的修改不是非得要一個複審者來走一遍形式。在項目開發的早期斤斤計較於一些細枝末節  例如:幫助文件裏的拼寫錯誤,數據文件格式不夠最優化等  、也是於大局無補的,但是,這些問題並不是不用處理了,我們必須建立一些優先級較低的工作項來跟蹤這些事情。

三、在代碼複審中還要做什麼

       好的複審者不光是要注意到程序員修改了什麼,還要把眼光放遠,問一些這樣的問題:

           “你這樣修改了之後,有沒有別的功能會受影響?”
           “項目中還有別的地方需要類似的修改麼?”
           “有沒有留下足夠的說明,讓將來維護代碼時不會出現問題?”
           “對於這樣的修改,有沒有別的成員需要告知?”
           “導致問題的根本原因是什麼?我們以後如何能自動避免這樣的情況再次出現?”

     有些修改看似聰明有效率,但是這樣的修改有可能會讓以後的開發和維護更困難。

四、在代碼複審後要做什麼

       人不能兩次踏入同一條河流,程序員不能兩次犯同樣的錯誤。在代碼複審後,開發者應該把複審過程中的記錄整理出來:
    (1)更正明顯的錯誤。
    (2)對於無法很快更正的錯誤,要在TFS中創建Bug把它們記錄下來。
    (3)把所有的錯誤記在自己的一個“我常犯的錯誤”表中,作爲以後自我複審的第一步。
      有些人喜歡在程序中加一些特定的標記,來跟蹤各種“要做的事情”,例如:
      //$todo:  make this function thread-safe
      //$review: is this function thread-safe?  Need to look at it again
      //$bug: when input array is very large, this function might lead //to crash
      這些標記最好是加上人名,以示負責。如:
      //$bug (AChao): when input array is very large, this function will //   become very slow due to O(N*N) algorithm。

        在代碼複審過程中,$review標記的問題要一一討論,在代碼複審過後,所有的$review標記要清除。在一個里程碑或正式版本發佈之前,所有的$todo:$bug:標記都要清除。

       做標記是不錯的辦法,但是如果開發者光記得做標記,最後卻沒有真正去研究和改正這些潛在的問題,這些todo、review、Bug就會被遺棄在代碼中。過了一段時間後,後來的程序員也不敢碰它們——因爲沒有人能真正瞭解上一個版本的$todo是真的要馬上做,還是已經做過了(done)了,只是沒有更新$todo的註釋,或者問題早已通過別的方式解決了。其根本原因在於團隊沒有用TFS(或者其他的管理軟件)進行記錄,沒有人會跟蹤這些事情。

五、代碼複審的核查表

  1.概要部分
     (1)代碼能符合需求和規格說明麼?
     (2)代碼設計是否有周全的考慮?
     (3)代碼可讀性如何?
     (4)代碼容易維護麼?
     (5)代碼的每一行都執行並檢查過了嗎?
  2.設計規範部分
     (1)設計是否遵從已知的設計模式或項目中常用的模式?
     (2)有沒有硬編碼或字符串/數字等存在?
     (3)代碼有沒有依賴於某一平臺,是否會影響將來的移植(如Win32到Win64)?
     (4)開發者新寫的代碼能否用已有的Library/SDK/Framework 中的功能實現?在本項目中是否存在類似的功能可以調用而不用全部重新實現?
     (5)有沒有無用的代碼可以清除?(很多人想保留儘可能多的代碼,因爲以後可能會用上,這樣導致程序文件中有很多註釋掉的代碼,這些代碼都可以刪除,因爲源代碼控制已經保存了原來的老代碼。)
  3.代碼規範部分
       修改的部分符合代碼標準和風格麼(詳細條文略)?
  4.具體代碼部分
     (1)有沒有對錯誤進行處理?對於調用的外部函數,是否檢查了返回值或處理了異常?
     (2)參數傳遞有無錯誤,字符串的長度是字節的長度還是字符(可能是單/雙字節)的長度,是以0 開始計數還是以1 開始計數?
     (3)邊界條件是如何處理的?Switch 語句的Default 是如何處理的?循環有沒有可能出現死循環?
     (4)有沒有使用斷言(Assert)來保證我們認爲不變的條件真的滿足?
     (5)對資源的利用,是在哪裏申請,在哪裏釋放的?有沒有可能導致資源泄露(內存、文件、各種GUI 資源、數據庫訪問的連接,等等)?有沒有可能優化?
     (6)數據結構中是否有無用的元素?
  5.效能
     (1)代碼的效能(Performance)如何?最壞的情況是怎樣的?
     (2)代碼中,特別是循環中是否有明顯可優化的部分(C++中反覆創建類,C#中 string 的操作是否能用StringBuilder 來優化)?
     (3)對於系統和網絡調用是否會超時?如何處理?
  6.可讀性
       代碼可讀性如何?有沒有足夠的註釋?
  7.可測試性
       代碼是否需要更新或創建新的單元測試?
發佈了26 篇原創文章 · 獲贊 9 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章