Software Engineering at Google

谷歌的软件工程

2017年1月31日

弗格斯·亨德森

(工作)(私人)

摘要

本文记载和描述了谷歌关键的软件工程实践。

作者简介

  弗格斯·亨德森在谷歌从事了超过10年的软件工程师工作。在1979年还是孩子时他就开始编程,之后进行编程语言设计和实践的学术研究。他和他的博士学位导师在墨尔本大学合作创立的研究小组开发出了Mercury语言。他是八次国际性会议的程序委员会委员,并且已经提交了超过50万行开源代码。他曾是Usenet的comp.std.c++新闻组主持人,并被官方认可为ISO C和C++ 委员会的“技术专家”。弗格斯有超过15年商业软件产业经验,他是谷歌软件构建工具Blaze最初的开发者之一,如今Blaze已经在谷歌内部广泛使用。他参与了服务器端软件背后的语音识别、语音操作(早于Siri)和语音合成工作。他目前主管着谷歌的文本到语音转化工程团队,但他仍在编写和评审大量的代码。他写出的软件如今安装在超过10亿台设备上,每天被使用超过10亿次。

目录

摘要
作者简介
目录
1.引言
2.软件开发
​ 2.1. 源码仓库
​ 2.2. 构建系统
​ 2.3. 代码评审
​ 2.4. 测试
​ 2.5. 缺陷追踪
​ 2.6. 编程语言
​ 2.7. 调试和分析工具
​ 2.8. 发布工程
​ 2.9. 启动批准
​ 2.10. 事后剖析
​ 2.11. 频繁重写

3.项目管理
​ 3.1. 20%时间
​ 3.2. 目标和关键成果(OKRs)
​ 3.3. 项目批准
​ 3.4. 企业重组

4.人员管理
​ 4.1. 角色
​ 4.2. 设施
​ 4.3. 培训
​ 4.4. 调动
​ 4.5. 绩效考核与奖励

5.结论

致谢

参考文献

1.引言

  谷歌是一家取得巨大成功的公司。同它成功的搜索引擎和AdWords一样,谷歌还开发了许多其他杰出的产品,包括谷歌地图、谷歌新闻、谷歌翻译、谷歌语音识别、Chrome和Android等。谷歌同样大大提升和扩展了许多通过收购YouTube等小公司得到的产品,而且对许多开源项目做出了显著的贡献。谷歌还展示了许多将要发布的惊人产品,无人驾驶汽车就在其列。

  谷歌成功的原因有很多:开明的领导、伟大的人物、极高的招聘条件,以及在一个极速增长的市场中成功通过早期确立的领先优势带来的财务实力。还有一个引领谷歌走向成功的原因是,谷歌开发出了杰出的软件工程实践。基于世界上最有才华的软件工程师们智慧的积累和提炼,这些实践随着时间推移一直在进步。我们想要和全世界分享这些实践中的知识,以及我们一路上在犯错中学习到的东西。

  这篇文章旨在简要地记载、描述谷歌关键的软件工程实践。其他组织或个人可以将其与自己的软件工程实践进行比较和对比,考虑是否应用其中的一些实践。

  许多作者(如引用[9]、[10]、[11])都写了书籍或文章来分析谷歌的成功和历史,但其中绝大多数都主要关注商业、管理和企业文化;只有一小部分(如引用[1]、[2]、[3]、[4]、[5]、[6]、[7]、[13]、[14]、[16]、[21])研究过软件工程方面,这些书籍和文章中的大多数都只探讨一个方面;并且所有书籍和文章都没有进行关于谷歌软件工程实践的总结,所以本文将提供一个整体的谷歌软件工程实践概述。

2.软件开发

