谷歌pH值:衡量软件产品的项目健康度

| 2021-02-28

最近,我又读了一遍《 SRE Workbook 》,其中又一次强化我的印象的一点是:SRE 是从用户视角为出发点,来识别和设计 SLO 的。由于这些已经上线的产品已经为真实用户提供真实的“不间断”服务,所以一定是以用户为起点,进行指标建设。

那么对于尚未上线的代码来说,如何进行衡量?需要遵循哪些理念与原则?

2020年3月出版的《谷歌软件工程(英文版)》中全面地解答了相关问题。

总的看来,谷歌强调共享责任积极协作,但这并不妨碍将整个软件生命周期的度量管理分为两部分。

一是研发阶段的工作(代码上线及上线之前),这部分主要由 Product Team 负责;

二是运维过程的工作(代码上线之后),这部分主要由 SRE 负责。

本文主要是作者根据多种信息的收集,对第一部分内容的简要总结,也就是研发阶段的项目健康度。

谷歌代码的基本信息

与大多数企业不同,的代码使用大仓( monorepo )+ 主干开发( Trunk-based Development )模式,同时每天的代码提交数量也很大 (约 6万多次,其中不仅包括工程师的提交,也包括自动化工具对代码的修改)。每一次提交被称为一个 Changelist(简称为 CL)。

谷歌云平台的发布节奏

谷歌云平台,即 Google Cloud Platform ,简称 GCP 。 下图所示的部分是 GCP 的上层组件,其显示为一周发布两次,这也是合理的。

而 GCP 是两周对外正式发布一次。但是,在云平台内部,仍旧希望坚持谷歌的 Daily Release Candadite 。

Daily Release Cadadite 实践:

    每天定时自动从 Trunk 拉出一个分支,
    并验证它能达到“正式发布的质量”。
    如果无法达到正式发布的质量标准,说明代码质量下降,需要采取行动。
    这样的话,
    一旦想提高发布节奏时,
    可以不必做任何额外的努力。

坚守的工作原则

那么,到底如何平衡开发速度与质量?

当谈到“平衡”时,每个人在做出任何相关的判断前,必须能够遵循相同的原则,否则就无法在整个组织中保持一致性,从而问题无法收敛。而谷歌云平台所遵循的原则有以下三个:

  • 推进测试健康 ( Drive Test Health )
  • 避免性能回归问题( Avoid Preformance Regressions )
  • 确保高质量的发布 ( Ensure High Quality Releases )

这些原则也是所有度量指标项的“根基”

否则,所有的度量指标都是无源之水,无根之苗。

测试健康

在《持续交付 2.0 》第十章(自动化测试策略)中指出,我们无法通过单一手段来保证软件质量,必须建立全面的自动化测试保护网,才有可能达成 高质量快速高频交付

下面是 GCP 建立的自动化质量保护网。可以看到,这就是《持续交付 2.0 》第七章所说的“部署流水线”一种实现。现在,微服务架构的流行也提升了测试的难度。

为了促进测试健康,自动化测试的优化方向是“密闭集成测试”、“提交前的快速测试(也有人叫提交门禁)”,以及“消除不稳定测试”。

另外,测试用例必须能够“ write once, run everywhere ”。这种高质量的测试用例可以扩大自动化测试的价值,即:无论是开发人员在自己的开发机上为某个需求写下第一行代码,还是验证 Release Candidate 的质量,甚至是生产环境之中,都可以使用它们。

提交前的快速测试,其目的就是预防 Bug ,而不是发现 Bug 。
需要建设工具与平台,让开发人员在提交代码之前,就能够很方便地来检查代码的质量。

避免性能回归问题

对于谷歌云平台来说,性能问题也与功能特性一样,是一等公民。

谷歌云在内部构建了一个性能基准库,使服务所有者和产品能够无缝地集成到他们的产品中,并获得丰富的性能基准。这个现在不仅仅在 GCP 中使用,而且已经在谷歌推广。

另外,它还将性能作为一项功能或一项服务来持续监控。

确保高质量的发布

这一点更能体现“速度与质量”的平衡。

发布速度是指将功能特性从开发环境推到生产环境的速率。

“常在河边走,哪能不湿鞋”。

所以,要持续不断地找到办法,以便在出现问题时,能够快速处理,对用户不产生或尽量少产生影响。

除了前面提到通过各种防御方式,尽可能不产生 Bug 以外,还可以使用下面的方法。

  • 将部署与发布分离

也可以叫做“二进制部署”与“配置发布”分离。即:功能特性还没有完成的代码可以部署到生产环境,但并不可见。

