話說本人從畢業到現在一直在某 B 公司工作,前些年折騰過不少開發方式和工具,但總覺得或許有更好的方案,所以很好奇其它公司內部是如何工作的,我曾經瀏覽過某 Y 公司內部無所不包的 TWiki,也拜訪過某 F 總部瞭解他們的開發流程,但對某 G 公司卻瞭解不多,只零零碎碎知道一些,這兩天抽空梳理了之前收集到的各種資料,希望能給 FEX 後續改進提供參考。
注意:以下內容主要資訊來自網上收集、『In The Plex』這本書及閒聊,純粹為了技術交流和討論,僅代表個人觀點,本人從未在某 G 工作過,不受 NDA 限制,但大部分資訊無法確認真偽,加上某 G 很大,每個部門都有可能不一樣,所以這裡的資訊也是比較片面的,歡迎大家提供更多參考資訊。
分工協作
首先,某 G 大部分產品線都不區分前端工程師和後端工程師,一個人需要用從前到後都負責開發,儘管這幾年似乎有變化,能看到專門的Front End 職位了,但應該是很少數產品線的做法。
N 年前有人去 G 面試過,和他閒聊後瞭解到某 G 要求應聘者必須至少要會 C++ 和 Java 中的一種,只會 Python/PHP 是不夠的,要是隻懂 JS 就更不行了。這個資訊很關鍵,能用來解釋一些內部現象,後面我會提到。
我個人認為前端工程師確實應該瞭解基本的後端知識,某 B 公司以前很多前端工程師都只負責模板(比如 Smarty)開發,這導致一個很嚴重的問題,那就是前端工程師往往不知道如何搭建環境,開發時需要依賴後端工程師提供環境和資料,嚴重影響了開發效率,這也是為什麼 FIS 還內嵌了本地服務功能。
另外國內有公司還對前端工程師做進一步細分,按照職能分為寫 HTML/CSS 和 JS 的,對於我所屬的團隊,我個人並不贊同這種做法,因為 HTML 和 JS 是密切相關的,這樣分工將不利於程式碼管理與優化,尤其是互動複雜的頁面,因為 JS 很依賴 HTML,拆分反而增加溝通成本,但或許在重運營的網站這麼做會更好。
程式碼管理方法
以下是一些零碎瞭解到的幾點:
- 內部所有人都能看到程式碼
- 據說在 09 年時某國家的 office 有例外(來自『In The Plex』第 6 章,內容比較不和諧,這裡就不展開了)
- 提交程式碼需要相關人員的 review
- 這使得某 G 內部工程師可以很方便地切換專案,很少一個人只負責一個專案
- 程式碼只有最新版(trunk),沒有 release 版本,沒有版本號
- 一般大家喜歡新增介面
- 如果要修改原有的介面,就必須通知所有使用方,不過因為所有人都能看到所有程式碼,所以很容易找到有誰使用
- 據瞭解某 F 也是這樣的
- 有個程式碼的搜尋引擎
- 估計和下線的 Code Search 比較像(好像還是某高管寫的,不過我忘記在哪看到的了)
- 如果想使用某個基礎庫,最好的方法是搜尋使用到這個庫的相關程式碼,抄過來
- 我認為這種方式比讓工程師寫文件靠譜多了,因為絕大部分呼叫這個庫的程式碼都是相似的,所以直接給出例子能讓別人更容易上手
- 程式碼依賴是通過全域性的方式統一管理的
- 我猜應該很類似 Chromium 中的 GYP,熟悉 node 的同學可以理解為 npm,不過是支援多語言的
- 之前在某 G 工作過的 iOS 工程師也在某篇後來刪除的文章中透露程式碼中不放 Xcode 專案檔案,而是由工具生成出來(話說這篇文章挺有價值的,可惜老外不喜歡轉帖,導致現在找不到了)
- 這種依賴管理方式讓人想起某 A 公司(賣書那個,不是賣水果的)內部完善的 SOA 機制,不過某 A 喜歡基於 service 來重用,而某 G 看起來喜歡程式碼級別的重用
- 很少使用第三方庫
- 只能選用內部維護的版本,比如類似這個 MySQL
- 會將第三方庫的編譯工具改成內部的,比如 Chromium 中都改成 GYP 方便管理
- 據說想申請用某個新第三方庫需要稽核,週期比較長
- 程式碼管理軟體用的 Perforce
- 某 G 直接將這個公司買下了(未確認,但下面那篇論文是在某 G 網站上的,所以我感覺訊息可靠),Perforce 的員工為某 G 內部定製了一套程式碼管理工具
- 另外我找到一篇 Perforce 的效能優化的論文,這裡面透露了很多 G 公司內部的程式碼情況(發表時間是 2011 年 5 月),以下資訊取自這篇論文:
- 這個程式用了 17 年,有 2 千萬次 changelist(可以理解就是 ci 數量)
- 最大的 client 有 6 百萬個檔案(應該絕大部分是資料吧,要知道 chromium 專案也就不到 30 萬個檔案)
- 文件及相關資料檔案也放上面
- Reivew 工具叫 Mondrian(確認就是 Rietveld 的前身)
整體來說某 G 的程式碼管理方式有很多可取之處,尤其是程式碼開放,能最大程度地調動開發人員的主動性與協作意識,從而創造出更大的價值。不過沒有版本管理這點是個雙刃劍,我也沒想好是否這樣會更好。
feature flag
因為沒有分支,程式碼只有一份,所以要實驗新功能就得通過 flag 來控制的,這個 flag 由線上 Borg 系統來管理,能做到針對某一部分的 Cookie 開啟不同的 feature,方便進行對比抽樣。
如果某個功能最終不上線,後續需要手工刪除相關程式碼。
這個 flag 開關功能在某 F 也有,我認為這是大型網站是必備功能,但需要注意,這個系統本身會成為關鍵節點,之前某 F 的類似系統掛過,直接導致整個網站大部分功能都關閉了,所以一旦出問題後果很嚴重。
嚴格的程式碼檢查
據說某 G 工程師大部分時間在寫單元測試,單元測試可以保證 UI 無關程式碼的質量,但對於頁面測試就很難了,雖然可以使用 selenium,但某 G 內部大家都不願意寫,我個人認為這個問題確實無解,頁面隨便一改就導致大量測試失效,我還沒見任何一家公司解決(某 F 說他們用的是 Watir,但主要用於保證登入等基本功能可用),目前看來唯一可行的就是自動頁面截圖 diff,某 G 在 Consumer Surveys 這個產品中也在嘗試。
據說某 G 的專案大多沒有嚴格的上線時間點,所以不能以專案緊急為藉口來不寫單元測試,這點和天朝不太一樣,大家更傾向犧牲質量來追求速度。
另外國外公司一般對瀏覽器相容性問題都不怎麼關注,因為現代瀏覽器中的相容性問題比以前好得多,這點某 G 和某 F 公司一樣,只支援高版本的 IE。
因為只有主幹,所以提交程式碼很謹慎,需要經過 3 個主要階段:
- 程式碼風格檢查
- 應該主要參考這個文件
- 非常嚴格,據說還會檢查命名什麼的
- 有段子說 Python 作者 Guido van Rossum 寫的 Python 程式碼無法通過檢查,所以一直沒提交。。。我認為這是假的,因為他老人家寫的 rietveld 還是挺符合某 G 規範的
- 單元測試檢查
- 程式碼 owner 的 Review
提交一旦出錯可能會導致影響其它人的工作(因為每個人都依賴主幹啊),甚至遭到其它國家 office 工程師的指責,所以大家對於程式碼提交都非常謹慎,再三確認,壓力不小。
在單元測試、程式碼風格和 review 的執行上,某 G 做得很徹底,這點值得學習,國內大家似乎更喜歡開發效率而不是質量。
前端如何開發
除了 Gmail、Maps、Plus 這樣的特例,基本上都是由後端模板生成頁面,很少專案使用 JS 來寫介面,更少使用 MVC 框架,這點其實在很多公司都差不多,比如某 B 也是一樣的,除了地圖及廣告管家等產品,其它產品基本上都是通過模板生成的。
某 G 的頁面是通常是由 Java 或 C++ 語言所寫的模版引擎生成的,而且開源出來了,分別是 Closure Templates 和 CTemplate,話說某 B 在幾年前也自己寫了個 C++ 的模板引擎,但目前基本被淘汰了。
對某 G 來說,「前端」工程師要寫 Java 和 JavaScript,而「後端」服務主要是 C++(某些地方開始使用 Go 了,比如這個)。
前面說到招人時都要會 Java,這帶來的結果是大多數團隊成員更瞭解 Java 而不是 JavaScript,於是在這種背景下很自然地誕生了 GWT 這個神奇的東西,它在內部很多地方使用,按照內部人士的說法,主要的考慮是:
- 能自動生成跨瀏覽器瀏覽器程式碼
- 結構規範,通過編譯器就能提前發現很多問題
- 能使用強大的 IDE 來提高效率(重構、自動完成、方便跳轉到定義等)
對於專業做前端的同學,看到 GWT 多半不喜歡,感覺就是多此一舉,但如果是 Java 出身的工程師其實是很容易接受的,尤其是對於習慣了 Java 的程式碼組織方式及強型別語言的人,反而會很不習慣 JavaScript 這種弱型別的語言,覺得太難控制太容易出錯了,比如拿到一個變數,在 Java 程式碼中通過它的型別就能知道它的資料結構,但 JavaScript 就不行了,只能在執行時 console.dir
出來或找相關實現的程式碼,從我個人體會來看,對於陌生程式碼,JavaScript 版本的理解難度要明顯高於 Java 版本。
話說某 G 曾經弄過一個叫 Wave 的產品,後來產品失敗後就將程式碼開源出來了,我認為這個程式碼能反應出 G 內部在使用 GWT 時的開發風格,我用 cloc 統計了一下它的程式碼情況,結果如下:
神奇吧,這麼複雜的前端互動應用,只有 1 個 56 行的 JS 檔案,而且其實這個 JS 還是無關緊要的,所以你可以理解為什麼某 G 只招懂 Java 或 C++ 的工程師了吧。
後來某 G 的 Lars Bak 大神推出了 Dart,在我看來就是用來取代 GWT 的,前面說到的 GWT 優點在 Dart 都有,而且在 I/O 2012 有一個演講題目是 Migrating Code from GWT to Dart,赤裸裸啊。
另外其實某 G 內部也不是所有人都喜歡 GWT,比如 Plus 就沒使用,而是直接基於 Closure 開發,並使用 Closure template。
說到 Closure,就不得不提它的起源:Gmail,在 WebApps 2010 會議上,有篇 PPT 介紹了 Gmail 程式碼的情況,以下摘抄其中幾個資訊:
- 2004 年就有 9400 行程式碼了,還有個 JS 編譯器(Closure compiler 的前身)來壓縮程式碼、檢查錯誤等
- 2005 年有 22000 行程式碼,10000 行註釋
- 2006 年有 52000 行程式碼,23000 行註釋,開始使用物件導向,並初步形成 Closure library
- 2007 年重寫,程式碼達到 90000 行,註釋居然有 97000 行(太厲害了。。。),開始出現模組化機制,而且出現了 Closure Templates
- 隨後開始內部使用,並最終對外推出了 Closure 系列工具
- 演講人認為
Type-checking is important and possible
- 有報導說在這個會議中演講人還透露 Gmail service 也是用 JS 寫的,程式碼有 443000 行
- 對於這個訊息,我不確定是否真實,但確實是有可能,08 年時 Stevey Yegge 也說過某 G 的規範有漏洞,沒說 JS 只能用在前端,而且他還真這麼做過。
最後插一句我的觀點:對於我所處的團隊及使用者類產品來說,GWT 沒有意義,而 Dart 雖然比起 GWT 要好得多,但和 JS 互動實在太麻煩,導致它的使用場景很有限,語法有明顯變化,使得難以讓大部分前端工程師接受(Lars Bak 就在 I/O 2013 上吐槽工程師太糾結語法,看起來大神在內部推廣時肯定遇到不少阻力)。對於型別檢查的好處,我個人是比較贊同的,因此我更喜歡 TypeScript 這種增強形方案,因為它可以逐步適應,既有型別支援的優勢,又能直接使用現有程式碼。
內部工作流程
以下是打聽到的某個產品專案開發流程:
- PM 或工程師提出某個想法,UX 做原型 mock
- PM 申請專案稽核(通過率不高)、工程師開發 UX 無關部分
- 工程師完成開發
- 線上小流量實驗
- PM/工程師分析實驗結果,完成報告,申請全量上線(通過率不高)
- 通過資料來證明這個功能是有價值的
- 需要解釋這些資料的變化原因
- 分批逐步上線,這個過程中還會有很多稽核
- 最終確定是否要上線(通過率不高)
一般整個專案週期很長(至少一季度),專案最終上線時間點無法確定,大部分的專案最終都無法正式上線。
最大的特點是完全靠資料說話,而不是由某個 PM 決定一切,以前有個視覺設計師在離開 G 後就在部落格上吐槽這點,他認為這會導致無法進行大膽的介面改版。
這個流程和我們搜尋產品幾年前的開發流程很類似,對於成熟的搜尋引擎來說這麼做確實有它的道理,畢竟隨便改個什麼都很可能影響收入,當然要十分謹慎了,但這種開發方式並不適合面向使用者類的產品,如社群、遊戲等,因為開發週期太長了,很容易錯過時機。
某人一天的工作
這裡拿 Matt Welsh 來舉例介紹一個工程師在某 G 一天的工作,雖然他不是做前端開發的,但他目前在 Chrome 團隊負責移動 Web 效能優化,所以還是比較相關,而且最重要的是他比較喜歡寫部落格,有意無意地透露了很多資訊,所以我比較好公開談。
另外話說他之前還是哈佛的計算機終身教授(!!!),八卦很多,比如差點說服小扎同學不要搞什麼社交網路了,還是好好學習拿畢業證照。。。
這這篇文章裡,Matt Welsh 介紹他的一天是如何度過的(另外還提到了在哈佛當教授是如何度過的,也很有意思),我摘抄如下:
- 9:00,到公司,查郵件
- 9:30-10:15,寫程式碼,增加功能,編寫單元測試,發起 changelist 程式碼 review,喝無糖可樂
- 10:15-11:00,切換 git 分支到其它專案,檢視同事 review 程式碼的結果,回覆評論併發新版本 changelist
- 10:00-11:30,再次切換 git 分支,提交一個要執行 3 小時的 MapReduce 任務分析網路延遲日誌
- 11:30-12:00,和山景城的團隊成員開視訊會議
- 12:00-12:35,午餐
- 12:35-14:00,檢查郵件,檢查 MapReduce 任務執行狀態,回覆程式碼 review 的評論,再次提交程式碼,然後檢視任務列表確定接下來的工作
- 14:00-15:00,和在劍橋(有評論指出這裡是波士頓的劍橋,不是英國那個)、山景城等多個地區的團隊成員開專案會議
- 15:00-16:00,喝紅牛,這時 MapReduce 任務已經跑完了,生成圖表,分析資料中不符合預期的部分,整理程式碼,準備下一次 MapReduce
- 16:00-17:00,喝蘇格蘭威士忌(scotch)並玩吉他英雄(Guitar Hero)
- 17:00,收拾筆記本回家
看完後我的幾點體會是:
- 前面提到的程式碼只有 trunk 並不準確,當然每個部門確實可能不一樣
- 程式碼 review 做得很認真
- 看起來任務很明確,所以雖然工作時間是 9-5,但效率挺高,這點我最為好奇的,怎麼做到將工作安排這麼具體?
- 除了寫程式碼,分析資料也是每天的重要工作,具體分析什麼可以通過他的論文了解,看得出來是很細緻的
內部工具
2008 年前的內部工具情況可以通過這篇文章和這個 PPT 瞭解,不過之後就不清楚了,看起來很多外部工具都有內部版本(Docs、Mail、Talk、Calendar 等)。
這裡說一下 Chromium 專案中我看到的工具使用情況:
- 網站是基於 Sites 搭建的
- 設計文件喜歡使用 Docs,因為可以線上編輯和評論功能,所以多人協作會很方便
- https://codereview.chromium.org/
- 在 Groups 中進行討論
- 使用 code 來管理 issues
- Buildbots 來進行編譯和整合測試
- 搭建了各種檢測工具來保證質量,具體細節推薦讀這本書,看完這本書我最大的體會是沒什麼神奇的東西,完全靠細心的工作
可用的石頭
以下是我認為可以借鑑的地方:
- 原始碼不分版本,對內部所有人公開
- 在 FEX 內部已經是這樣了,但我們應該推動更廣泛的開放與共享
- 嚴格的程式碼規範及單元測試機制
- FEX 所有專案將接入 Travis CI
- 程式碼規範及單元測試的強制檢查
- 程式碼 owner 的 review 機制
- 通過實際例子來使用,甚至不用看文件
- 加強對 demo 及 example 的要求,不能是簡單的 hello,而最好是從產品線實際使用案例中抽取出來
- 文件及相關資料和程式碼放一起
- 這能保證找起來很方便
- 如果由於種種原因不能放一起,至少也要放連結
- 外部產品有內網版,比如 Docs
- 典型的 Eating your own dog food
- 在內網提前測試外部產品的新功能,而且一般內部人員都會很積極地吐槽,對產品改進很有幫助
- GWT 的靜態檢查機制
- 整理這篇文章時我發現 TypeScript 也已經接近 1.0 版本了,看起來時機快成熟了,後續計劃嘗試 TypeScript