高質量的代碼庫能使產品迭代、協作和維護變得更為容易,加快長期項目開發的速度。在Quora,我們十分重視代碼庫的質量。
然而,縱然有上面提到的好處,維護高質量的代碼仍會間接耗費大筆費用,占用開發周期。權衡得失對很多人而言是件難事,他們面臨相悖的兩個選擇:降低代碼質量以提高開發速度;還是放緩步伐,提高代碼質量?對初創公司而言,開發速度快更重要,所以人們假定你會犧牲代碼質量。
通過設計一系列工具和相應流程,我們得以打破僵局——在快速開發中仍能保持高品質代碼。本文將介紹我們在Quora使用的這套方法和具體案例。
維護高質量代碼的原因
高質量代碼主要有助于加快長期項目的開發速度,這是我們的著眼點以及宗旨。只需在開發前期編寫更整潔的代碼,這些短期成本就能換來長久的收益。
在該理念指導下,我們發現以下四個基本原則對保證代碼質量有益:
Quora代碼質量原則
代碼應可讀易讀——閱讀代碼比寫代碼更耗時間,因此,為盡量減少閱讀時間,適當放慢寫代碼的速度也無妨。
每部分代碼應符合其對應的質量標準要——不同的代碼行有不同的使用期、適用范圍,它們的崩潰風險程度、修復崩潰和破損的費用各異。綜上,不同代碼行對長期迭代速度的影響迥異,強求所有代碼行都遵從統一的質量標準并非佳抉擇。
維護代碼質量的成本可被縮減——通過自動化技術、使用更好的工具和程序、提高開發者素質,往往能降低因維護高質量代碼而產生的間接成本。
注重代碼庫編碼一致性——雖就局部而言,有的代碼還可以更好,但保持整個代碼庫的一致性仍然有意義。代碼風格不一會加大閱讀和理解難度(見點),編寫及使用自動工具優化也會遇到困難。
接下來,我談談在開發過程中如何實踐這些原則。
提交代碼后審查
在我們的代碼庫中,每處修改都必須在以下六個方面接受同行評議:正確性、隱私、性能、構架、復用性和風格。由于閱讀代碼在代碼審查中必不可少,代碼審查自然能(大幅)提高代碼可讀性。
但不巧的是,代碼審查也會降低開發速度。例如,預提交代碼前預審查制是行業慣例,雖然只有2到3次迭代,每輪審查也僅需兩天,卻很容易讓程序員在這里浪費大把時間,工作停滯不前。
在Quora,我們一般使用提交后審查,即先在生產環境中使用代碼,再派人審查。昨天,我們中的48個人一共提交了187次代碼,這個數據可以幫助你了解我們的提交規模。提交后審查制的好處在于,它將程序員從等待(預提交)代碼審查中解放了出來,讓他們能繼續其他任務。代碼審查員也能更好地規劃工作時間,他們不必因程序員的等待而加緊審查的節奏,相反,他們可以自由選擇合適的時間工作。我們預期所有代碼通過審查應在一周內,但通過合理安排,多數審查都能在1到2天完成。“一周”這個時間是經過慎重考慮的,它夠長,以便審查員靈活安排,也夠短,將可能出現的壞結果降到低,例如減少在代碼庫中傳播差勁的代碼。在實踐中,它也契合了大部分開發者的日常工作節奏。
之所以能采用提交后審查,是因為我們充分信任每位開發者 —— 畢我們只聘用的人,并且在代碼質量培訓和工具上投入了大量資金。這也督促我們更嚴格地測試代碼。就算還未經過任何審查,良好的測試覆蓋率也有助于確保代碼正確。在此基礎上,我們還使用了Phabricator,一款可靠且可靈活配置的審查工具。我們對它做了幾項改進,這樣一來,在提交后審查工作流程中,效果就更為出色。例如我們構建了一款命令行工具,用來生成代碼和請求審查。有了它,Phabricator就能在不修改已提交代碼的情況下繼續執行diff命令。
話雖如此,對于不同類型的代碼修改,我們的審查標準有別。如果潛在的漏洞/錯誤會造成嚴重的問題,代價高昂,我們就會改變平時的提交后審查流程,轉為提交前審查。典型情況包括:
觸及用戶隱私和匿名的代碼
觸及影響大量其他代碼的核心抽象
觸及基礎結構的某些部分,它們有問題會導致宕機
這也取決于開發者的傾向 —— 如果他們想通過提交前審查來收集更多建議,也可如其所愿,不過這種情況少之又少。
代碼審查任務分派
為了讓審查達到良好效果,每處更改都須由背景知識足夠豐富的人來把關。如果審核代碼的人同時也負責后續維護就更好了,這樣便能長期受益。
我們實現了一個簡單的系統,在模塊/目錄等級的代碼“ownership”上添加標簽,例如:
__reviewer__ = 'vanessa, kornel'如果提交新變更,系統就自動會分析文件(或其上層目錄),將新審查者的名字添加到提交數據中。我們還有額外的規則將代碼審核派發給合適的人,例如在A/B測試中,系統會自動指派一位科學家作為審核人。 其實,為了能輕松地添加更多定制“分配”規則,我們還制作了一些工具。舉個例子,只要我們想,就能輕而易舉地把所有新員工提交的代碼派發給他們的導師來審查。
借助智能系統,把審查任務派發給恰當的人,不僅減輕了程序員的負擔,還能確保每個審核者都是佳人選。
代碼測試
在我們的開發流程中,代碼測試是重要一環。我們撰寫了覆蓋各個環節(包括單元、函數和UI等)的測試案例。覆蓋全面的測試可以幫助開發者快速前進,不需擔心已有功能受影響。為了減少撰寫測試案例的成本,我們投入大量時間開發測試框架(基于nosetests),令完成這項工作更便利、快捷。
我們還開發了多種自動化測試工具。正如之前我們發布的一篇關于持續部署系統的文章所講述的那樣,一臺中央服務器會在所有新代碼部署前運行一遍所有的測試。測試服務器高度并行化運作,因此即便是運行全部的測試也只需5分鐘。如此快捷的運轉,激勵開發者經常撰寫和運行測試。還有一個叫做“test-local”的工具,僅用于識別和執行相關測試文件,從而測試正在運行的代碼中發生的改變。為了讓這個工具正常運轉,測試必須是模塊化的(也便于在測試失敗時進行快速故障排除)。為了實現這個目標和其他一些測試代碼所必須的屬性,我們維護了一個共享文檔,用于描述撰寫測試案例的佳實踐。這些指南都在代碼審查中嚴格執行。
通代碼審查一樣,對于不同類型的更改我們也有不同的測試標準,不論何時不論推倒已有工作的成本有多高,我們都需要更高的測試標準。
所有這些系統加在一起幫助我們充分利用那些為了在長遠開發流程中減少損耗而撰寫的測試樣例。
代碼質量指南
我們非常樂意共享代碼質量標準指南,因為它有幾個作用:
這些指南是培訓新開發者的極佳工具
這些指南幫助我們給整個團隊分享基于我們開發實踐的智慧和佳實踐。
設定通用的標準,從而幫助我們維持代碼庫的一致性。
減少開發過程和代碼審查過程中的損耗。比如,在每一次代碼審查時都討論一段代碼應該是80個字符還是100個字符是沒有意義的,如果只需討論一次,做出定論,未來都不需討論,那么就可以減少很多損耗。
除了給不同的語言設定語法風格指南,我們還給更抽象的事物設定了指南,如:如何撰寫好的測試樣例、如何組織代碼模塊來減少閱讀時間。這些指南都不是一成不變的,而是會隨著開發更容易理解的代碼而不斷調整。我們還積攢了一大堆代碼重構工具(其中包含像codemod這樣的開源工具,頁包括一些我們自己開發的工具),這些工具在一些我們需要回溯的案例中非常好用,能夠在我們修改風格指南后幫助我們“修正”過去所有的代碼。
舊代碼清理
快速前進的團隊會嘗試不同事物,雖然期望新事物可用,但也有很多沒法用,這很自然。因此,任何快速發展的公司的代碼庫中都會有些質量參差不齊的代碼,有些代碼不再使用了,但留在那里不動又會造成更多的麻煩。清理這些質量參差不齊的代碼能保證代碼庫的健康,提高開發的速度。
我們會定期組織“清理周”,來清理這些質量差的代碼。在這段時間,有些團隊甚至有時是整個公司會拿出時間專門清理舊代碼。這種定期的清理減少了“一般開發工作”和“代碼清理工作”之間工作狀態切換帶來的成本損耗,并且讓代碼清理變得更社交化,也更有意思。
代碼庫中有些代碼要比其他代碼更容易清理。同樣的,清理代碼庫中的一些代碼也對開發速度有不同程度的影響。為了能充分利用花在代碼清理上的時間,我們根據清理的成本和清理后會對未來開發速度的影響來對需要清理的模塊進行優先級排序。
代碼校驗(Linting)
在一次性實例中如果不遵循句法規則的代碼質量指南(如docstring或代碼長度的格式)我們很容易低估這造成的成本損耗,但是時間一長,這些成本是會累加到一起的。記住和應用許多不同的規則經常讓人很惱火,尤其是這些規則還在不斷增加的時候。所以,當工業界中的許多人決定不遵循哪些規矩的時候,也沒什么奇怪的。
我們開發了一款叫做“qlint”的內部使用的代碼校驗器,用來減少那些惹惱人們的麻煩。Qlint是一個“聰明的”代碼校驗器,既懂得文本,也理解結構,還能處理AST,它是基于flake8和pylint開發的。設計qlint的初衷是讓以后添加自定義的lint規則變得簡單。比如,我們遵循的一個規則是:Python所有的“私有”變量之前都應該有一個下劃線,所以我們在qlint中增加了一個規則,這個規則可以檢測所有沒有加下劃線的私有變量,并判定為“非法”。
我們在許多系統中都嵌入了qilint來提供無縫的開發環境,這樣一來開發人員不必時刻自己關注lint錯誤。首先,qlint完全兼容我們常用的幾款編輯器,如Vim、Emacs和Sublime,并且一旦某個規則被違犯,編輯器內就會出現可見的反饋(紅色標記)。qlint還整合到了我們的push流程中,任何時候一有人push代碼,qlint就會啟動。事實上,qlint還會根據commit時違反的規則來阻止代碼部署。我們的代碼開發風格指南也和qlint集成到了一起,對于每一處lint錯誤,qlint都會建立一個超鏈接,鏈到風格指南中相應的規則。我們的代碼審核工具Phabricator也被設置成可以使用qlint。通過這樣的方法,所有被qlint發現的錯誤都會用可見的標記標出來,讓代碼審核變得非常簡單。
所有這些改變都在損耗很小的前提下提高了我們代碼的一致性和質量。
結論
正如本文所述,在Quora我們非常看重代碼質量。對此我們非常務實,架構了非常好的系統、工具和流程來確保我們可以促進和維持長遠的開發效率。同時,今天的我們也努力保持平衡,我們的團隊不斷成長和進步,所以我們相信在未來我們會開發出更多的工具和系統。
本站文章版權歸原作者及原出處所有 。內容為作者個人觀點, 并不代表本站贊同其觀點和對其真實性負責,本站只提供參考并不構成任何投資及應用建議。本站是一個個人學習交流的平臺,網站上部分文章為轉載,并不用于任何商業目的,我們已經盡可能的對作者和來源進行了通告,但是能力有限或疏忽,造成漏登,請及時聯系我們,我們將根據著作權人的要求,立即更正或者刪除有關內容。本站擁有對此聲明的最終解釋權。