2.1. 源码仓库

  谷歌的绝大多数代码都存放在一个统一的源码仓库中,所有为谷歌工作的软件工程师都可以访问它。有一些值得关注的例外,特别是两个大型开源项目Chrome和Android,它们存放在单独的开源仓库中,还有一些高价值和涉及重要安全的代码被严格控制其访问权限。总的来说,大多数谷歌的项目都分享使用同一个源码仓库,截止2015年1月,这个86TB的仓库中存储有10亿个文件,包括含有总计20亿行代码的900万个源码文件,这些代码已经有了3500万次的提交历史,并且提交数量以每天4万次的速度不断增长着[18]。这个仓库的写入权限被以下方式控制:只有在被列出的仓库子树所有者才能批准对子树的变更。但通常情况下,所有软件工程师都可以访问任意一部分代码,他们可以查看、构建、做本地修改、测试,还可以将变更发送给代码所有者进行评审,如果评审通过,这部分变更就会被提交到仓库中。无论项目的边界如何,谷歌的企业文化鼓励所有工程师学习如何修复代码并且实践于任何他们觉得存在错误的项目中。这增加了工程师的自主权并使工程师能够获得更高质量的基础架构,从而更好满足用户的需求。

  几乎所有的开发活动都发生在仓库的“头部”而非分支,这样有助于尽快识别集成问题所在、极大地减小合并分支的工作量,还可以更快更容易地推出安全修复程序。

  自动化测试系统频繁工作,通常在每次测试的传递依赖项中任何文件更改后运行,尽管这么做并不总是可行。当某次变更导致测试结果失败时,系统会在几分钟内自动通知变更提交者和评审员。很多团队通过放置醒目的标志物甚至是挂有彩色编码灯的雕塑来使自己的构建状态变得显眼(绿色表示构建成功并且所有测试用例通过,红色表示有一些测试用例没有通过,黑色表示构建失败),这种方法有助于让软件工程师追求成功的构建。大多数大型团队还有一名“构建警察”来保证测试持续通过,他们在提交违规变更的开发者身旁工作来快速修复问题或者回滚违规变更(这些构建警察一般是团队成员轮流担任,或者由团队中有经验的成员担任)。即使在非常巨大的团队中,这种专注于成功构建的模式也能使得开发工作变得更加实用。

  代码所有权。仓库中每个子树中都有一个存储着子树“所有者”用户id的文件,子目录一般从父目录那里继承所有者,也可以选择抑制继承。如下文的代码评审部分所述,每个子树的所有者控制着子树的写入权限。每个子树——尤其是那些地理位置上分散的团队——需要有至少两名所有者,实际应用中人数会更多。常见情况是团队中所有成员都会被列为子树所有者。因为不仅是团队成员,谷歌的任意工程师都会向子树提交变更,所以变更必须得到团队成员的批准。这样可以确保每次变更都被了解这个软件的工程师所评审。

  想要了解更多关于谷歌源码仓库的内容,可以阅读引用[17]、[18]和[21];想了解另一个大公司如何应对类似的挑战,可以阅读引用[19]。

2.2. 构建系统

  谷歌使用分布式系统Blaze来进行编译、链接软件以及运行测试用例。Blaze提供用于跨整个仓库工作的构建软件和测试软件的标准命令。这些标准命令和高度优化的实现意味着任何谷歌的软件工程师通常都可以十分简单迅捷地构建和测试仓库中的任何软件,这种一致性是工程师跨项目边界进行变更活动的关键推动因素。

  程序员编写Blaze所使用的如何构建目标软件的“BUILD”文件,例如库、程序和测试用例等构建实体是使用相当高级的声明性构建规范声明的,规范对每个构建实体指定名称、源文件以及它所依赖的库或其他构建实体。这些构建规范由名为“构建准则”的声明组成,每个声明都指定如“这个C++库带有这些源文件,这些源文件依赖于这些其他库”这样的高级概念,构建系统需要将每个构建规则映射到一组构建步骤,例如:编译、链接每个源文件的步骤,以及要确定所使用的编译器和编译标志的步骤。

  在某些情况下,特别是Go语言程序,构建文件可以被自动生成(并更新),因为BUILD文件中的依赖关系(通常)是源文件中依赖关系信息的抽象,但是它们仍然会被写入仓库。这样保证了构建系统能够仅通过分析构建文件(而不需要源文件)来确定依赖关系,避免了构建系统与所支持的许多不同语言的编译器、分析工具间的过度耦合。

  构建系统的实现使用到谷歌分布式计算基础架构,每个构建工作通常分布在成百上千的物理机器中运行,这使得构建超大项目或者并行执行数千个测试用例成为可能。

  单个的构建步骤必须是“封闭”的,它们仅仅依赖于声明的输入。分布构建的结果是强制所有的依赖都要被正确声明,只有声明的输入才可以被送到运行构建步骤的机器。因此,可以根据构建系统来了解真正的依赖关系,甚至是构建系统调用到的编译器也可以被一并视为输入。

  单个的构建步骤是确定的,因此构建系统能缓存构建结果。软件工程师能从一个旧的变更版本同步自己的工作区,也可以重新构建并且将会得到和之前完全一致的二进制文件。另外,这个缓存机制能在不同用户间安全地共享。(为了让缓存机制正常工作,我们必须消除构建调用的工具中的非确定因素,例如:清除生成输出文件中的时间戳。)

  构建系统是可靠的。它会追踪构建准则本身变更的依赖性,如果产生目标文件的操作变更时,即便是操作的输入没有改变,系统也知道重新构建目标文件,如构建时编译器选项的更改就会导致上述构建系统行为。构建系统还能适当处理以部分方式中断构建的问题,或在构建过程中更改源码文件问题:这种情况下你只需要重新运行构建命令。所以永远没必要在这个构建系统上运行类似“make clean”的命令。

  构建结果,包括中间结果都缓存在云平台上。即便是不同用户的另一个构建请求需要相同的结果,构建系统也不会重新构建,而是自动复用结果。

  增量重新构建是很快的。构建系统会驻留在内存中,所以对于重新构建命令,系统会分析出自上次构建以来更改过的文件。

  提交前检查。当初始化一次代码评审和/或准备提交一次变更时,谷歌有会自动运行的一系列测试工具开始工作。源码仓库的每个子树都有一份配置文件,文件里确定哪些测试需要运行,在代码评审时运行、在提交前立即运行还是两种情况都运行。测试可以是同步的,即在发送变更以供评审之前和/或在提交变更到源码仓库前(适用于快速运行的测试);测试也可以是异步的,结果将通过电子邮件发送给代码评审讨论主题。(评审主题是进行代码评审的电子邮件主题,主题内的所有信息都会展示在Web端的代码评审工具中。)

