聊聊Google的工程实践(一)

共 4630字,需浏览 10分钟

 ·

2020-10-28 12:58

也许你是被标题吸引进来的,为避免误解,这里需要事先声明下,本人从来没有在谷歌实习或者工作过,因此,张小龙的那句话“我所说的,都是错的”倒是很适合本文。就这个角度而言,标题叫做「从Googler学到的工程实践」可能比较恰当。

先说一下我的工作经历,我实习的第一家公司叫云壤,云壤有很多的ex-Googler,创始人是谷歌中国工程院副院长,他搭建的早期团队,基本都是谷歌工程师。在这家公司工作了两年的时间。目前所在的公司和谷歌的关系也比较特殊,不仅CEO和CTO都是谷歌出来的,而且在C轮还拿到了谷歌的投资。前段时间写过《How Google Works》的施密特,还到我们参观了一趟。目前我在这家公司呆了大概五年半时间,多少也对谷歌的一些文化有了更多的了解。

除此之外,最近两年详细阅读了不少jeff dean 和 sanjay ghemawat等谷歌大牛的论文,也算是加深了对谷歌工程文化的理解。

之所以想聊聊这个话题,是因为这几天我们一个同事翻译了《Software Engineering at Google》这份文档。翻译文章是《Google软件工程之道》,有兴趣的可以阅读下。下面围绕这份文档的话题,聊聊我这几年的一些理解。

代码仓库

谷歌基本使用单一代码仓库,大家基于master进行开发。我呆的第一家公司也是这么干的。当时我们基于SVN来做代码管理,我们在master branch上创建了一个experiment目录,每个人在上面创建一个自己的文件夹,可以把未经review的代码提交到这个文件夹。

单一的一个仓库当然会有很多问题,比如代码庞大,新人checkout 代码需要花费不少时间,编译缓慢等。谷歌使用的是分布式编译系统,使用几百甚至上千台服务器,这对绝大部分公司来说都是巨大的成本,非常奢侈。我们当时使用的也是分布式编译,不过主要还是借助每个人的工作电脑。

当时云壤有一个目标是让每台电脑都可以登陆任何人的账号,随便换台电脑都可以开始写代码。当时使用的是LDAP + NFS,不过早期发生了很多次故障,导致全公司的人都没法干活,只能开始聊聊天。当时机器故障几乎是成为除了TGIF外,最佳的促进团队沟通的时间。

单一的代码仓库也会有很多问题,比如权限的管理。你的代码大家都能看到,这会引入很多的管理成本。比如,有一些代码库,你希望大家能够access最新版本,但是又不想让代码被看到,怎么办?当时我们的解决方式是,引入 public/internal 两个不同的目录,结合定制化的SVN 插件和jenkins job来实现。public 目录放对外可见的接口,internal 放内部实现。如果你没有权限,编译的时候会依赖上提交上来的pre-build 的静态库。

Git 起来后,使用单一的repo应该比较少了,想要强力推动也有很多的顾虑。毕竟,一套应对86T代码的内部系统,不是任何公司都有能力去开发的。

在若干年前,百度的基础架构部在推类似的事情,不知道后来是否执行了。不过听说百度内部的类gRPC 框架有好几套,并且没看出来哪一个有一统百度之势。这不,后来谷歌自己的gRPC 都开源了。想来在百度要学习谷歌的基础架构,并不简单。

构建系统

怎么让编译变得简单应该是关注工程师生活质量最重要的事情。工程师的生命,除了耗费在重新造轮子上外,浪费在编译上的大好年华应该也数不胜数。当你写好代码,等待结果输出的时候,是最焦灼的,这仿佛一个剑客刺出去一剑,等了半天不知道是否刺中了对方一般。

云壤成立那会,当时还没有开源的Google bazel,所以云壤的同事开发了一套简化版本的构建系统,内部名字叫做ymake,支持的语法基本和 bazel 差不多,不过功能稍微少一些,比如还不能支持依赖关系查询等。刚到云壤的我,觉得这工具真牛逼,相见恨晚。在这之前,我负责一个Windows项目,因为项目依赖的开源项目有点多,我花了很多的精力在整理编译的配置。而有了类似Google构建工具的ymake,工程师工作中很少需要去关注怎么编译程序,你只需要写代码就行了。

我猜想,在Google的bazel 开放出来之前,中国互联网上至少出现过十个本土化的模仿者。其中一个叫做blade, 是腾讯开发的,后来还开源了,使用python实现。我当时还有幸参与了一部分。

那么构建系统解决什么问题呢?我认为有以下几个:

  1. 让各个语言有统一的构建语法。不过你使用C++还是java,都可以使用bazel build xxx, bazel test xxx 来进行编译或者测试。

  2. 简化编译需要编写的内容。使用make 工具的话,往往makefile 不忍卒读,并且比较凌乱,结构化很差。而bazel 的话就很简单,你只需要写一个简单的BUILD 文件就行了,你需要关注的是编译类型(静态库,动态库,二进制文件,测试文件等),文件列表,依赖关系等。

  3. 简化依赖关系。比如让冗余的头文件无处藏身,尝试检测冗余的依赖等。一些实现文件,没必要对外暴露,这样有利于最小化代码重构带来的影响。

  4. 在构建工具层面,引入C/C++语言本身缺失的现代编程理念。比如包和可见性。

  5. 引入全局的配置信息。比如优化选项,编译警告级别等。

  6. 方便引入后处理流程。比如引入单元测试运行,如果失败就拒绝合并。比如在执行单元测试的同时引入内存泄露检测等。

  7. 最大化基础库的使用。通过简单的依赖就可以引用大量基础代码,当然开发效率会提到很大的提升。

