持续交付2.0

谷歌:使用机器学习,自动修复失败的构建

Image

关注我,每天收获一个新技能!

原文发表于 2024年3月

原文链接:https://research.google/blog/safely-repairing-broken-builds-with-ml/

1

引言
自动修复非建筑代码可以提高生产率(以总体任务完成度来衡量),而且只要采用高质量的训练数据和负责任的监控,似乎不会对代码安全产生可察觉的负面影响。

软件开发是一个设计、编写、构建、测试、部署和调试的循环过程。如果您曾经解决过一个构建错误,却又引入了 20 多个错误,那么您一定很熟悉在陌生的代码中查找类型不匹配或使用一些新 API 诊断错误位置的挫败感。

在 Google,每次保存源代码文件时,代码快照都会保存到版本控制库中。每次构建运行时,都会保存构建日志。这是一个数据宝库!我们可以确定构建何时中断、出现了哪些错误消息、构建何时成功以及哪些代码发生了更改 — 本质上,就是开发人员如何修复构建。

下面,我们将介绍如何训练DIDACT ML 模型来预测构建修复。DIDACT是一种将整个软件开发过程用作训练数据的方法。使用这个专用的构建修复 ML 模型,我们在 IDE 中展示了修复,并针对此功能进行了受控实验。

该实验表明,在多个生产力指标上都取得了统计上显著的提升,包括代码更改数量增加了 2%。这些提升不仅本身是好事,而且还通过自动化消除了开发人员的辛劳,让开发人员有更多时间进行创造性的问题解决。

这能通过消除障碍来促进专注,从而使开发人员更长时间地处于心流状态。

事实上,我们现在观察到,在遇到构建中断的用户中,约有 34% 最终应用了此类 ML 建议的修复。我们还发现,在应用 ML 来生成的修复代码,并没有明显增加安全风险或错误。

ML 驱动的构建修复现已面向所有 Google 开发人员启用,最近在 Google Cloud Next 上亮相。

如上所示,我们的构建修复模型正在接受训练并建议修复代码,该模型的概述如下。

预先训练的 DIDACT 模型会根据开发人员的构建错误及其修复记录进行微调。然后,该模型会用于在 IDE 中实时建议修复开发人员的构建。

2

问题与动机

我们的抱负源于希望改善开发人员修复Java构建错误的体验。我们能否让错误消息更具可操作性,甚至自动修复错误?我们利用 Google 对其开发人员代码的全面记录和 Google 的研究专业知识,使用 ML修复 损坏的构建 。

构建错误并不全是简单的缺少括号、拼写错误或哎呀我又忘记了我的依赖项。来自泛型或模板的错误可能很复杂,错误消息可能很隐晦。在 Google,构建不仅仅是编译。多年来,我们做出了很多努力来“左移”开发生命周期中可能发生的几种错误,尽早发现它们,通常是在构建时(即在代码编写的早期阶段)。例如,每次构建都会运行一组精选的易错 静态分析检查器。构建错误和一些静态分析检查会阻止代码更改的提交。我们看到我们的方法可以解决这些更复杂的问题,因此我们将修复范围扩大到其他类型的错误,然后是包括 C++ 在内的其他语言以及更多的环境。

3

训练和输入数据

为了从 Google 的开发历史中生成训练数据,我们组织了“解决会议”。这些是按时间顺序排列的代码快照和构建日志,它们出现在同一个工作区中,捕捉从第一次发生故障到问题解决期间代码的变化。

Image

对同一工作区内随时间推移发生的一系列四次构建中的两次解决会话进行细分。

DIDACT 针对解决会话的第一个快照中出现的内部代码库中的构建错误、同一损坏快照中的代码状态(即文件内容)以及损坏快照和修复快照之间的代码差异进行训练。

在服务时,DIDACT 输入是当前代码状态和在该状态下发现的构建错误。然后,DIDACT 预测要应用于代码的补丁(具有置信度分数)作为建议的修复。

4

筛选建议的修复方案
以确保质量和安全

