豆瓣 CODE 兩年歷程:git不是萬能的,沒有review是萬萬不能的
2014 年 2 月 14 日是豆瓣 CODE 項目成立兩周年,同日豆瓣 CODE 團隊宣布將整個框架開源。本次開源包含 CODE 框架以及 Mikoto、Linguist、P、CodeLive 等周邊項目,感興趣的工程師可以在這個基礎上繼續開發。
下面,豆瓣 CODE 的工程師們將在 InfoQ 上分享一些 CODE 背后的故事。
背景
2012 年初,已經對 SVN 和 Mercurial 無力吐槽的豆瓣工程師們正面臨一個艱難的決定:是往 Github 企業版遷移,還是自己研發一套代碼管理系統。
CODE —— C: Community O: Original D: Developer E: Eldamar
</blockquote>2012 年 2 月 14 日,清風老師為豆瓣 CODE 寫入了第一行代碼;一周之后,一個用 Trac 改造的原型誕生了。CODE 不是一個公司項目,自始至終沒有任何一個豆瓣的產品團隊或工程團隊為這套系統負責,但一個又一個的豆瓣工程師開始被吸引到這個項目周圍,形成了一個強大的 虛擬團隊。
隨著 Github 在國內的普及程度越來越高,越來越多的企業也開始探索自建 git 倉庫之路。開源的 Gitlab 項目開始受到部分團隊的關注,但這個項目仍有較多局限性。這時,豆瓣 CODE 引起了包括淘寶在內不少同行的興趣,并開始溝通將 CODE 開源的可能性。
作為一個面向內部設計的系統,CODE 的源代碼中混雜了大量豆瓣專有系統的依賴,如果要開源出來需耗費不少的工作量,之前清風在 C2D2 的分享和段念在 QCon 上海 2013 大會的分享中都提到這一點:
CODE 在豆瓣內部是運行到 DAE 上面的,一些基礎設施的維護都靠豆瓣平臺組的工程師,如果要開源的話需要把豆瓣內部的依賴移除。另外豆瓣內部也有一套 Web 框架,CODE 用的就是這套內部框架,如果 CODE 開源,這套框架也需要一并開源。
</blockquote>雖然有種種麻煩,但豆瓣的工程師們仍然將大量工作之外的時間熱情的投入到將 CODE 開源的工作當中。一開始,CODE 團隊零星的將一些底層的基礎庫開源,包括 Python 版本的 git HTTP 實現 GPack,git SSH 實現 Maria,Pygit2 的 wrapper Ellen 等;2014 年 2 月 14 日,豆瓣 CODE 宣布將框架代碼和周邊項目代碼全部公開!此時的 CODE 已經聚集了 85 位 committer 為其貢獻代碼,并且已經托管了 1916 個項目,其中大部分都是類似 CODE 這樣的、由工程師自主發起的非官方項目。
技術說明
CODE 在豆瓣內部對應一個 DAE 應用,其實就是一個 Web 項目。CODE 目前最核心的工作是對 git 倉庫的讀寫,另外就是 code review。
</blockquote>CODE 發展兩年來一直在擴展自己的定位,從單純的代碼管理系統發展為可以將設計、產品都拉進來一同協作的平臺,周邊衍生出大量的附加項目,從 image diff 到類 Dropbox 的文件同步工具,可謂五花八門。所有這些功能都圍繞一個核心:對 git 倉庫的讀寫。
XTao:目前 CODE 支持 http 和 ssh 協議進行 clone 和 push 操作,http 目前我們使用的是 git_http_backend 這個開源項目,我們自己也用 Python 實現了一個 Grack(原項目基于 Ruby),即 GPack,目前還沒有大規模的使用。ssh 我們用的是 CMGS 同學寫的 Maria。
CODE 的 Web 版 git 操作的變化很大,一開始直接使用 subprocess 調用 shell 的 git 命令,然后解析命令返回的文本,python 對象化,然后供 Web 使用,后來因為性能問題開始使用 Pygit2,從此就是 git 命令和 Pygit2 混用,一直到現在。我們的這個庫也開源了:Ellen。
Pygit2 是 libgit2 一個 Python 接口,在使用 Pygit2 和 libgit2 的過程中,也經常當小白鼠,也給 Pygit2 和 libgit2 提過一些 Pull Request。對 Pygit2 我們改動比較大,因為 CODE 使用模式問題,我們有一個自己的 fork 版本,跟上游的主要區別有:
- 把 libgit2 內嵌到 Pygit2 里面,做了靜態編譯,不再依賴系統的 libgit2 版本,方便升級 Pygit2。
- 一直采用最新版本的 libgit2,Pygit2 本身比較穩定,不能及時的使用 libgit2 最新的方法。
- 為了性能寫了一些特殊的方法。
</ol>總體來說 libgit2 實現了核心的 git 接口,并不是很成熟,但是對于 Web 應用來說已經夠用了。
以上的部分是與 git 操作緊密結合的部分。我們在 Web 界面也做了一些核心功能,主要包括 Pull Request 的實現,代碼 diff 和 review 功能的實現,以及 Issue 和 Gist 系統。這里說一下代碼 diff 和 review 功能,在實現的時候,一方面是用了 git 操作接口的 API,一方面是對于代碼片段做了處理,在前端頁面上支持 diff 的按行展開評論等一些功能。
同時,CODE 團隊也在嘗試分布式 git 方案,目前也已經有個原型了。
</blockquote>規則的建立
豆瓣曾經歷過大桌子開會、強制大家做 review 的歲月,而 CODE 顛覆了這一情況。CODE 團隊將 code review 視為 CODE 的第二個核心功能,認為促進工程師之間的溝通是 CODE 最大的成就之一。
CODE 為每個項目設置了三個角色,分為 owner(有全部權限)、committer(有 push 和 merge 權限)、member。review 機制根據項目的不同設置了不同的規則,如產品線級別的、需要對外發布的項目,基礎庫等項目都需要經過嚴格的 review,如
東西團隊
對 review 設置了如下規則:
- 尊重他人,就事論事,對事不對人,畢竟每個人都寫過爛代碼;
- PR 中的每一個 commit log 都應該可以和代碼對應,方便 review;
- 盡量不要發太大的 PR,以免引起 reviewer 的恐慌;
- 建議保證一個 PR 的粒度和專注,最好不要出現一個 PR 里又有重構又加新 feature 的情況,同樣容易引起 reviewer 的恐慌;
- 提 PR 之前請確保在本地或測試環境一切正常;
- reviewee 如果接受 reviewer 提出的修改意見,需要在修改提交以后知曉 reviewer,常見的做法可以是在 review comment 處回復(并帶 commit 鏈接);
- 評論中至少出現一個 lgtm 且保證 ci 通過之后 PR 才可以被合并;(注:lgtm 即 looks good to me 的縮寫)
- PR 合并者與提交者不能是同一個人;
- PR 需從一個特定分支(分支的名字盡量能表達代碼的功能)發往上游的 master 分支;
- Model 的部分,如不緊急需要 unittest;
- Web 的部分,如不緊急需要 webtest;
- PR 合并后如引起 bug 或功能異常,并經查確為此 PR 引起,提交者需請全組攻城濕喝飲料或吃冰棍(由被請者決定);
- 將 fork 的倉庫與上游同步時,應使用 git fetch upstream && git rebase upstream/master (或 code sync -r ),而不是 git merge 或 code sync (這里 code 是指面向 CODE 系統的一個命令行工具),以保持清晰的提交歷史,并防止覆蓋他人的修改;
- 注意安全問題,對于 SQL 拼字符串,模版中有 n 的,以及處理用戶輸入等地方都需要仔細 review,更多請參考 Web 安全開發指南
</ul> </blockquote>對于松散或娛樂性項目、小工具項目,并不會那么嚴格的 review,這也取決于 owner 自己,他可以借這個項目尋找到一位導師,來幫助他進行 review:
舉一個具體的例子,例如東西團隊的 Android 版本的開發,實際上最開始只是團隊內部的一些成員想學習 Android 開發自發組織起來的,但一開始就找了移動組的同學來隨時幫助 review。
</blockquote>對于 CODE 項目本身,所有工程師都可以向 CODE 上的任意項目提 PR,也都可以是
CODE 的 reviewer
,同時所有工程師的代碼都需要經過 review 才會被 merge 到 master 分支。發展到現在,豆瓣的 review 基本上都是自發,很少遇到需要 review 的代碼堆積的情況。代碼討論區里據說時不時會出現美女圖,這可能是刺激工程師們去 review 的因素之一;另外,CODE 系統本身也有獎勵機制,鼓勵大家去評論別人的代碼。
CODE 系統的獎勵機制主要有積分和勛章這兩個部分。積分的規則主要就兩個:
- 提交的 PR 被 merge,增加 100 點積分
- 提交的 PR 被評論,增加 5 點積分
</ol>目的就是鼓勵多發 PR。一般來說,小 PR 要好過大 PR,不過有時候開發任務比較緊的時候,發出比較大的 PR 也是在所難免。
勛章系統在 CODE 早期階段就做了進去,早期的獎勵規則主要跟代碼提交相關,例如給開源項目發過 Patch 并被 merge 會有相應的徽章。現在 CODE 團隊對勛章系統有一些新的規劃:
目前希望徽章系統可以獨立出來,只是一套獨立的 API,任何人任何產品線都可以去設置自己的獎勵規則,讓這種獎勵變成不是一種公司行為,而是更小的行為。例如 antispam 組,可能就會有
</blockquote>百人斬
徽章,這個對于其他組可能就不是那么必要,當然如果你跨界幫助過 antispam 組,那么也有可能會獲得這個徽章。CODE 上沒有設置懲罰機制。
測試
相比 Github,CODE 有一些非常實用的地方,比如在提交代碼入庫之前可以先在 CI 里面完成自動測試,reviewer 可以直接看到代碼測試是通過(綠色)還是失敗(紅色);代碼完成 merge 之后還可以通過 DAE 直接往線上部署。持續集成、自動測試、監控、部署這些都是獨立系統,與 CODE 都是靠 API 來進行交互。
對于測試的實現,豆瓣對開發者有明確的要求:
單元測試和集成測試的代碼都是由開發者自己提供的,這是一種義務。測試的寫法就是按照標準的 Python unittest 來寫,斷言比較推薦直接寫 assert x == y。
</blockquote>每周,豆瓣的測試組都會發出各個組的測試覆蓋率報告,比如:
全站 35% 35%biz 47% 47%book 42% 42%estel 68% 67%fm 30% 29%group 59% 59%ilmen 57% 57%loc 38% 38%movie 49% 49%music 43% 43%travel 58% 58%waymeet 56% 56%測試覆蓋率基本上都是在 40% 上下,最低要求是覆蓋到關鍵流程和邏輯。
投奔 git 之路
CODE 團隊在下面的問答中分享了他們使用 git 的心得和經驗,無論對正在使用 git 的團隊還是還沒有使用 git 的團隊都是很好的參考。
InfoQ:你們當時從 SVN、Mercurial 轉換到 CODE,這個過程好像還挺順利的?有沒有遇到過一些阻力?如何化解的?
清風:其實過程并沒有想像的那么愉快與順利,這個過程 CODE 虛擬團隊還是做出了很大努力的,日常的營銷(拉人下水是很重要的),對于需求的快速滿足,這些才是保障 CODE 逐漸被接受的重要因素。
開始的時候 CODE 功能比較簡略,但大家畢竟都是用過 GitHub 的人,所以對功能上肯定是有要求的,經常會聽到,某某某功能 GitHub 是有的,但 CODE 就沒有,這個沒法用啊,要不咱們還是嘗試買 GitHub 企業版?這個時候其實也沒有什么特別的方法,就是需要快速的去滿足這個需求,并加以實現,可以比較自豪的說,CODE 的一些關鍵功能都是在 1-2 天,大一點的功能不超過 1 周實現的。
而且有趣的是,甚至有些功能在一開始的時候會認為和 GitHub 不一致,但后來發現習慣了甚至變成了一個設置項。最早 CODE 是雙擊某一行來進行 line comment 的,但 GitHub 是點擊行首的 “+” 號,進行 comment ,后來 CODE 也實現了行首點擊 “+” 號來 comment 的功能,但很多成員覺得雙擊很方便。這個還產生了很多的爭論,有從方便性說的,有從 UI 人機交互規范談起的,為了統一這兩個派別,于是我們只好把這個做成一個設置項。
黃小毛:補充一下,豆瓣內部的團隊是一個一個的遷移到使用 CODE 上的。在這個過程中,每個團隊都會因為開發內容、使用工具、流程的不同,對 CODE 往往會提出不一樣的需求,CODE 開發過程中都分別為這些團隊做過相應的開發(其實也是因為這些功能之前都太簡陋),一步一步的完善功能。常常是某個團隊遇到了什么問題,CODE 開發人員都直接上門去解決,如果解決不到的,就加功能回來開發,一步一步得到使用團隊的信任;同時因為 CODE 在豆瓣內部是架在 DAE 上的,常常需要 DAE 團隊緊密的配合;各個產品部署上線的一套系統也逐漸使用 CODE 做為倉庫之后,與 DAE 和 SA 的合作也逐步加深,就是這樣慢慢的讓整個產品線、平臺部門、SA 部門,都轉移到 CODE 上來的。
說個笑話,以前 CODE 開發還不穩定的時候,極少在白天上班時間上線,擔心如果有什么狀況,豆瓣幾乎所有的工程師都要等著,耽誤開發,可見豆瓣工程師對這個開發環境的依賴。這也是一步一步過來的。
</blockquote>InfoQ:對于 git,CODE 團隊有沒有特別的經驗要分享?
CODE:大體上 git 就像大家那么用沒有什么問題,只是開發過程中其實發現了一些 git 的問題,比如 git 編碼存在問題,沒有對 commit log 和 path 做 encoding ,在 Web 顯示的時侯需要額外的處理。可以參考下這篇文章。
git 對大文件大倉庫的支持不是很好,當一個項目變大的時候,適時的拆分一下。要不然就要像 Fackbook 那樣去 hack hg 來支持大倉庫。
GitHub 的 fork 模式導致磁盤空間被大量重復文件占用,GitHub 的工程師解決了這個問題,CODE 團隊也在盡量解決這個重復文件的問題。
Java 實現的 git 庫 CODE 團隊還沒有研究過,但是從 C、Python、Ruby 以及 Javascript 實現的 git 庫并沒有一個完美的解決方案,都需要混用 git 命令和第三方庫。期待在不久的未來,libgit2 能完全取代 git 的核心庫,成為一個完善的 git 庫。目前 libgit2 還有一些內存泄漏的問題沒有解決。
</blockquote>InfoQ:如果現在讓你們重新設計 CODE,會在哪些方面做調整和重新的規劃?是否會考慮基于類似 GitLab 進行二次開發?
CODE:現在的重構其實就是一次重新設計,最重要的方向就是把 API 和 UI 分離。
最開始做 CODE 的時候,其實考察過 GitLab,可惜當時的 GitLab 還不夠成熟。如果當時的 GitLab 足夠成熟,很有可能就會基于 GitLab 來開發了,不過,幸好沒有發生這樣的事情。
</blockquote>InfoQ:你是否會建議所有還在使用 svn 和 hg 的團隊早日脫離苦海、投奔 git?
CODE:這個問題我一直是這么看的:git 固然強大,但是能否正確使用也是關鍵。我走訪過很多團隊,有些團隊可能連版本控制工具本身都還沒有掌握,例如每天提交一次,甚至一周提交一 次,commit log 亂寫,不使用分支,等等,這些都是使用者的問題。像 非死book 最近就表示他們 hg 用的就不錯(當然他們做了很多修改)。如果已經有了版本控制工具的正確使用習慣,那么我個人還是推薦 git。
</blockquote>相關資料
相關演講和分享
- DAE 系統的設計(洪強寧教授在 PyCon 2013 上的演講,文字摘要參考這里)
- 清風在 C2D2 的分享(Slide)
- 段念在 QCon 上海 2013 大會的分享
</ul>CODE 相關開源項目列表
- CODE 框架
- GPack
- Ellen
- Linguist
- PyCharlockHolmes
- Scanner
- Mikoto
- P
- CodeLive
</ul>CODE 依賴的 Douban 開源庫列表
- https://github.com/douban/douban-utils
- https://github.com/douban/douban-sqlstore
- https://github.com/douban/douban-mc
- https://github.com/douban/douban-quixote
- https://github.com/douban/python-libmemcached
- https://github.com/douban/douban-orz
</ul>感謝以下豆瓣的同學在本次采訪中提供的支持
- 段念:豆瓣網工程副總裁,本次采訪的策劃。
- 清風:CODE 團隊 leader
- 黃小毛:CODE 團隊成員
- XTao:CODE 團隊成員
- 大落:CODE 團隊成員
</ul>來自: InfoQ<span id="shareA4" class="fl"> </span>
本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!