多年以来,Mozilla 的工程团队不断为(提高)代码质量而引入工具。这一套工具链可以在 Firefox 复杂开发周期的不同阶段发挥作用。在本文,我们将介绍我们使用的开发工具类型、工具解决的一些挑战和我们设计的架构解决方案。当我们谈到代码质量工具时,主要指以下几类工具:

  • 静态分析

  • 代码检查

  • 代码分析

  • 代码覆盖度

一般而言,对于比构建 Firefox 桌面浏览器更小的开发项目,简易 CI(持续集成)作业的设置和配置相对比较容易。它们的目标语言数量有限。开发人员有许多设置和 CI 平台可以使用。

而 Firefox 是一个庞大的开源软件项目,它有 2100 万行代码,由网景公司在 1998 年创建。我们使用 C++、Rust、JavaScript、Python 等多种语言,每天处理数百个更改,并且与有好几个 GB 的大型存储库打交道。这带来一些独特的挑战!

使用的工具

我们使用的一些工具:

面对2100万行代码,Mozilla工程师如何保证Firefox的代码质量?

同时,我们将重点聚焦这些使用工具的原因和方式。关于所有这些工具的更多细节,你可以查看文档。由于很难在 Firefox 开发人员的工作站上使用这些工具,我们决定在审查阶段集中精力运行这些工具。

面临的挑战

不同的失败类型

当测试(代码)质量时,我们会遇到不同类型的失败。这是我们面临的第一个挑战:

  • 部分 issues 需要中断 CI(持续集成),没有假阳性(构建错误、未严格遵守某些强制性格式化规则等)。例如,我们有针对特定代码库的自定义 C++ 检查器。我们知道它们会给产品带来问题。因此,我们要确保中断 CI,以防止代码在产品中落地并造成麻烦。

  • 潜在错误:根据不同的观点,C/C++ 要么太复杂,要么就是容易产生问题的语言。因此,我们开发了一些工具(静态分析器)来避免问题产生。然而,这些检查器存在假阳性报告。(否则,它们会成为编译器触发的错误。)

    例如,clang-tidy“有损于性能的参数值”

    面对2100万行代码,Mozilla工程师如何保证Firefox的代码质量?

  • 能有最好的工具:可以增强代码库一致性的检查器,让代码更具可读性,并减少技术债务。

代码库的规模

下一个主要挑战是:我们要与 2100 万行代码打交道。在启用新检查器前,我们无法完全修复现有的数百个问题,并且我们也不想强迫开发人员修复所有与他们建议更改的无关问题。所以,在这种规模下,我们必须想出不同的解决方案。

为解决这个问题,我们已经确定并部署了两个解决方案。当 issues 容易修复时(例如 eslint、flake8 等),我们创建并维护一个它们可以在上面运行的目录 / 文件列表,并按目录逐个修复问题。

对于更复杂的检查器,比如 C++ 静态分析器,我们想出一些启发式方法来识别问题是新是旧。我们要么创建一个以前接受的用法列表(例如:已废弃的线程使用方法),要么使用一些启发式方法来评估缺陷是新的还是旧的。

架构

我们目前最大的优势在于,使用的工具是由 Mozilla 优秀的工程团队构建的。我们用内部的 CI 系统 Taskcluster 来测试和构建 Firefox。此外,在发布前,我们还会使用自己的 Phabricator 实例来检查 Firefox 的每个补丁。

工作流

每个补丁都遵循同样的工作流:

  1. Phabricator 通知我们的 Web 服务有新补丁需要分析;

  2. Web 服务会使用一个工作进程池将补丁及之前的版本应用到预先克隆好的存储库上。(还记得吧, Firefox 的主存储库有几 GB 。)

  3. 之后,该补丁会被推送到我们的 Try 服务器。该服务器是一个 Mercurial 服务器,Mozilla 开发人员会用它触发 CI 构建(来尝试构建)。

  4. Try 服务器在 Taskcluster 上创建一组代码分析任务。这些任务和它们的触发规则是由开发人员在 Firefox 的源代码中定义的。

  5. 每个代码分析任务都会生成一个 JSON 负载,列出从栈中找到的所有潜在缺陷。

  6. 最后,发布任务分析、聚合和过滤所有问题。然后,它在 Phabricator 和我们的 Web 服务中发布相关内容。这样,开发人员就可以在任何时候查看它们,并在出现问题时收到通知。

对于大多数补丁,这个工作流通常执行要花费 12 到 15 分钟。一些修改文件非常多的补丁可能会触发更多的分析程序,处理起来也会更慢。

面对2100万行代码,Mozilla工程师如何保证Firefox的代码质量?

关于架构的更多信息,请查看项目文档

优点

这种方法的主要优点在于它让我们可以利用许多现有工具,而且这些工具也能在开发人员自己的计算机上使用。(这可以通过 mach 工具实现)。

另一个很大的好处是:我们无需维护自己的分析程序,也不需要维护它们的依赖关系。这些分析器可以保证一直运行最新的版本,因为它们是在 Firefox 代码库中定义的,并由全球各地的人维护。

目前,我们支持特定的分析程序(clang-tidy、clang-format、rustfmt、mozlint 等)以及通用格式。这让任何 Firefox 开发人员都可以轻松地扩展我们平台的功能。

它看上去是什么样子?

以下是 Phabricator 上的一些 issues 截图。这些是大多数 Mozilla Firefox 开发人员都可以看到的工作流视图。

在修订中发现的所有 issues:

面对2100万行代码,Mozilla工程师如何保证Firefox的代码质量?

汇总说明清单:

面对2100万行代码,Mozilla工程师如何保证Firefox的代码质量?

补丁自身报告的 issues:

面对2100万行代码,Mozilla工程师如何保证Firefox的代码质量?

未来计划

这个关于代码质量的工作流项目是由一个小团队在过去几年构建的。在去年,它已经处于一个非常好的稳定性水平。现在,对来自 Firefox 开发人员的每个补丁,我们平均用不到 15 分钟就可以处理完。

并且,我们还通过这个平台支持其他几个 Mozilla 项目: NSS (Firefox 中的主要加密库)、一些 CI 内部项目,预计将来还会有其他新项目。

几个月来,我们一直在与 Ubisoft 合作进行一项实验,用机器学习来分析审查阶段的补丁。该分析会提交一份详细报告,评估补丁风险,这取决于很多因素(例如复杂性、过程度量等等)。将来,这个项目或许可以帮助我们减少 Firefox 代码库中的回归数量,并提高审查速度。

在接下来的几个季度中,我们的目标是通过将 Mozilla fuzzers 移植到 Taskcluster 在落地阶段引入模糊测试(fuzzing)。我们还希望在不向开发人员发送垃圾邮件的情况下报告更多问题,采用一些方法来避免重复,包括仅报告修订更新时可能是新问题的问题、列出补丁带来的问题,或者与以前已知的状态进行比较。

最后,我们希望利用工程流程团队开发的新电子邮件通知系统来增强我们的报告系统。这可能会改进问题在 Phabricator 中的显示方式,并扩展我们对检测到的问题进行说明和分析的能力。

打上补丁。我们希望 Firefox 的每一个新版本都能带来这样的变化。另一方面,我们为我们开发的代码质量工具感到骄傲,这些工具可以支持 Mozilla 工程师和贡献者构建更好的 Firefox。

如果你想参与或者了解更多关于 Firefox 构建的知识,这有很多方法可以让你参与到浏览器技术的构建中。代码审查平台已经开源,你可以从 Github 上获得。

英文原文:

Engineering code quality in the Firefox browser: A look at our tools and challenges