业界已经看到,代码生成 ML 模型可能会引入一些开发人员无法发现的错误,因此使用 ML 生成的修复确实有可能使代码变得更糟。为了维护 Google 代码库的质量和安全性(以及我们开发人员的信誉),我们对 ML 生成的修复应用了后处理:自动格式化,然后使用我们结合专业知识和用户反馈设计的启发式过滤器,以避免常见的质量和安全陷阱,例如代码漏洞。

5

界面操作流程展示

开发人员可以在修复可用时立即看到修复程序、预览它,并可以选择接受或拒绝它。

在 IDE 中应用构建修复的记录。当开发人员在编码时遇到构建错误时,系统会向他们显示“查看 ML 建议的修复”按钮。单击该按钮后,系统会显示建议修复的预览,然后他们可以应用或放弃该修复。在本例中,开发人员应用了修复,新代码构建成功。

通过质量和安全过滤的修复将在 IDE 中呈现给用户,如果被接受,开发将照常继续 — — 构建、测试、静态和动态分析以及代码审查的标准流程将继续。

6

AB实验

我们随机将 50% 的 Google 开发人员分配到控制组:他们可以访问 IDE 中的 ML 构建修复程序。另外 50% 被分配到对照组。我们对这两个组进行了 11 周的监测,然后比较了结果。

7

提高生产效率

在评估生产力影响时,我们调查了与速度和效率相关的指标。我们的结果显示出统计上显著的结果:

  • 每个变更列表(CL)的活动编码时间减少约 2% :在发送审核之前,在 CL 上“用手指在键盘上”工作的平均时间,包括编码和密切相关的活动。

  • 每个 CL 的指导时间减少约 2% :发送审查后开发 CL 所花费的平均时间,包括处理代码审查反馈。

  • CL 吞吐量增加约 2%:每周提交的 CL 平均数量。

这些发现表明,ML 构建修复可帮助开发人员更快地解决构建失败等开发障碍,因此他们可以专注于每个 CL 的整体目标。

换句话说,ML 构建修复可以帮助开发人员保持流畅。

此外,CL 吞吐量增加 2% 表明开发人员不仅输入了更多代码;事实上,他们现在完成了更多已提交、经过测试的工作单元。

我们猜测,开发人员意识到提高生产力不仅仅来自诸如缺少分号之类的琐碎构建修复,否则他们就不会投入额外的时间来审查建议。

相反,开发人员可能会更有效地解决更复杂的构建失败或静态分析失败。示例包括复杂的C++ 模板问题和lambda,或复杂的并发Java API。

我们假设开发人员最终会审查并修改针对此类失败的 ML 建议,而不是不得不离开 IDE 去寻找可能的答案,从而中断他们的编码流程。

8

安全性保证

单纯提高开发速度并不一定更好。我们还必须确保 ML 生成的代码是高质量且安全的。

为了评估通过修复引入不正确甚至危险的代码的风险,我们回顾性地检查了与安全性和可靠性相关的指标,包括:

  • CL 回滚率:生产中的问题通常通过回滚引起错误的罪魁祸首 CL 来解决。

  • 新清理程序失败率:Google 运行的清理程序可以检测并标记单元测试和模糊目标中的内存损坏或泄漏等问题;例如AddressSanitizer,MemorySanitizer和GWP-Asan。

我们对借助构建修复编写的 CL 与未借助构建修复编写的 CL 之间的这些指标进行了监控,没有发现可察觉的差异。

9

结论

在 IDE 中呈现 ML 构建修复,并由自动安全检查和人工审核保护,显著提高了开发人员的工作效率,而不会对代码安全产生负面影响。


这支持了我们的直觉,即在软件开发过程中利用 ML,即使是在易于理解的问题上,也可以减少辛劳,并让开发人员能够更有效地解决高阶问题。

此外,类似的方法可能在整个开发周期中有效解决其他类型的错误和工程任务。这些结果证明了 ML 可以有效且安全地提高开发人员的工作效率、专注度和代码质量。

下面是另一个 机器学习辅助提升软件工程师提高效率的视频。

欢迎关注视频号《持续交付2.0》,获得更多相关资讯。