只有当该功能特性的代码都部署到生产环境以后,通过配置开关将该特性打开。

如果发生问题,直接关闭这个特性的配置开关即可,应对非常从容。

  • 定时发布且间隔时间短

定时发布让所有人都能够知道对主干代码的质量标准,降低了很多的沟通协调成本。

如果发布的间隔时间短,任何特性都不必象“赶最后一班车”那样着急,可以从容地写出高质量的代码。

因为下一个发布时间点马上就到,而不必等很久。

  • 先回滚,再修复

一旦发现问题后,就先把有问题的版本回滚,恢复到前一个工作良好的版本。

这种方法通常在原子提交实践的基础上更容易实施。因为即便问题并不是因某个特性引发的,而只是由于几处代码有问题。那么就可以将与这几处代码相关的 CL 直接从 Trunk 上 backout 出来,再次发布即可。由于所有 CL 都具有原子性的,所以绝大多数情况下,这是非常容易的。

在这种方式下,可以确保在修复严重 bug 时,不必因为走“捷径”,而引发更多的问题和低质量代码。

项目健康度

如果你不知道你从哪儿来,你就不知道你身处何处。你不知道身在何处,就不知道要去何方。如果你不知道要去何方,你很可能会走错。

项目健康度(Project Health,简称为 pH值)。它由一组指标组成,用于监测软件产品在发布之前的研发过程保持健康状态。

这个指标集的选取也是秉承了谷歌对软件产品的研发理念(可持续的、小批量、高质量、快速地发布),坚守上面提到的研发原则,同时也结合了其自己的实际研发流程。

由于谷歌内部的绝大部分产品所用的研发流程具有高度的一致性,所以其度量体系的建设和维护成本也就相对较低。

谷歌内部多年积累的数据表明,pH值越高,项目越能很好地平衡速度与质量。pH值越高,开发实践越好,更少的问题,更快地修复bug,更有时间聚焦于开发新功能。开发人员只要写代码,然后提交就行了。因为整个版本发布流程都是自动化的了。

如果代码有问题,部署流水线中的各种自动化测试就会把它拦截下来,通知你修复。

pH 指标与级别说明

这个指标体系主要跟踪以下四个方面:工程师的生产效率( developer productivity ),发布速率( release velocity ), 可靠性( reliability ), 以及最为重要的质量( quality )。

具体指标包括:

  • TAP 测试通过率
  • TAP 测试不稳定性
  • 全量测试覆盖率
  • 增量测试覆盖率
  • 预提交测试( Pre-submit test)的测试覆盖率
  • 预提交测试的跳过率
  • 预提交测试的运行时长
  • 发布间隔
  • 发布分支上的 Cherry-pick 数量
  • 每次发布的 粒度(即有多少 CLs )

如下图所示:

需要注意的是,对于一个项目的评级是使用“最低分”。

为什么使用“最低分”,而不是“平均分”呢?

因为平均分是一种虚荣指标,并不能真实反映该项目真正需要关注的弱点。

下面是某个项目的具体指标得分情况,仅供参考。

TC VS pH

谷歌使用了八年的 Test Certified(2008~2016) 被这个 Project Health 所取代。这是为什么呢?

  • 开发工程师已养成写测试的习惯

  • TC 标准是静态的

当团队达到某个级别以后,就没有再进一步的动力提升级别,甚至项目实际情况已经降级了。

由于没有再次参与评估的动力,所以很多项目团队都停留在 Level3 。

也就是停在中间水平,即不高,也不低。

  • TC 中的一些指标不易度量

在评估时需要人工评委参与;

某些指标的取证比较困难。

有时需要人工举证,且取证成本较高,可信度较低。

为了解决这个问题,pH 值出现了。

  • pH 全部可以自动计算,无需人工评委参与;
  • 随时评估:因为可以做到全自动化地动态更新,可以随时掌握数据变化;
  • 让 Engineering Productivty 团队可以有针对性地帮助产品开发团队。
Test Certified (后文简称 TC ) 是 Google 内部的一个认证项目,从 2008 年开始使用,到 2016 年停止使用。八年的时间里取得了多个里程碑,有 1700+ 的项目注册,其中 1200+ 获得了 1 到 5 级的认证,578 名导师参与。Test Certified 最早起源于 2006 年,通过多年的实践,在 Google 的很多项目中起到了积极的作用,Test Certified 使用 5 个级别的规范来定义一个项目的测试健康度,以此来促进开发人员将测试当作软件开发的一部分,尤其是单元测试。

参考链接:

  1. GCP: Move Fast and Don’t Break Things, 2019
  2. 谈谈 Google 的 Test Certified, 2016