2.3. 代码评审

  谷歌建立起了优秀的Web端代码评审工具,集成有电子邮件,作者可以请求评审,评审员可以并排查看代码差异(有漂亮的颜色编码来体现视觉差异)并对其进行评论。当变更的作者启动一次代码评审时,评审员会收到电子邮件通知,电子邮件中包含一条对应Web评审工具中这次评审页面的链接。当评审员添加代码评论时,电子邮件也会发给作者。此外,自动化工具可以发送通知,例如自动化测试的结果或者静态分析工具的发现。

  所有对主源码仓库的变更“必须”被至少一名工程师评审。此外,如果不是某个文件所有者的工程师提交了关于这个文件的更改,那么必须由至少一名文件的所有者评审并且批准变更。

  在特殊情况下,子树的所有者可以在评审前对子树进行紧急提交变更,但他仍然必须指定评审员。直到评审通过前,变更的作者和评审员肯定会被指责做这种紧急提交。在这种情况下,解决评审意见所需的任何修改必须在新的变更中提交,因为原始的变更已经被提交了。

  谷歌有工具通过查看被更改的代码的所有权和作者、最近评审员的历史以及每个潜在评审员的待审代码审阅次数,自动为给定的更改建议评审员。一次变更影响的每个子树的至少一个所有者必须审查并批准变更。但除此以外,作者可以自由选择他们认为合适的评审员。

代码评审的一个潜在问题是,如果评审员回复太慢或者极度不愿意赞成变更,这可能潜在地拖慢开发。事实上让代码的编写者选择他们的评审员有助于避免这样的问题,允许工程师避开可能过度占用他们的代码的评审员,或允许工程师向较不全面的评审员发送简单变更的审阅请求,向更有经验的评审员或者多个评审员发送更加复杂的变更的审阅请求。

  每个项目的代码评审的讨论会自动复制到项目维护者指定的邮件列表中。任何人都可以在提交变更之前和之后自由评论任何变更,不管他们是否被指定为这个变更的评审员。如果发现了一个缺陷,追踪引入它的变更并对原始代码评审线程进行注释来指出错误是很常见的,以便原始作者和评审员们能够意识到这个错误。

  工程师也可以向几个评审员发送代码评审,然后一旦其中一位评审员批准(当然前提是作者或者第一个响应的评审者是所有者),在其他评审员评论前,立即提交更改,并在后续的更改中处理随后的任何评审注释。这可以减少评审的周转时间。

  仓库除了主要部分之外,还有一个“实验”部分,在这个部分正常的代码评审请求不被强制执行。然而,在产品中运行的代码必须存储在仓库的主要部分,并且强烈鼓励工程师在仓库的主要部分编写开发代码,而不是在实验区开发然后再移动到主要部分。因为相比在代码开发之后,代码评审在代码编写中完成是最有效的。在实践中工程师甚至经常对实验中的代码请求代码审查。

  工程师们被鼓励保持较小的单个变更,并最好把大的变更分解成一系列更小的变更,以便一个评审员能够一次性更简单地审阅。这也使编写者更容易对每个评审中提出的主要更改中做出反应;非常大的变更经常太死板并且反对评审员建议的更改。一种被鼓励的保持变更较小的方法1 是使用代码检查工具,用这次变更的大小标记每次代码评审。对30-99行代码的添加/删除/移除的更改被标记为“中等大小”,对超过300行代码的变更被标记为越来越贬低的标签,例如“大”(300-999)、“极其巨大”(1000-1999)等。(然而,以一种典型的谷歌式的方式,在每年的几天内,如在每年的“像海盗一样谈话的日子”里,用有趣的替代说法来代替这些熟悉的描述,以此来保持乐趣。:)

1 这在近几年有所改变。最新版本的代码检查工具不再对大型的变更标记使用贬义标签,但是它们仍然被用大小标记,例如“S”、“M”、“L”、“XL”。

2.4. 测试

  单元测试在谷歌得到了强烈支持和广泛实行。所有产品中使用的代码都被预期拥有单元测试,并且如果源文件在没有对应的测试时被添加,代码检查工具会突出显示。代码评审员通常要求任何增加新功能的更改都应该增加新的测试来覆盖新功能。模拟框架(它允许构建轻量级单元测试,甚至对于依赖重量级库的代码也是如此)相当流行。

  集成测试和回归测试也被广泛应用。

  正如上面在“提交前检查”中讨论的,测试可以作为代码审查和提交过程的一部分自动执行。

  谷歌还拥有测量测试覆盖率的自动化工具。测量结果也被整合为源代码浏览器中的可选层。

  部署前的负载测试在谷歌也是必不可少的。团队预计会生成一个显示关键度量的表格或图表,特别是延迟和错误率随着输入请求的速率如何变化。

