人,技術與流程
我先做一下自我介紹,我是 2007 年加入的 Google,在 Moutain View 總部任 Google SRE,今年年初回國加入 Coding。
在 Google 我參與了兩個 Project,第一個就是 Youtube,其中包括 Video transcoding, streaming 等,Google 的量很大,每個月會有 1PB 級別的存儲量,存儲,轉碼后,我們還做 Gobal CDN ,最大的時候峰值達到 10 TB,我們在全球 10 萬個節點,每臺機器都是 24 核跑滿狀態。然后我從 Youtube 團隊離開加入 Google Cloud Platform Team。我們做的主要工作是管理 Google 全球的機器,大概有 100 萬臺左右。我離開 Google 之前做的就是 Omega Project,一個集群管理系統,管理 Google 整個云平臺的任務調度,協作。可能很多人會說“然并卵”,因為這是都是國內不存在的網站。(笑)
從 Google 回來在 Coding 做 CTO,對我來說也是人生的一個大改變。最近我在知乎看到一個好問題,“從大公司離開到小公司當 CTO 是怎樣的體驗”,我摘抄了一個好答案:“ 頂著 CTO 的名號,招聘,培訓,鼓勵程序猿,拉網線,查機房,裝系統這都是 CTO 要做的事;討論方案,推方案,定方案,確定進度,拖延進度,安撫程序猿,挨老板罵,安撫老板,這也是 CTO 的職務。” 不包括 Coding,而我的工作還包括 Coding,很傷心。(笑)
所以我把這個問題作了歸類:
CTO 是什么:
第一、他在公司里是一個鼓勵師(我這個形象做鼓勵師其實也不是很合適)。
第二、可能是網管,這個我確實干過,接網線,架服務器。
第三、可能就是受氣包,程序員對我很不滿,老板對我也很不滿。
那我們把這三個角色做為一個對應:
第一、就是研發人員的管理,包括怎么招人合適,怎樣讓這些人更好地協作。
第二、是研發技術和研發環境的管理,就是你有了這些人怎樣讓大家更好地協作在一起,更高效地開發。
第三、就是研發流程的管理,怎樣讓公司這臺機器轉得更加靈活,順暢。
那這里我提出了三點要素, 人,技術與流程,這是一個公司研發體系中不可缺少的關鍵要素。
那我們就先來說一說人,創業公司都需要全棧工程師。這個全棧工程師長的什么樣子呢?一般來說他就像是一個大俠,踩著白馬,手持金槍,什么問題都能解決,一人單挑好幾百人。可是有些時候我們招不到全棧工程師,我們就招了一些全干工程師,這也是我們最近發明的一個詞匯。這個人就是他什么都干,寫完網站后寫 iOS 最后寫安卓。這樣的人也很難找, 更難的是把這些招來的人員合理地安排在一起,合理地組織起來協作。
要想解釋 Coding 在研發人員管理上的演變,我首先講一下 Coding 的服務架構,這是我們去年 5 月份(第一版本上線) 的服務架構,非常簡單只有一個 core 程序,經過一年多的演變,現在變成了這個樣子。我特意寫的字很小,因為不想讓大家看清楚,這里面其實錯綜復雜也不一定對。一個簡單的架構是如何一步步演變復雜的呢? 這里我想先介紹一個康威定律,就是
“任何一個設計系統的組織,最終產生的設計都等同于組織之內、之間的溝通結構”。
回想起 Coding 以前的交流方式,老板說“我們要做一個新的功能”,大家就開始把功能拆,前端需要怎么改,要改誰來做,后端要不要改,改的話誰來,服務層,DB 層,測試,部署,每個人做的事情都不一樣,每個人都只做一件事情,每個人都做流水線上的一個螺絲釘。這樣會造成什么情況呢?前端程序員在等后端程序員,什么時候把接口接上了再開工,后端程序員在等數據庫,什么時候加上這個字段我再開工,測試和部署就更在等了。每次一開項目進度會議時,老板問功能做的什么程度了,前端說后端沒寫好接口,后端說數據庫還沒搞定,經常是這個狀態。
那么 Coding 今天采用了一個組織方式,我們叫它全員全棧,這個全棧的意思和普通意義上的全棧意義不是很一樣,這個全棧我們指的是產品,功能上的全棧。具體是怎么實現的呢?其實對任何一個比較合格的程序員來說,語言并不是他的瓶頸,也不應該作為他的限制條件。我們更希望的是我們公司里更多的是這種全棧工程師。我們在做任何功能的時候,我都會和這個人說你要負責這個功能從頭到尾的實現,你需要改前端就改前端,需要改后端就改后端。做這個事情可能剛開始有一定難度,后端工程師對前端不是很熟悉,前端工程師對后端不是很熟悉,那我們就用一些其他方法去克服它。這個過程 強迫了組織內部的 Knowledge sharing 。
很多大公司的工程師可能只關心這一小塊,比如說前端工程師只寫了這一點前端的東西,后端我一概不管,原因是我不敢動,大家也不讓我動。 在小公司里面,每一個人都要了解公司的使用的各種技術,我們共同努力去降低系統的復雜度。 全員全棧是我們公司的一個戰略方向,我們希望每個人都有主人翁的意識,在做需求的時候他可以從前到后一直跟到完,但是想要把全員全棧做好就要做研發環境和研發工具的調整,接下來我們講我們是如何通過技術手段來協助的。
技術手段在一個研發環境里有三個要素:
第一個就是代碼如何管理,如何操作;
第二個就是運行, 前端后端代碼運行起來要求繁雜,如何調節。
第三點就是你寫好之后怎么部署、運維。 開發環境和生產環境經常打不通。
為了代碼這方面講好,我先來給大家介紹下在 Google 當碼農是一種怎樣的體驗。Google 最強大的就是他的研發體系經過了 10 多年的積累和錘煉,現在是一套非常高效的體系。那這個“不存在”的公司是怎樣管理自己的代碼呢?
第一點, 整個公司只有一個倉庫,一個版本控制系統。這個東西說起來簡單,其實很難實現的,很多人覺得這個東西好像可行,但他細想難道我們用的都是SVN?很明顯不是,那 SVN 沒有用,Git 也沒用,那如何做到只有一個倉庫呢?只有一個倉庫帶來的問題就是所有程序代碼都在一個目錄下,如果你有一個特別大的硬盤,你把整個公司全都 down 下來,他就是一個目錄下的很多子目錄,可能幾十級子目錄,可能整個公司代碼有幾百G。那如何同步,管理,操作,這個都是 Google 的獨門秘方。
這個有什么好處呢?
第一, 你可以看到任何人的代碼,你可以看到這個服務是如何實現的,為何會出現異常,會有什么錯誤。
第二,它帶來一種更高效的復用方式,比如我寫了一個小程序,我 Gmail 的一個程序用了一段 bigtable 的代碼,那我直接在代碼里引用過去即可,反正大家都在一個目錄下。編譯環境也一樣,有了這個 repo 之后,還有一套編譯系統,這個編譯系統能做到一鍵編譯任何程序。我運行一個 Gmail 也是一行程序,一個命令是就是編譯程序 build 加上 Gmail 的路徑即可編譯。編譯 Go,Python,C++全都是一個命令,不用你去關心底層是如何實現的,運行起來即可。有了這個之后就做到了部署,開發很容易,就是說如果我寫業務代碼需要用到一個服務,那我可以很容易地啟動這個服務,還可以對該服務做小改動后再次運行起來,這都很容易。
這里說多了大家又會說: 道理我們都懂,但是如何實現呢?
那 Coding 怎么組織我們的代碼呢?因為我們確實沒有 Google 集中式的代碼服務,為了調整我們的代碼結構,讓大家更好地復用,引用彼此的代碼,又因為我們本身提供 Git 服務,那我們也是用了 Git 倉庫的方式,但我們每個項目都是一個獨立的倉庫,比如我們前端代碼放一個倉庫里,后端代碼放一個倉庫了,各個服務都放在不同倉庫里。
這種方式就產生一個問題, 如何同步?
我們用的是 Google 開源的東西叫 Android repo,大家做安卓開發時可能用到過,這個 repo 是什么意思呢?就是他定義了一個 Workspace,這個 Workspace 有一個固定的格式/結構,然后你統一用 repo 這個工具去 sync,這個 repo 應該到哪個 commit ,那個 repo 應該到哪個 commit, 全公司都用同樣一種 Workspace 的方式,就保證了每個人看到的代碼都是一致的。
做了這個代碼結構定義的好處就是:
第一,我們有代碼閱讀功能,比如我們 Coding 的代碼閱讀可以根據這個 Workspace,可以看到每個項目的代碼,互相引用的狀態;
第二就是質量分析,你可以針對這個 Workspace 做質量分析。這個 Workspace 有一個 repo.sh,這個是一個命令,比較關鍵的是有一個 default.xml, 是這個命令的配置文件,然后大家打開 Workspace 運行 repo sync 即可,它會自動更新每個組件到最新版本。我們也鄙視 xml,但是沒辦法,這個程序就是這么寫的。其實很簡單,里面主要定義了很多 Project,這個 Project 可能是代碼倉庫的路徑映射到了本地的路徑,它有一個比較先進的功能就是它可以同時 sync 好幾個 Project,就是你一敲 repo.sh sync -g=4 就是開 4 個線程去同步。實踐起來這個是很容易的,有了這個東西后打開了這個局面,為大家下一步搞開發環境做了基礎鋪墊。
工具有了, 那我們真正想要的開發環境是什么呢?
我從 Google 得來的感觸就是我們想要一個統一的,代碼化的,可復制的,可重現的一個開發環境。
第一、每當新同事來公司后,他帶著自己的電腦或用公司的電腦,他所用的工具是不一樣的,環境是不一樣的,怎樣能讓公司的代碼在自己機器上跑起來其實是一件比較困難的事情。大家的解決辦法可能是通過寫文檔,但這是一件非常痛苦,浪費時間的事情。
第二就是說如果你這個開發環境如果不可以復制,重現,那你的自動化測試怎么做?不能說某個人手動配一下,跑一下,今天掛明天又改吧?
那如何做到統一化的開發環境?我們做法就是 定義了一個通用的接口,我認為這個編譯系統的內部實現是無所謂的,怎樣都可以,但它的接口比實現要重要一百倍。大家可以想下如果你每個程序,每個組件都是用同一個方法去 Build,去運行,這是一種怎樣的體驗?我們定義了一個 build.sh 和 package.sh ,這個 build 就是說我用 Java,Python,Ruby也好,我就定義 build.sh,它最后能產生一個結果能把這個東西 build 好。對我來說我不關心下面的程序怎么來寫,但我只要 build 它,我改了一行代碼它能夠 build 出新的東西即可。這是我們現在比較土的做法。
Google 最近開源了 bazel ,它是用 Java 寫的編譯工具,它實際上就是 build 命令,之后兩個反斜杠代表整個 Workspace 的根,然后 coding 是一個 project,server 是 project 的 target。有了這個東西之后其實你 build 任何項目你都考慮的是說它邏輯上的分層而不是物理上的分層,邏輯上的分層就是我要 build 一個 coding server,那這個 coding server 可能里面引用了其他的第三方庫,頭文件,Ruby 程序,Java 程序都無所謂,我只要說我能 build 一個 coding server 來即可,那這個 bazel 更好的就是它可以自動處理遞歸依賴,就是你這個 rule 可以依賴到另外一個 rule。
有了這個編譯后我們還需要有可復制,可重用的開發環境,那這個開發環境我們怎么做的呢?
我們用的是 Vagrant 和 docker。Vagrant 是 VM 的一個管理工具,它可以生成一個新的 VM 來,這個 VM 使用代碼來定義的, 然后把每一個服務作為一個 docker 服務在 VM 來運行的。
Vagrant 其實做了三件事:
第一件事是它從一個指定的地方下載 Base Box,Base Box 是我們自己做的,比如說是一個 Ubuntu 鏡像加一些本地依賴.
第二就是它支持腳本定義,你可以運行 shell 腳本來定制,再選一個所謂的 Provider,這個 Provider 就是你可以比如說本地是 Virtual Box 的 Provider,遠端和很多云廠商可以對接。
進行完這兩個操作之后,他就產生了一個 VM,這個 VM 就是一個你可以一鍵 ssh 進去,它自動把你所有東西都搭配好,這是一個屬于你的開發環境。那有了這個東西之后,整個公司可以有一套一致的開發環境,因為是一個 VM,它在哪臺機子上運行的方式都一樣,所有的依賴庫都可以放進去,所以最后的結果就是我們有一個幾G的鏡像放在我們內網里。每個新同事來了后,我們讓他裝一個 Vagrant 或 Virtual Box,然后他敲一個命令,自動把鏡像下載,啟動,然后一鍵把整個 Coding 的項目在他的機器上跑起來。
做到了這種可重用的開發環境,我們還做到了所謂的一鍵運行。一鍵運行是和開發另外一個層面的東西,它既不關心這個東西是怎么構建出來的,我只關心我啟動這個服務,比如 Coding 開發環境的時候我需要哪些服務,那我們采用的就是一個自己寫的腳本來給 Docker 做編排,這個配置文件里定義了一些 Job,每個 Job 有一些 image(運行程序的版本、環境變量等)。其實很多時候我們都是用 go run 命令一鍵啟動很多服務和鏡像,達到了我們剛才說到的統一的代碼化的,可復制,可重現的開發環境。
技術工具是我最懷念 Google 的地方,因為這些工具帶來很多好處: 首先他鼓勵公司內部的協作, 每個人寫程序時不是首先想到自己搞一個小東西出來,我們更多的時候是去看公司里面其他人做了一個什么東西,它是怎么實現的,能不能引用過來,能不能使用,能不能把常用的類庫都抽離出來;
第二它可以讓新人很快上手,我們新同事也好,老同事也好,更多的場景是老同事換 Project,以前可能只是使用者,瞬間就可以變成開發者。開發環境的無縫切換無形中降低了很多公司運行的成本。
第三點就是它讓自動化變為可能。剛才我們是裝了 Vagrant 執行一個命令就搞定了,這個東西同樣可以作為自動化的東西來實現,比如每次我們在發 Code Review 時,我們后臺就可以自動啟動一個新的 VM,把所有東西下載后運行測試,最后給出改動對錯,造成影響等結果, 這都是可以自動化的。這點我覺得是一個關鍵點,接下來有了這個環境之后你有了質量分析,工具后它才能夠進行,才能有更多生產力工具接入進來。
最后我再講一講流程的管理。
作為一個 CTO,我們小小的夢想是持續交付更好的軟件。老板說你作為一頭老黃牛就得不停地往前跑,你也能夠按時完成任務。
公司內部有很多實現方式:第一就是 Code Review, 這個流程工具毀譽參半:
第一點就是這個 Code Review 絕對是腦抽檢測器,每個人都有腦抽的時候,評審期就是緩沖期,讓你想一想你真的是要做這件事情嗎?你讓別人幫你檢查一下是很有好處的。
第二是Code Review 是一種很好的知識分享的方式。我一個人在寫這個功能時我把代碼交給別人去看,那另一個人就會對這個有了解,將來他可能來做你的功能,你換過去做他的工作。鼓勵公司內部的知識共享是很重要的。
第三它是一個 idea generation,大家在做知識共享時很容易發現自己另外一個地方也發現了這個問題,我是這么解決的,你為什么不這么解決?有什么更好的辦法解決嗎?Code Review 為促進交流來做這件事。
那 Code Review 做的不好時有哪些方面呢:
第一就是程序員鄙視鏈,就是老程序員對新程序員很鄙視,說你寫的東西太次了,我都懶的看。這個是很不好的一種行為,我們一定要避免這種東西。我們做了一些程序上的要求,比如你每次 Review 時必須把整篇文檔都看完,不能說我看了一行發現太爛了不看了,你改好了我再來看,這是不允許的。
第二我們講到 Ownership ,就是你在 Code Review 時這個代碼屬于誰的,我們講代碼評審是誰寫代碼誰負責,你寫了 rm -rf,你就要對此負責,我看了之后覺得你寫的有道理,但我沒看出 bug,,這是你的問題不是評審者的問題。這是我們在Google,Coding 實踐出來的經驗,
第三點就是擁抱變化,就是我原來可能某員工寫了一個程序,他覺得我寫的這個程序實在是太好了,你們不要改,一改我就看不懂了。所以他對任何改動都非常抵觸,這也是錯誤的,不論在 Google 還是 Coding ,我們堅持的一大原則就是說我們有 Business case,你就可以改代碼。你只能去把這個代碼寫得更好,不能不允許他改,這個是關鍵,你所寫的代碼都是屬于公司的,那公司怎么能用代碼來做更好的事情呢,你要講清你的道理,你為什么要改這段代碼,改了有什么好處, 把這個東西變成了技術上的討論。而不是一個職責,權限上的討論。
所以我覺得這幾點是把 Code Review 做的更好比較關鍵的幾點。接下來說一下 Release Schedule 的問題。
因為創業公司和大公司不一樣,每個公司都有自己的 Release Schedule,以前我們基本靠吼,今天我們要上線,所以全員就上線,我們今天要 Release 那就 Release,你如果和老板吼今天上不了線,老板想了想那就算了吧,明天上,并不是很嚴肅。我們在內部推行的改革是我們要把沖刺變成長跑,我們已經創業一年多了,不能總是沖刺狀態了。 我們要變成長跑,能夠持續不斷地交付高質量的軟件,而不是每天上完線后加班到后半夜才把它改好。
方式很簡單,就是一周釘死 Release 兩次,我說服老板每周兩次就夠了,三次就多了。Google 一個月才 Release 一次,大項目半年才 Release 一次,我們創業公司能做到這點已經很好了。一周 Release 兩次是什么概念,星期一就要上 Staging 測試環境,星期二就要 Release,星期三又要上 Staging,星期四又要 Release,留給大家周五寫寫程序,修修 Bug。其實一周 Release 兩次也很頻繁,但是保證現階段我們的轉化率,我們采用了這個方式。把這個東西攤在桌面上來講有什么好處呢,他讓大家可以計劃我這個功能下周二上還是下周四上,你和項目經理打仗的時候有理有據了,他說你看你是星期二上還是星期四上,程序員說星期二可能搞不定,那我們星期四來吧,一來二去就把這個沖刺改成了長跑。
接下來還有一點,我們要 區分 Feture Team 和 Infrastructure Team, Infrastructure Team 也是我們以前谷歌的一個名詞。他講的是什么意思呢?就是說雖然全員都在做業務邏輯,但我們一定要抽出一定時間來推行技術演進,你不能說每天我上來就是寫代碼,拷貝粘貼,這樣搞得大家所有東西都非常混亂。我們在內部提出了三個點:
第一叫 Coding One,就是我們公司內部所有的項目,所有的技術,所有的服務都爭取用同一套方式來運行。比如說用同一個 Java 版本。這就是一件很難的事情,我覺得好多公司都做不到,用同一個第三方類庫,這也很難,所以用起來類庫都不一樣,但干起來事情是一樣的。這樣每個人代碼看起來都好像是差不多,但是又不一樣,所以 Coding One 就是解決這個問題,就是 Java 版本,第三方庫的版本,第三方庫的類型,編譯方式,運行環境,啟動方式,都應該是一樣的。這樣會降低很多程序員之間內耗,提高大家的效率。
第二就是 Coding Two,剛創業的時候大家覺得我們都用一臺機器,所有東西都跑在上面,這個東西如果壞了那就全掛了。每次更新大家都覺得太危險了,我們還是后半夜進行吧,十二點還不夠半夜,我們三四點鐘進行吧。因為你這個東西沒有備份,沒有任何的灰度。我們叫 Coding Two,以前是從零到一的過程,現在要把從一到二的過程邁過去。從一到二的意思是你這個東西可以是多份的,你這個東西掛了那邊還可以繼續頂上,這個程序你做發布的時候你可以先發布給自己用,用好了再發給用戶,這才是負責任的方式。
第三點: Coding CI 是我要講到的最后一點,最后我們的終極目標,就是所謂的 Push On Green ,意思就是你提交了代碼一分鐘之內只要所有的測試都跑動過,馬上就上生產環境,大家可以想一想在自己公司里能做到這一點嗎?如果不能,那是為什么?你程序員寫了代碼,我們認為程序員寫的代碼都是善意的,你經過了 Code Review,業務流程上都沒問題,他為什么不能直接去上生產環境呢?Push On Green 就是終極考驗,來考驗你這個研發體系能不能做到這一點,如果能做到這一點你才是好的研發體系。
這就是我和大家分享的三點,人,技術和流程,我認為這在一個公司里是一個齒輪一樣組合起來的,公司我們可以說是一個大機器,如果將機器潤滑,將齒輪轉得更好,結合得更緊密,這就是我們想追求的目標。謝謝大家。
觀看視頻可點擊這里