我先做一下自我介绍,我是 07 年加入的 Google,在 Moutain View 总部任Google SRE,今年年初回国加入 Coding。
在 Google 我参与了两个 Project, 第一个就是 Youtube, 其中包括 Video transcoding, streaming 等,Google 的量很大,每个月会有 1PB 级别的存储量,存储,转码后,我们还做 Golbal 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 就是终极考验,来考验你这个研发体系能不能做到这一点,如果能做到这一点你才是好的研发体系。
这就是我和大家分享的三点,人,技术和流程,我认为这在一个公司里是一个齿轮一样组合起来的,公司我们可以说是一个大机器,如果将机器润滑,将齿轮转得更好,结合得更紧密,这就是我们想追求的目标。谢谢大家。