2.5. 缺陷追踪

  谷歌使用一个名为Buganizer的缺陷跟踪系统追踪问题:缺陷、特性请求、客户问题和工序流程(比如发布或清理工作)。缺陷被分类为分级组件,每个组件可以具有默认的受托人和给次级接收者的默认邮件列表。当发送源更改以供评审时,工程师会被提示将变更与特定的发行编号相关联。

  谷歌的团队(尽管不是所有)经常按时扫描他们组件中的未决问题,确定它们的优先级,并在适当的时候将它们分配给特定的工程师。一些团队有一个人专门负责缺陷的分级,其他的团队在定期的团队会议上进行缺陷的分级。谷歌的许多团队利用缺陷上的标签来指示是否已经对缺陷进行了筛选,以及每个缺陷预定在哪个版本修复。

2.6. 编程语言

  在谷歌我们强烈鼓励软件工程师使用谷歌官方批准的五种编程语言之一来编写程序:C++,Java,Python,Go或JavaScript。最小化被使用的不同编程语言的数量可以减少代码重用和程序员协作的障碍。

  谷歌还有针对每种语言的谷歌风格指南,以确保那种语言的代码在整个公司都以相似的风格、布局、命名规约等编写。此外,还有一个全公司的可读性培训过程,在这个过程中,关心代码可读性的经验丰富的工程师通过回顾重大变化或者一系列变化来训练其他工程师如何用特定语言编写可读的、惯用的代码,直到评审员满意地认为作者知道如何使用那种语言编写可读的代码。每一项在特定语言中添加不平凡的新代码的更改都必须得到用该语言通过了“可读性”培训过程的人的批准。

  除了这五种语言之外,还有许多专门的领域特定语言用于特定的目的(例如,用于指定构建目标及其依赖项的构建语言)。

  这些不同编程语言之间的互操作主要使用Protocol Buffers来完成。Protocol Buffers是一种以高效但可扩展的方式对结构化数据进行编码的方法。它包括用于指定结构化数据的特定于域的语言,以及包含这样描述的编译器,并在C++、Java、Python中生成代码,用于构建、访问、序列化和反序列化这些对象。谷歌的Protocol Buffers版本与谷歌的RPC库集成,支持简单的跨语言RPC,请求和响应的序列化和反序列化由RPC框架自动处理。

  即使拥有庞大的代码库和语言多样性,过程的通用性仍是使开发变得容易的关键:只有一组命令来执行所有常见的软件工程任务(比如检查、编辑、构建、测试、评审、提交、文件缺陷报告等),并且相同的命令可以被无论什么项目或语言使用。开发人员不需要因为他们正在编辑的代码碰巧是不同项目的一部分或者用不同语言编写而学习新的开发过程。

2.7. 调试和分析工具

  谷歌服务器与提供多个调试运行服务器的工具的库链接。万一服务器崩溃,信号处理器将自动将堆栈跟踪转储到日志文件,并保存核心文件。如果崩溃是由于堆内存耗尽造成的,服务器将转储活动堆对象的采样子集的分配站点的堆栈跟踪。还有用于调试的Web接口,允许检查传入和传出的RPC(包括定时、错误率、速率限制等)、更改命令行标志值(例如增加特定模块的日志详细性)、资源消耗、简要介绍等。这些工具极大地提高了调试的整体易用性,达到了很少启动gdb等传统调试器的阶段。

2.8. 发布工程

  少数团队有专门的发布工程师,但对于谷歌的大多数团队来说,发布工程工作是由普通的软件工程师完成的。

  对于大多数软件来说,发布是经常进行的。每周或每两周发布是常见的目标,有些团队甚至每天发布。通过自动化大多数普通的发布工程任务使其成为可能。频繁发布有助于使工程师保持积极(为某个要到未来几个月甚至几年才会发布的东西感到激动是更难的事情),并且通过允许更多的迭代来提高总体速度,从而内获得更多的机会来反馈和更多的机会来回应反馈。

  一次发布通常从一个新的工作区开始,通过同步到最新的“绿色”构建的更改编号(所有自动测试都通过的最后一次更改),制作发布分支。发布工程师可以选择其他更改为“挑选”,即从主分支合并到发布分支。然后软件将从头开始重建,并运行测试。如果任何测试失败,则会进行额外的更改以修复故障,并将这些额外的更改挑选到发布分支上,之后将重建软件并重新运行测试。当测试全部通过时,内置的可执行文件和数据文件被打包。所有这些步骤都是自动的,所以工程师只需要运行一些简单的命令,甚至只是选择一个菜单驱动的用户界面的一些条目,并选择其中的变化(如果有的话)来挑选。

  一旦候选构建被打包,它通常被加载到“staging”服务器上,以便由一小组用户(有时只是开发团队)进行进一步的集成测试

  一种有用的技术不仅将来自生产流量的请求的副本(的一部分)发送到临时服务器,而且将这些相同的请求发送到当前的生产服务器以进行实际处理。临时服务器的响应被丢弃,来自现场生产服务器的响应被发送回用户。这有助于确保在将服务器投入生产之前能够检测到可能导致严重问题(例如服务器崩溃)的任何问题。

  下一步通常是向一个或多个正在处理一部分实时生产流量的“canary”服务器推送。与“staging”服务器不同,这些服务器对真实用户进行处理和响应。

  最后,发布版本可以被推送到所有数据中心的所有服务器。对于非常高流量、高可靠性的服务,这是通过几天内的逐步推出来完成的,以帮助减少由于新引入的未被前面任何步骤捕获的错误而导致的任何中断的影响。

  有关谷歌发布工程的更多信息,请参阅SRE图书第8章[7]。也见[15]。

