每个工作日,Facebook工程师都会将数千个差异(由一个或多个文件组成的更改)投入生产。这种代码速度使我们能够快速发布新功能,提供错误修复和优化以及运行实验。但是,在任何行业中迅速采取行动的自然弊端是无意间导致性能,可靠性或功能正确性下降的风险。
为了使我们能够将开发人员基础结构扩展到此数量(同时还减少和最小化回归),我们构建了工具和机制来帮助工程师了解差异是否引入了(或将引入)回归。我们将从这些工具获得的反馈视为信号。信号可以通过自动测试,静态分析框架,性能日志,崩溃转储,错误报告,生产监控警报以及许多其他来源生成。这些信号可以在开发生命周期的任何阶段浮出水面。给定的差异可以生成数百个信号-包括错误,成功和警告-可以帮助工程师评估该差异是否已准备好进入稳定的分支并开始进入生产。差异到达后,通常会通过我们的任务管理系统将信号发送给工程师。单个工程师可以轻松地在其积压中处理数百个任务。
由于产生了如此多的信号,工程师很快就不知道该把时间集中在什么地方了。一个非常重要的信号可能会被忽略,因为它被嘈杂的低优先级信号淹没了,这使工程师更难于快速或轻松地评估每个信号的相对优先级。他们可能花费数小时来调试一个原来是噪音的问题,或者缺少必要的信息来帮助他们进行诊断和修复。
随着时间的流逝,随着我们建立更多的工具和监视系统,信号量不断增加,直到持续分散注意力。最终,我们意识到我们需要一种更有效的方法来帮助工程师轻松地了解要做什么,并花费更少的时间调试和解决问题。
我们已经看到,随着缺陷在开发过程中的不断发展,修复这些缺陷的时间成倍增加。对于大多数制造业来说,都是如此,无论您是生产汽车,建造摩天大楼还是运输软件。我们在自己的数据中清楚地看到了这一点:
工程师在编码时在集成开发环境(IDE)中检测到的缺陷可以在几分钟之内修复。
如果允许同一缺陷着陆并投入生产,则修复可能需要几天的时间。
任何旨在减少修复时间的解决方案都需要确保在此过程中尽早检测到并修复了回归问题。
为了应对这些挑战,我们于2019年启动了跨功能工作,称为FixFast。FixFast召集了工程团队,负责生成代表性能,可靠性和功能正确性回归的大部分信号。这项工作的任务是通过改善工程经验和将可行的检测移到更上游来减少大规模修复回归所需的工作。
尽管高层战略很明确,但我们仍然要回答一些大问题:我们应该从哪些问题入手?我们应该如何衡量进度?我们知道我们需要一种数据驱动的方法来在Facebook规模上实现此目标,因此我们首先找到一种方法来衡量工作量并设定基准。
为了追求指标,我们考虑了许多选择。最终,我们采用了一种激励工程师“向左移动”的度量标准,这是一种行业惯例,即在开发生命周期的早期尽可能早地检测(并修复)回归。如上所述,帮助工程师在仍专注于该代码块的同时在IDE中检测和修复问题的过程大大减少了耗时,因为一旦将代码投入生产后就可以这样做。
我们开发了一个统一的顶级度量标准,称为“每位开发人员成本”(CPD),从而使修复的较早阶段获得的加权成本低于后来的加权成本。 (请注意,术语“成本”用于表示工程师花费在解决问题上的时间,而不是直接的美元金额。)每个阶段的权重是通过分析内部数据并进行严格的回测以确定相对数量来确定的。时间工程师已经在每个阶段花费固定的回归。这种方法鼓励在生命周期的早期阶段发现和预防问题。它还激励工程师专注于降低噪声,信号可操作性以及其他阶段内改进。这不仅有助于确保检测到信号,而且可以确保相关工程师能够在代码移至开发生命周期的下一个阶段之前快速解决问题。
通过根据CPD评估进度,团队能够采用定量方法来跟踪每个阶段中的项目,以帮助减少花费在修复回归上的工程时间。
一旦CPD到位,Fix Fast团队就会开发出一系列方法,这些方法已证明在左移,降噪,可操作性等方面得到了改善。
到目前为止,早期检测是减少修复时间的第一方法。自动化测试对于确保不将回归推广到生产中非常重要。但是,在持续集成和生产中运行测试需要大量的数据中心容量和时间。考虑到每天需要测试的差异数量非常之多,以及可供选择的测试数量,我们采用了复杂的试探法和机器学习算法,以帮助您在每个阶段选择最佳测试,并考虑了数量可用的时间和容量。通常,Facebook仅在diff上运行数百万次测试。
我们还在IDE中开发自动化测试,以使工程师在编写代码时就可以运行测试的子集,甚至无需提交代码进行审查。 IDE测试结果将在几分钟内返回。通过采用这种方法,工程师现在可以更快地获得90%的测试信号,并且我们将提交后失败的次数减少了10%以上。
减少嘈杂的,重复的或不可操作的信号对于确保工程师知道哪些信号很重要并可以快速修复任何潜在的回归非常重要。
为了解决这个问题,我们首先必须了解哪些信号正在产生有用的结果。在手动分析了数百种此类信号之后,我们开发了一种启发式的方法,称为有意义的动作,以表示有用的任务,并导致工程师采取某些行动。有意义的启发式行动考虑了人类的评论,与任务相关的差异或其他非平凡的行动。通过系统地删除不健康的漫游器(更可能产生嘈杂或无法使用的信号),我们能够对信号进行重复数据删除并将其映射到具有合理触发阈值的正确回归指标。准确且可操作的信息已添加到任务中,为用户提供了解决问题的明确指导。
健康指南针就是一个例子,去年,我们通过减少噪音和调整回归检测逻辑,成功地将有意义的行动比率提高了20%。
第一次在正确的人面前发出信号可以使修复时间减少几天。但这通常说起来容易做起来难。将任务分配给错误的所有者后,它可以在公司周围进行乒乓球运动,然后最终与可以修复该问题的人员一起着陆。在内部测试中,我们发现,对于在解决之前拥有三个或三个以上所有者的任务,在达到正确所有者之前,将近50%的任务周期时间用于乒乓球重新分配。
只需在任务中添加“我不是正确的所有者”按钮,即可获得胜利。以前,工程师必须自己寻找下一个最好的所有者,这可能会造成很大的延迟,并阻止他们继续执行新任务。现在,当工程师表明他们不是解决问题的合适人选时,所有权选择算法将根据下一个最佳预测来分配任务。随着时间的推移,该数据还被用于帮助改进我们的所有权选择算法。
更好的回归分类和根本原因分析对于解决此问题很重要。该领域最大的成功案例之一就是我们的多部门服务。 Multisect分析分支以将根本原因追溯到引入回归的特定差异。今年,我们致力于加快这一过程。结果,我们现在可以找到比以前快三倍的正确差异。
实施障碍信号,以防止阻止回归。空指针异常(NPE)是一种常见的崩溃类型,会影响Facebook应用程序家族的数亿Android用户。使Java类成为NullSafe是减少和消除这些崩溃的重要一步。
我们的数据表明,非NullSafe Java文件导致NPE崩溃的可能性是NullSafe Java文件的三倍。 NullSafe问题越多,文件与NPE崩溃相关的可能性就越大。 2020年,我们进行了静态分析,以帮助我们检测尚未使用NullSafe的代码,并为工程师提供快速解决这些问题的工具。但是,也许更重要的是,一旦类不再有NullSafe问题,它就会被@NullSafe注释标记。如果引入了任何新的NullSafe问题,这将阻止代码再次被提升。
通过实施所有这些过程,我们能够在工程生命周期的早期阶段检测出较大百分比的回归,从而减少了修复这些回归所需的总体工作。机器学习和其他技术的其他工作正在进行中,以大规模监控和完善早期检测技术的最佳组合。
我们还建立了一套可靠的指标,有助于确保传递给工程师的回归信号的质量足以使他们快速分类,诊断和修复潜在的回归,而不会因潜在的嘈杂或重复信号而使他们不知所措。在不久的将来,我们将投资能够自动调整回归检测器灵敏度的系统,而无需工程师主动监控这些指标。例如,可以暂时禁用正在产生潜在噪声或无效信号的检测器,直到工程师可以诊断问题为止,从而防止产生不必要的噪声。
最终,事实证明,这些技术不仅有益于工程师,他们减少了花费在修复回归上的时间,而且还受益于客户,他们从更少的回归和更快的修复中受益。