当然,构建系统除了节省你编写编译规则的时间外,非常重要的一点就是节省你编译的时间。谷歌内部解决这个构建问题是好几个系统,包括Blaze, Forge, SrcFS, ObjFS等。
感兴趣的同学可以阅读以下文章,这个博客还有很多谷歌基础架构的干货:
https://mike-bland.com/2012/10/01/tools.html

云壤当时开发人员还不算太多,使用的方案是 distcc + ccache。编译速度也还过得去。

代码审查

有关code review,之前写过一篇文章《代码审查(Code Review)之道》,没读过的可以读读。

这里说一下这两年的新感悟。面试过几个总监候选人,也大概知道code review,但是往往是最原始的review方式,开会的时候,拉着一堆人对着代码进行review。这样做有一个问题:
1,code review很随意,无法强制执行。开会也浪费很多人的时间。
2,comment不够便捷。
3,缺少一来一回的交互。拉个会,估计大部分人都不敢说话了,大家提出一些问题,只能乖乖认错,技不如人,心理感受也差。
4,开会review无法形成积累,不方便新人从以往review的comment中学习经验。
5,无法结合工具做来必要的前置检查,比如代码风格检查,内存泄露检查等。

也有不少候选人认为,做code review固然有价值,但是项目忙的时候,交付最重要,从而认为code review在更重要的交付面前,是必要的牺牲品。

作为一个Leader, 说服老板认为到code review的重要性,是他的基本工作。而想办法解决code review的可行性和推行成本,也是对leader工程经验的考验。

当然,能做code review的解决方案很多,github 也行,gitlab 也可以,review board 也挺好的,gerrit 也不赖。说一下code review工具需要解决的几个问题:
1,发起一个code review,需要发送必要的通知邮件。内容可以是标题,更新涉及的文件列表等。
2,可以和持续集成工具结合,比如jenkins。解决掉代码风格自动检查,编译警告检查,确保能编译成功,单元测试成功,甚至确保单元测试没有内存泄露等。如果自动化工具无法检查出来问题,自动加一个verified状态,否则拒绝合并,并通知发起人,这样不仅可以节省reviewer的时间,也可以一定程度保证代码的质量。
3,方便对diff,最好是一左一右对比。
4,有简单的issue概览和状态显示。方便别人做决策是否能LGTM。
5,可以方便地合并代码。

再聊聊应该花多少时间在code review上。我认为一个合格的leader,应该花费10%以上的时间在code review上。不过最好控制在30%以内。我目前公司的CTO,到今天为止,也仍然花一定的时间在code review上。对leader而言,花一定的时间做代码审查,可以保持对代码的敏感性,否则没过几年,可能C++的新标准和STL的新功能就完全陌生了。此外,做code review,也是重要的新人培养工作。做code review,也能保证对核心系统的细节有更深的把控,否则讨论技术解决方案或者是架构的时候,就容易落入空对空。

遗憾的是,随着负责事情的增多,往往技术管理者很难抽出足够的时间编写代码,甚至是做代码审查。不过即使如此,也应该让整体团队保持对代码审查这件事情的重视,不能因为自己没时间了,而导致整个事情放松。应该是代码审查成为工程文化,成为工程师的日常。学生时代,作业需要老师批改,学生在批注中学习。成年了,作家在自我review中保持对文字的精益求精,而程序员则在相互的review中成长。reviewer往往是leader,当然也可以是同级,甚至可以是自己的下属。reviewer的级别不是很重要,能不能对你提交的代码提出有建设性意见才是最重要的。

说过code review, 其中最基本的环节就是代码风格。我这几年一直使用的是Google的编程风格,地址如下:https://google.github.io/styleguide/cppguide.html
Google的风格指南,每隔一段时间就会有所变化,因此我们也偶尔会重读一遍,多读几遍,往往也会常读常新。一般随着C++标准的更新,Google的C++风格指南也会有所更新。有趣的是,我在面试中碰到不少技术人,说他们有比较丰富的code review经验,但是当我问及他们,一般code style涉及到哪些环节,他们一般只能想到可怜的三四点,不成系统。我认识一位编程经验很丰富的朋友,他一般会定时阅读下Google C++编程风格的新版本,并且常将C++基础库的设计和其他语言的基础库做对比,不同语言相互参照,以获得更深入的理解,他的这个习惯非常值得大家学习。

有关代码风格的话题,《代码大全》这本书也有不少篇幅谈及,有兴趣的朋友可以找来读读。这本书我读过两三遍,也算是重读常新的好书。

由于涉及到的话题比较多,暂时先写到这里,后续主要想聊聊以下话题。

  • 软件测试

  • 调试与剖析

  • 上线与发布

  • 代码重写

  • OKR

  • 20%时间

  • 绩效考核

  • TGIF

  • 谷歌的大佬们



推荐阅读


福利

我为大家整理了一份从入门到进阶的Go学习资料礼包,包含学习建议:入门看什么,进阶看什么。关注公众号 「polarisxu」,回复 ebook 获取;还可以回复「进群」,和数万 Gopher 交流学习。

浏览 91
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报