2.9. 启动批准

  任何用户可见的更改或重大的设计更改的启动都需要执行更改的核心工程团队之外的许多人的批准。特别地,需要批准(通常要经过详细审查)以确保代码符合法律要求、隐私要求、安全要求、可靠性要求(例如,具有适当的自动监控以检测服务器中断并自动通知适当的工程师)、业务需求等。

  发布过程还被设计成确保每当重要的新产品或功能发布时,公司内部合适的人员都会得到通知。

  谷歌有一个内部启动批准工具,用于跟踪所需的审查和批准,并确保符合每个产品定义的启动过程。该工具易于定制,因此不同的产品或产品区域可以拥有不同的所需审查和批准集。

  有关启动过程的更多信息,请参阅SRE图书第27章[7]

2.10. 事后剖析

  每当我们的生产系统发生重大中断或类似事故时,相关人员就需要编写事后剖析文档。该文档描述了该事件,包括标题、摘要、影响、时间线、根本原因、什么生效/什么没有生效以及行动条目。关注的是问题以及如何在未来避免它们,而不是针对人员或分配责任。影响部分尝试根据中断持续时间,丢失查询数(或RPC失败等)和收入来量化事件的影响。时间线部分给出了导致中断的事件的时间线以及诊断和纠正中断的步骤。什么生效/什么没有生效部分描述了所吸取的教训——哪些做法有助于快速检测和解决问题,出现了什么问题,以及可以采取哪些具体行动(最好归档为分配给特定人员的缺陷)来降低未来类似问题的可能性和/或严重性。
  有关谷歌交付后验收文化的更多信息,请参阅SRE图书第15章[7]。

2.11. 频繁重写

  谷歌的大部分软件每几年就会重写一次

  这看起来代价高昂。事实上,它确实消耗了谷歌资源的很大一部分。然而,它还具有一些关键优势,这些优势对于Google的敏捷性和长期成功至关重要。在几年的时间里,随着软件环境和周围的其他技术的变化,以及随着技术或市场的变化影响用户需求、愿望和期望,产品的需求发生显著变化是平常的。几年前的软件是围绕着一组较旧的需求而设计的,通常不是以对当前需求最理想的方式设计的。此外,它通常积累了大量的复杂性。重写代码减少了处理不再那么重要的需求的所有不必要的累积复杂性。此外,重写代码是将知识和所有权感转移给更新的团队成员的一种方式。这种归属感对生产率至关重要:工程师自然会投入更多的精力开发特性和修复他们认为属于他们的代码中的问题。频繁的重写也鼓励工程师在不同项目之间的流动,这有助于鼓励思想的交叉传授。频繁重写也有助于确保代码是使用现代技术和方法编写的。

3.项目管理

3.1. 20%时间

  工程师被允许把最多20%的时间花在自己选择的任何项目上,而不需要经理或其他任何人的批准。这种对工程师的信任是非常有价值的,有以下几个原因。首先,它允许任何有好主意的人,即使别人不会立即认识到这个主意是有价值的,他们也有足够的时间来开发一个原型、演示版或报告以展示他们的想法的价值。其次,它提供了对可能隐藏的活动的可视化管理。在其他没有允许20%时间的官方政策的公司里,工程师有时会在不通知管理层的情况下从事“臭鼬工程”项目。如果工程师能够对这些项目持开放态度,在他们的常规状态更新中描述他们在这些项目上的工作,那就更好了,即使他们的管理层可能不同意项目的价值。有一个公司范围的官方政策和支持它的文化使这成为可能。第三,通过允许工程师们花一小部分时间在更有趣的事情上,使得工程师们对他们所做的事保持积极和兴奋,并防止他们精疲力尽,如果他们觉得被强迫花100%的时间来处理更繁琐的任务,这种精疲力尽的情况很容易发生。忙碌的、有进取心的工程师和精疲力尽的工程师之间的生产率差别远远超过20%。第四,这种政策鼓励创新文化。看到其他工程师开展有趣的实验性20%项目,会鼓励每个人都这样做。

3.2. 目标和主要成果(OKRS)

  谷歌的个人和团队需要明确记录他们的目标,并评估这些目标的进展情况。团队制定了季度目标和年度目标,并以可衡量的关键成果显示这些目标的进展情况。这是在公司的每一个层级完成的,一直到为整个公司定义目标。个人和小团队的目标应该与他们所在的更大团队的更高级别目标以及公司总体目标相一致。在每个季度结束时,记录可衡量的关键结果的进展情况,并给每个目标一个分数 ,从0.0(无进展)到1.0(100%完成)。OKRs和OKR得分在谷歌中通常都是可见的(偶尔也会有一些特别敏感的信息,比如高度机密的项目),但它们并没有直接用于个人的绩效评估。

  OKRs应该要设置得高:理想的总体平均得分是65%,这意味着鼓励团队将目标设定为比实际完成的任务多50%左右。如果一个团队得分明显高于这个水平,他们就会被鼓励为下一季度设置更有野心的OKRs(反之,如果得分明显低于这个水平,他们就会被鼓励在下一季度设置更保守的OKRs)。

  OKRs提供了一个关键机制,用于沟通公司各个部门正在开展的工作,并通过社群激励措施鼓励员工提供良好的绩效......工程师们知道他们的团队将会召开OKRs会议,并且尽管OKRs对绩效评估或薪酬没有直接影响,但他们有一种自然的动力去争取好成绩。 确定客观和可衡量的关键结果有助于确保这种表现良好的人力驱动能够引导到对实现共同目标的进展产生实际具体可衡量影响的事物上。

