代碼改善的目的是為了提高質量,這個質量不僅包括從用戶角度出發的軟件質量,也包括從開發者角度出發的代碼質量。
軟件質量的普遍原理就是改善質量以降低開發成本。提高生產效率和改善質量的最佳途徑就是減少花在這種代碼返工上得時間,無論返工是由需求、設計改變還是調試引起的。?提高質量的方式有協同構建、調試與測試、代碼重構和調整等多種方式,本文記錄了一些主要策略。
協同構建
協同開發實踐往往能比測試發現更多的缺陷,并且更有效率。
“協同構建”包括結對編程、正式檢查、非正式技術復查、文檔閱讀,以及其他讓開發者共同承擔創建代碼及其它工作產品責任的技術。結對編程成本太高,代碼審查倒是非常普遍。
不得不說“協同構建”是確保軟件質量的有效途徑,結對編程本身有很多好處以及技巧,書中花了大量篇幅來介紹,但是在國內互聯網公司,基本上是一個程序員同時負責好幾個項目,很難讓兩個程序員來寫同一份代碼,因此通過”協同構建”來確保質量的重任就落在了代碼審查上面。
代碼走查是比較通俗的說法,細分的話可分為正式檢查(Formal inspections),走查(Walk-Throughs),代碼閱讀(Code Reading)。
正式檢查過程中,每個人都需要扮演一個明確的角色:
- 主持人。主持人一般是老手,能hold住場以及控制進度,不能因為某些細節問題爭論太久而導致會議延長。審查的主要目的是發現盡可能多的缺陷,而不是討論技術問題。
- 作者。作者的首要任務是向大家清晰的解釋代碼。
- 評論員。評論員是同設計和代碼有直接關系的人,但又不是作者,一般不止一個。
- 記錄員。記錄發現的問題,以及記錄因此而產生的修復計劃。
檢查會議的準備工作以及角色指定通常由主持人負責,會議結束后主持人/記錄員需要寫一份缺陷報告,然后作者負責修復缺陷,修復后由評論員來審核這些問題。
代碼走查是一個很隨意的概念,公司的Code Review大多是這種。走查的所有參與者都會看代碼并發現錯誤。走查的重點是檢測錯誤,而非修正錯誤。所以一般走查會議完成后,并沒有專門去檢查最后作者是否將這些缺陷修復了。
代碼閱讀就更加靈活了,就是直接看代碼來發現問題,通常一個人就能完成。雖然檢測出缺陷的概率不如前兩種,但是對于發現一些低級bug還是非常有效的。
測試
測試是改善質量最常見的方式,寫代碼的時候我們需要單元測試/組件測試/集成測試/回歸測試/系統測試,有的是程序員完成的,有的是專門測試團隊完成的。前端的系統化測試流程一直是個難題,我們團隊有人在專門研究這塊,此處不作展開。
調試
調試是確定錯誤根本原因并糾正錯誤的過程,對大部分程序員來說,這可能是開發中最困難的部分,尤其是在前端。
不會調試的程序員會受盡bug的折磨,先看下幾種導致調試效率低下的想法:
- 憑猜測找出缺陷。將print,console,debug散布在程序中,根據輸出來找bug:)
- 只找出問題并用最快的方式修復它,并不去一探究竟。
- 迷信式調試,堅信這個bug不是我造成的,要么是環境問題,要么是其他程序員造成的。如果你遇到了這樣的程序員那恭喜你,因為你再也不會遇到比他更糟的了:)
前端的調試比后臺復雜的多,不過無論何種程序員應該都有一個共識,那就是不僅要發現它,解決它,更要理解它,優雅的解決它。
前端調試發現的問題多半是兼容問題,有的是瀏覽器不支持某些屬性造成的,有的是瀏覽器內部機制造成的,我們需要理解它并且記錄下來,避免下次犯同樣地錯誤。
有一個問題就是,我們經常會在周會里面將自己遇到的一些問題分享出來,避免大家再次采坑,但是分享過后并沒有一個統一的地方來記錄和歸類這些問題,之后在遇到類似問題也很難去查周報記錄,或者有新人進來再次犯錯的時候,它根本不知道這是個坑,很可能在掉進坑之后才意識到。
尋找缺陷
調試包含了尋找缺陷和解決缺陷,通常尋找缺陷并且理解缺陷形成的原因會占到整個調試過程90%的時間,只要能重現問題并且發現原因,修復工作通常很快。所以這里重點講下如何優雅的尋找缺陷。
首先我覺得應該有一個大致思路:
- 出現任何問題首先應該審視自己,先檢查是否自己代碼的問題;
- 如果代碼中沒有發現問題,再確認一下是否自己的構建工具有問題,工具問題需要找維護工具的開發者解決,自己去解決或者規避非上上策;
- 如果工具也ok那再確定是否電腦環境的問題,這個環境包括了系統本身的host綁定,網絡情況,以及依賴庫,或者瀏覽器配置等等,經常有人遇到此類情況會嘗試重啟電腦,有時還真的管用;
- 如果還有問題,跟你同事確認下是不是其他人有發布相關代碼,而這部分代碼剛好影響到你工作了。
- 如果還是有問題,出去浪一會兒,然后再回到步驟1。
然后再重點說下第一點,畢竟出現問題原因多半在自己。首先要優雅的找出問題,一定要能夠優雅的使用各種調試工具,比如chrome dev tool,weinre,Safari,Charles等,除了知道各種常用技能,深入學習還能學到一些特殊動作,比如我們都會用Chrome的斷點調試,但是卻很少用條件斷點,然而條件斷點又是一個很實用的技能。這些工具和技巧都能讓你更快的發現問題。
當我們遇到問題時,控制臺或者編譯器一般都會報錯,我們根據錯誤提示去解決問題。
有時候js報錯根本就跟錯誤代碼沒半毛錢關系,兩者差了個十萬八千米,而且給出的錯誤行數也并非總是有用,尤其是在壓縮混淆后的代碼。
針對這種編譯器瞎BB的錯誤類型,我們很難直接通過錯誤提示找到原因;而更頭疼的時,在有的框架里面,你的代碼出問題時編譯器居然不報錯!這個時候很難從正面去解決問題,那么就得換一種思路。
先對你的代碼做語法檢查。如果是方法調用錯誤或者IO錯誤的話,提示一般還是很有用的。而某些語法錯誤,比如少個括號,分號,或者你把某個單詞拼錯了,或者引用包的路徑不對,或者json配置文件少個逗號,這時編譯器可能只是進行了一些拙劣的掃描,于是便給出了錯誤的行號。
如果錯誤是最近才出現,或者之前沒有的,直接查看版本歷史記錄,分析代碼。
分而治之。 將程序劃分為幾個模塊,對這幾個模塊分別測試或者嘗試注釋掉某個模塊來逐漸定位問題。
以上是當通過編譯器的錯誤提示很難找到具體問題時,總結的幾種方法。針對這類問題我還沒有找到一個很有效的方式,希望有高人指點。
修正缺陷
尋找缺陷雖然占了90%的時間,但是找到問題只是成功了一半,最終還是要解決問題的。
解決問題的結果有三種:一是拙劣的解決了問題,二是優雅的解決了問題,三是解決了這個問題又出現了新的問題!
好吧,這只是經驗之談,因為這三類情況我都遇到過。。。
第一種情況出現的原因有很多種,可能是情況緊急,不得已采取的臨時方案,但是治標不治本;也可能是沒有理解程序本身,只是解決了某些情況下的問題;也有可能是代碼寫久了自己懵逼了,思維定勢了,出去浪一下說不定就會想到一個更優雅的解決方案。
第三種情況出現的原因多半是不理解業務造成的。尤其是在修改別人的bug時,一定要保證代碼的侵入性最小,避免去修改全局變量。而且改完后最好做充分測試。
重構
前端技術發展飛快,我們昨天使用的技術,今天可能就被淘汰了,明天可能又換了新技術。Angular火了,項目用Angular重構,雙向綁定真是爽!React火了,又用React重構一次,組件化大法好!vue火了,又開始用vue重構,輕量級就是靈活!后來WEEX火了...(當然實際情況應該不會如此,我只是開個玩笑)
所以說我們最初重構的目的是什么?
當然是為了提高質量,一方面提高軟件外部表現質量,比如性能,穩定性等,另一方面提升軟件內部質量,比如可維護性,可擴展性等。還有一種因素就是業務因素,不同的框架適用于不同的業務,如果重構使用的新框架能更適合當前業務,那也是極好的。
不過框架只是包含了特定軟件思想的外殼,能解決一部分問題,但不能解決代碼本身質量問題。因此拋開框架,在重構時我們通常要關注以下點。
- 重復代碼。復制粘貼即設計之謬。
- 冗長的子程序。很少會有超過一屏的子程序。
- 長循環與深嵌套。
- 內聚性太差的類或者模塊。如果某個模塊包含了許多彼此無關的方法,那么這個模塊本身的設計就是不合理的。高內聚,低耦合,能實現這兩點的人代碼設計水平一定很好。
- 擁有太多參數的方法。
- 一處變化,多處修改。如果外部有一個小小的改動,或者產品加了一個新的需求,你需要在多出進行重復的修改,那么這個設計也是有問題的。
- 流浪數據。把數據傳遞給某個子程序,然后子程序將數據又傳遞給另一個子程序,這樣傳來傳去的數據被稱為流浪數據。請確定是否有此必要。
- 命名問題。之前也說過變量命名,方法命名等技巧,重構的時候也需要注意。
- 給難懂的代碼寫注釋。這里的注釋是辯解。某位大神曾說:不要為拙劣的代碼寫文檔——應當重寫代碼。
- 全局變量。在修改別人的代碼時,你會切身體會到全局變量有多危險,應當避免使用全局變量。我還見過一種寫法,就是在一個對象里面包含了一些公共屬性,然后在這個類的各種成員方法里面會修改這些公共屬性,然后這些公共屬性又能暴露給其他對象使用,我覺得這樣的寫法會破壞封裝打破內聚,是一種寫起來很爽但是維護起來相當危險的做法。
- 超前設計。程序中的代碼似乎是在將來的某個時候用的。書中認為超前設計是一種畫蛇添足的做法,增加了程序的復雜性并帶來了額外的工作量。我覺得并不完全正確,一名優秀的程序員應該是充分了解業務的,雖然不能直接寫針對未來特定業務的代碼,但是可以對目前的某些方法進行增強,增強其擴展性。
以上關注點偏重于前端開發,對于繼承,抽象這塊的話在JS中體現的并不強所以沒有關注,大家也可以去翻翻書。重構的更多細節,可以參考 Code Complete — 創建高質量的代碼中的一些點來進行。
最后再記錄一下重構的幾個要點:
- 重構成功的關鍵在于程序員是否知道那些標志著代碼需要重構的點,并且程序員本身也能寫出高質量的代碼;
- 要有安全重構的策略。重構有風險,務必要小心,同一時間只做一項重構,測試通過之后再進行下一步會比較好。
- 改代碼是程序員始終要做的事情,開發階段的重構是風險最小,提升程序質量的最佳時機!
代碼調整策略和方法
很久以前,計算機的性能有限,所以人們通常會書寫更利于節省資源的代碼。后來硬件越來越強大,程序員們意識到過于關注性能會損害程序的可讀性和可維護性,于是便開始轉移焦點。時至今日,代碼調整的最終目的是什么?是為了高效?還是可讀性和可維護性?還是兼具兩者?當然兼具兩者最好,然而有時候高效的代碼并不一定就是“更好”的代碼。
性能優化誤區
- 代碼越少程序越快。現在應該不會有人這樣想了吧。
- 特定的運算可能比其他的快。在某些瀏覽器下面高效的寫法在另一個瀏覽器下面可能會很慢,所以在代碼調整的時候一定要小心效率最低的那個。比如DOM操作。
- 應當隨時進行優化。當程序沒有完成時,你很難知道瓶頸在哪里。而在一開始就想做到最優,往往會干擾自己對程序目標的理解和判斷。二八法則同樣適用于軟件開發:80%的性能,耗在了20%的代碼上面。
- 程序的運行速度與其正確性同樣重要。很顯然正確性更重要。
何時調整代碼
程序員應當使用高質量的設計將程序編寫完成,使之易于維護,之后再去檢查系統性能,然后分析瓶頸并對其優化。
常見低效之源
- 輸入/輸出操作;
- 操作系統交換內存頁面;
- 系統調用。調用子程序會涉及上下文切換;
- 解釋性語言。所以說,這是語言的先天缺陷;
- 錯誤。沒有去掉調試信息,忘了釋放內存,超時等。
總結
性能只是軟件整體質量的一個方面,通常不是最重要的。只有當性能影響到用戶體驗時才需要進行優化。