3.3. 项目批准

  虽然有一个定义良好的启动审批流程,但谷歌没有一个定义良好的项目审批或取消流程。尽管在谷歌工作了10多年,现在我自己也成为了一名经理,但我仍然不完全明白这些决定是如何做出的。在一定程度上,这是因为在整个公司,解决这个

  问题的方法并不统一。每个级别的经理都要对他们的团队所从事的项目负责,并在他们认为合适的时候行使他们的判断力。在某些情况下,这意味着这些决策是以一种自下而上的方式做出的,工程师可以在他们的团队范围内自由地选择要从事的项目。在其他情况下,这些决策是以一种自上而下的方式做出的,主管或经理决定哪些项目将继续进行,哪些项目将获得额外资源,哪些项目将被取消。

3.4. 公司重组

  有时会做出取消一个大型项目的执行决定,然后许多从事该项目的工程师可能不得不在新的团队中寻找新的项目。类似地,也有偶尔的“重组”工作将跨多个地点的项目合并成更少的地点,某些地点的工程师需要改变团队和/或项目来实现这一点。在这种情况下,工程师通常可以自由地从其地点的可用职位中选择新的团队和角色,或者在碎片整理的情况下,他们也可以通过变动职位来选择留在同一团队和项目中。

  此外,其他类型的公司重组,如合并或拆分团队以及报告链的变化,似乎是相当频繁的事情,尽管我不知道谷歌在这方面与其他大公司相比如何。在大型的、技术驱动的组织中,当技术和需求发生变化时,为了避免组织的低效,可能需要频繁的重组。

4.人事管理

4.1. 角色

  正如我们将在下面更详细地解释的那样,谷歌将工程和管理职业发展阶梯分开,将技术主管角色从管理中分离出来,将研究嵌入到工程中,并为工程师提供产品经理、项目经理和现场可靠性工程师(SREs)的支持。似乎至少有一些做法对维持谷歌的创新文化很重要。

  谷歌在工程中有少量不同的角色。在每个角色中,都可能有职业发展,有一系列的级别,以及晋升(与薪酬相关的改进,如薪水)的可能性,以表彰下一级别的表现。

  主要角色如下:

  • 工程经理

    这是列表中唯一的人员管理角色。软件工程师等其他角色的个人可以管理人员,但工程经理总是管理人员。工程经理通常是以前的软件工程师,并且总是拥有相当多的技术专长和人际技巧。

    技术领导和人员管理是有区别的。工程经理不一定领导一个项目;项目由技术主管领导,他可能是工程经理,但经常是软件工程师。项目的技术主管对该项目的技术决策有最终决定权。

    经理们负责选择技术主管,以及对他们团队的表现负责。他们对职业发展进行指导和协助,进行绩效评估(使用来自同行反馈的意见,见下文),并负责薪酬的某些方面。他们还负责招聘流程的某些部分。

    工程经理通常直接管理3至30人,但最常见的是8至12人。

  • 软件工程师(SWE)

    大多数从事软件开发工作的人都有这个角色。谷歌招聘软件工程师的门槛很高;通过雇佣非常优秀的软件工程师,能避免或最小化许多困扰组织的软件问题。

    Google在工程和管理方面有不同的职业发展顺序。 虽然软件工程师可以管理人员,或转换到工程经理的角色,但是管理人员并不是晋升的必要条件,即使是在最高级别。 在更高层次上,展示领导力是必要的,但这可以有多种形式。 例如,创建具有巨大影响力或被许多其他工程师使用的优秀软件就足够了。 这很重要,因为这意味着那些拥有出色技术技能但缺乏管理人员的愿望或技能的人仍然拥有良好的职业发展道路,而不需要他们走上管理的道路。 这避免了一些组织所面临的问题,即人们最终因为职业发展的原因而进入管理岗位,而忽视了团队中人员的人事管理。

  • 研究科学家

    这个角色的招聘标准是非常严格的,并且门槛非常高,要求展示出卓越的研究能力,这可以通过出色的出版记录和编写代码的能力得到证明。学术界许多能够获得软件工程师资格的非常有才能的人不具备在谷歌担任研究科学家职位的资格;大多数拥有博士学位的人都是软件工程师,而不是研究科学家。研究科学家对他们包括出版物在内的研究贡献进行了评估,但除以上所诉和不同的头衔外,软件工程师和研究科学家在谷歌的角色之间并没有太大的区别。两者都可以做原创研究和发表论文,既可以开发新产品理念和新技术,也可以编写代码和开发产品。 谷歌的研究科学家通常与软件工程师一起在相同的团队中工作,并从事相同的产品或相同的研究。这种将研究嵌入工程中的做法有助于将新的研究轻松地结合到到运输产品中。

  • 网站可靠性工程师(SRE)

    操作系统的维护由软件工程团队完成,而不是传统的系统管理员,但SRE的招聘要求与软件工师职位的要求略有不同(如果有其他专业知识技能,如网络或unix系统作为弥补,软件工程技能要求可能略低)。SRE角色的性质和目的在SRE书中解释的非常详细[7],因此我们不在此进一步讨论。

  • 产品经理

    产品经理负责产品的管理;作为产品用户的倡导者,他们协调软件工程师的工作,向用户宣传重要的特性,与其他团队协调,跟踪bug和时间表,确保生产高质量产品所需的一切。产品经理通常不会自己编写代码,而是与软件工程师合作以确保编写正确的代码。

  • 项目集经理/技术项目经理

    项目集经理的角色和产品经理大致相似,但他们管理的不是产品,而是项目、过程或操作(例如数据收集)。技术项目经理是类似的,但也需要与他们的工作相关的专门技术知识,例如用于处理语音的语言学。

    软件工程师与产品经理和项目集经理的比例因组织而异,但通常很高,例如在4:1到30:1的范围内。

4.2 设施

  谷歌以其有趣的设施而闻名,其中包括滑梯,球坑和游戏室。 这有助于吸引和留住优秀人才。谷歌很棒的咖啡馆对员工免费提供,办公室也提供这个功能(即免费咖啡)从而巧妙地鼓励员工留在办公室;饥饿绝不是离开的理由。随处可见得“微型厨房”也有相同的作用,员工可以在那获取零食和饮料,它们也充当了非正式想法交流的一个重要来源,因为很多对话都是从那里开始的。健身房、运动和现场按摩有助于员工保持舒适、健康和快乐,从而提高生工作效率和记忆力。

  谷歌的座位是开放式的,通常相当密集。虽然存在争议[20],有时以牺牲个人注意力为代价,但它鼓励交流而且经济。

  员工被分配了一个单独的座位,但是会经常换座(例如,每6-12个月,通常是组织扩展的结果),由经理选择座位使得促进和鼓励相邻或接近相邻的个人之间的交流更加容易。

  谷歌配备了最先进视频会议设施的会议室,只需轻轻一按即可连接到另一方进行预先安排的议事日程邀请。

4.3. 培训

  谷歌通过多种方式鼓励员工接受教育:

  • 新员工(“Nooglers”)有一个强制性的初始培训课程。
  • 技术人员(SWEs和研究科学家)从做“Codelabs”开始:短期的个人技术在线培训课程,以及编码练习。
  • 谷歌提供员工各种网络和面对面的培训课程。
  • 谷歌也为在外部机构学习提供支持。

  此外,每个Noogler通常都被指定一个官方的“导师”和一个单独的“伙伴”来帮助他们跟上进度。非正式的指导还通过与经理的定期会议、团队会议、代码评审、设计评审和非正式过程来进行。

4.4. 调动

  鼓励公司不同部门之间的人员调动,以帮助在整个组织内传播知识和技术,并改善跨组织的沟通。在一个职位上工作了12个月的员工可以在项目和/或办公室之间进行调动。软件工程师也被鼓励在组织的其他部分做临时任务,例如 在SRE(现场可靠性工程)中进行为期六个月的“轮换”(临时任务)。

4.5. 绩效考核和奖励

  谷歌非常鼓励反馈。工程师可以通过“同行奖金”和“荣誉”给予彼此明确的积极反馈。 任何员工都可以提名任何其他员工获得“同行奖金” ——现金奖励100美元——每年最多两次,奖励超出正常的任务范围的工作,只需填写网络表格来描述理由。当获得同行奖励时,通常也会通知团队成员。员工也可以给予“荣誉”,即正式的表扬声明,为良好的工作提供明确的社群认可,但没有资金奖励; 对于“荣誉”而言,并不要求工作超出正常的任务范围,也不限制他们可以获得的次数。

  经理还可以发放奖金,包括项目完成时的奖金。和许多公司一样,谷歌的员工会根据自己的表现获得年度绩效奖金和股权奖励。

  谷歌有一个非常细致的晋升过程,包括自我提名或经理提名,自我审查,同行审查,经理评估;随后,晋升委员会根据这些意见做出实际决定,其结果可由晋升审查委员会进一步审查。确保合适的人员获得晋升对于维持对员工的正确激励至关重要。

  另一方面,绩效不佳则由经理反馈处理,必要时还会采取绩效改进计划,其中包括制定非常明确的具体绩效目标和评估实现这些目标的进展。如果失败,员工可能会因表现不佳而被解约,但实际上这在谷歌极其罕见。

  通过反馈调查评估经理的绩效; 每位员工被要求每年填写两次关于其经理绩效的匿名调查,然后把调查结果汇总给经理。这种向上反馈对于维护和提高整个组织的管理质量是非常重要的。

5.结论

  我们简要介绍了谷歌过去的大多数关键软件工程实践。当然,谷歌现在是一个庞大而多样化的组织,组织的某些部分有不同的做法。但是谷歌的大多数团队通常会遵循此处描述的做法。

  由于涉及许多不同的软件工程实践,以及与我们的软件工程实践无关的谷歌成功的其他许多原因,因此很难提供任何定量或客观证据来将个别实践与改进的结果联系起来。 然而,这些实践在谷歌经受了时间的考验,受到数千名优秀软件工程师的集体主观判断。

  对于其他组织中那些主张使用本文中描述的特定实践的人来说,也许会说“这对谷歌来说已经足够好了”。

致谢

  特别感谢Alan Donovan的非常详细和建设性的反馈,并感谢Y aroslav Volovich,UrsHölzle,Brian Strope,Alexander Gutkin,Alex Gruenstein和Hameed Husaini对本文早期草稿的非常有益的评论。

参考文献

[1] Build in the Cloud: Accessing Source Code , Nathan York,
http://google-engtools.blogspot.com/2011/06/build-in-cloud-accessing-source-code.html
[2] Build in the Cloud: How the Build System works , Christian Kemper,
http://google-engtools.blogspot.com/2011/08/build-in-cloud-how-build-system-works.htm
[3] Build in the Cloud: Distributing Build Steps, Nathan York
http://google-engtools.blogspot.com/2011/09/build-in-cloud-distributing-build-steps.html
[4] Build in the Cloud: Distributing Build Outputs, Milos Besta, Yevgeniy Miretskiy and Jeff Cox
http://google-engtools.blogspot.com/2011/10/build-in-cloud-distributing-build.html
[5] Testing at the speed and scale of Google , Pooja Gupta, Mark Ivey, and John Penix, Google
engineering tools blog, June 2011.
http://google-engtools.blogspot.com/2011/06/testing-at-speed-and-scale-of-google.html
[6] Building Software at Google Scale Tech Talk, Michael Barnathan, Greg Estren, Pepper
Lebeck-Jone, Google tech talk.
http://www.youtube.com/watch?v=2qv3fcXW1mg
[7] Site Reliability Engineering , Betsy Beyer, Chris Jones, Jennifer Petoff, Niall Richard Murphy,
O'Reilly Media, April 2016, ISBN 978-1-4919-2909-4.
https://landing.google.com/sre/book.html
[8] How Google Works, Eric Schmidt, Jonathan Rosenberg.
http://www.howgoogleworks.net
[9] What would Google Do?: Reverse-Engineering the Fastest Growing Company in the History
of the World , Jeff Jarvis, Harper Business, 2011.
https://books.google.co.uk/books/about/What_Would_Google_Do.html?id=GvkEcAAACAAJ&redir_esc=y
[10] The Search: How Google and Its Rivals Rewrote the Rules of Business and Transformed
Our Culture , John Battelle, 8 September 2005.
https://books.google.co.uk/books/about/The_Search.html?id=4MY8PgAACAAJ&redir_esc=y
[11] The Google Story , David A. Vise, Pan Books, 2008.
http://www.thegooglestory.com/
[12] Searching for Build Debt: Experiences Managing Technical Debt at Google, J. David
Morgenthaler, Misha Gridnev, Raluca Sauciuc, and Sanjay Bhansali.
http://static.googleusercontent.com/media/research.google.com/en//pubs/archive/37755.pdf
[13] Development at the speed and scale of Google , A. Kumar, December 2010, presentation,
QCon.
http: //www.infoq.com/presentations/Development-at-Google
[14] How Google Tests Software , J. A. Whittaker, J. Arbon, and J. Carollo, Addison-Wesley,
 2012.
[15] Release Engineering Practices and Pitfalls , H. K. Wright and D. E. Perry, in Proceedings of
the 34th International Conference on Software Engineering (ICSE ’12) , IEEE, 2012, pp.
1281–1284.
http://www.hyrumwright.org/papers/icse2012.pdf
[16] Large-Scale Automated Refactoring Using ClangMR , H. K. Wright, D. Jasper, M. Klimek, C.
Carruth, Z. Wan, in Proceedings of the 29th International Conference on Software Maintenance
(ICSM ’13) , IEEE, 2013, pp. 548–551.
[17] Why Google Stores Billions of Lines of Code in a Single Repository , Rachel Potvin,
presentation.
https://www.youtube.com/watch?v=W71BTkUbdqE
[18] The Motivation for a Monolithic Codebase , Rachel Potvin, Josh Levenberg, to be published
in Communications of the ACM, July 2016.
[19] Scaling Mercurial at Facebook, Durham Goode, Siddharth P. Agarwa, Facebook blog post,
January 7th, 2014.
https://code.facebook.com/posts/218678814984400/scaling-mercurial-at-facebook/
[20] Why We (Still) Believe In Private Offices , David Fullerton, Stack Overflow blog post,
January 16th, 2015.
https://blog.stackoverflow.com/2015/01/why-we-still-believe-in-private-offices/
[21] Continuous Integration at Google Scale , John Micco, presentation, EclipseCon, 2013.
http://eclipsecon.org/2013/sites/eclipsecon.org.2013/files/2013-03-24%20Continuous%20Integration%20at%20Google%20Scale.pdf

翻译:安昕瑜 蔡新宇 孔庆振

你可能感兴趣的:(Software Engineering at Google)