返回开发者新闻

A Meta developer's workflow: Exploring the tools used to code at scale

2022年11月15日发布者:Neil Mitchell

Meta 的项目规模和范围庞大,需要数以千计的开发者来处理数百万个文件。许多受小型项目青睐的工具在扩展以满足这些需求时,往往会出现故障,因此我们必须扩展现有工具或创建新工具。在本文中,我们将讨论 Meta 开发者的工作流程 — 顺序依次为代码的获取、更改、构建、测试,以及审核。本文将分享我们的开发者日常使用的一些工具。之前我们写文章介绍过很多这类工具,其中一些工具为开源软件,因此,如果您有兴趣详细了解这些工具,我们会在文章各处提供相关链接。

获取代码:借助 EdenFS 扩展版本控制

编辑代码之前,第一步是获取您计算机上的文件。虽然 Git 已是大多数情境中的标准解决方案,但我们使用的是源自 Mercurial 的自定义源代码控制方案。在将任何版本控制系统扩展到 Meta 所需的规模时,存在诸多问题,因此我们从 Mercurial 着手,并逐渐作出了很多改进。自发布那篇文章之后,我们继续开发工作,研发出了 Mononoke 服务器,它能够更快地记录和服务于代码入库 (commit) 工作。

然而,就我们存储库的规模而言,仅将必要字节写入磁盘这一项工作就会花费太长的时间。因此,我们需要使用虚拟文件系统 EdenFS。EdenFS 与使用稀疏检出具有相似的性能优势,但前者的用户体验更为优越。与稀疏检出不同,EdenFS 无需手动收集要检出的文件列表,并且用户无需更新配置文件,便可以透明地访问任何文件。EdenFS 文件系统与近期开源的 Sapling 源代码控制客户端紧密集成。阅读 Sapling 博文,详细了解 Sapling 如何令源代码控制更加人性化和可扩展。

所有这些文件均可访问之后,其他开发者工具需要了解哪些文件已更改,这一点很重要。由于文件数量众多,仅对存储库中的每个文件调用 stat 是不可行的。为解决这一问题,我们使用了 Watchman,这是可以快速检出文件更改的另一个 Meta 开源项目。Watchman 支持同时与 EdenFS(适用时)和其他地方的内核通知机制集成。如需详细了解 Watchman 如何大规模有效追踪和更新代码,请查看此视频讲解

编辑代码:自定义工具可简化规模问题

Meta 为内部开发者编辑代码提供了很多集成开发环境 (IDE)、编辑器和平台。但是,在本节中,我们仅重点介绍其中一个,即最常用于 C++ 等语言中后端服务的 IDE。

首先,开发者需要一台机器,用于编写代码。很多 Meta 开发者都拥有一台 MacBook,但实际上他们都在 Linux 服务器上进行编码。一些用户拥有专用服务器,许多其他用户则使用“按需型开发服务器”。按需型开发服务器是一种远程服务器,您可以获取并利用此类服务器编写一些代码,然后在不再使用时(通常是在当天结束时)释放服务器资源。起初,这种方法乍一听可能觉得体验并不好,就像每天都要重新配置一台新机器一样。但是,很多技术可以让体验变得顺畅起来,如永久主驱动器和可自动带您返回停止之处的源代码控制服务器。更大的优势在于,您获取的服务器现成可用,已经调取了一些常用工具,预热了缓存并做好了其他准备工作,因而能够提供比使用专用机器更快的体验。

在实际的编码过程中,很多开发者会使用 VS Code。VS Code 首先是一个本地编辑器。我们拥有大量的 VS Code 自定义扩展,这些扩展可以弥补不足,使本地 VS Code 可以打开远程服务器上的文件。我们拥有一些功能更为丰富的扩展,这些扩展可与众多功能集成,比如开发者的日历和服务中断通知,因此开发者可以一整天都处于 VS Code 中,而无需切换界面。此外,还有更多扩展可提供源代码集成、类似 IDE 的功能和 lint/格式化功能,可以在开发者编辑代码时向他们提供反馈。

说明:连接到远程服务器的 VS Code IDE,显示版本控制与一堆 diff(四条)相集成。

Meta 拥有数以百万计的源文件,因此标准的 VS Code 功能(如按文件名搜索文件和搜索源文件中的文本)所花费的时间将多得不可想象。为解决这些问题,我们推出了一些自定义工具,这些工具的性能覆盖范围囊括上述重要功能,因此,尽管工具外观和普通的 VS Code 完全一样,但内在的工作方式却迥然不同。在这两种情况下,我们都拥有可以根据源代码控制修订预计算信息的服务器,然后还有一些将本地更改纳入结果中的本地进程,使开发者可以在几秒之内在大量文件中进行搜索。

使用 Buck 构建代码

开发者的文件进入他们的计算机之后,内部开发者循环工作包括编辑和编译文件以及对结果开展实验。Meta 的大多数项目均使用 Buck 进行构建。Buck 构建系统已与 Watchman 集成,可找到已更改的文件,然后前者会结合使用远程缓存(用于下载之前构建的文件)和远程执行(用于并行构建数千个文件)。Buck 目标使用 Starlark 来指定,这是一种类似于 Python 的确定性语言。尽管 Buck 很有效,但是对于它和本文列出的所有工具,我们一直在寻找进一步改善这些工具的方法,我们还分享了自己对Buck 完善之路的想法。

说明:构建系统正在编译项目(使用由 Superconsole 产生的输出结果)。

使用手动测试和静态分析测试代码

我们希望确保所编写的所有代码都能按预期工作,而实现这一目标的两个重要方法就是编写测试和使用静态分析。

对于静态分析,由于我们拥有大量的代码,快速得出结果和确保信号优质显得非常重要。如果静态分析认为您的代码出现错误,则该代码很可能是错的。我们有一个通用的静态分析平台 Infer该平台横跨多个平台,且支持包括 Java 和 C++ 在内的多种语言。我们还构建了更多量身定制的分析工具,如 RacerD。RacerD 可以检测到 Java 并发漏洞,曾用于帮助将 Android 动态消息从单一线程转移到多线程。

我们有很多特定语言的测试框架(如用于 Javascript 测试的 Jest)和使用这些框架的很多单独测试。事实上,在某种程度上,我们拥有 太多的 测试:每当对代码的更改不可行时,系统均会运行所有相关测试。为解决这一问题,我们会采用预测性测试选择,使用机器学习模型来确定可能具有最高信号的测试,然后只运行这些确定的测试。

在静态分析和手动测试用例之间,我们会使用 Sapienz 工具来自动测试移动应用。开发者可以使用该应用假扮成用户,点击各种按钮,尝试找出崩溃之处和其他不良行为。

以成堆 diff 的形式提交代码

代码处理完成后,就可以将代码入库 (commit) 并提交了。也许 Meta 开发工具的一个独特之处在于,我们不是一次提交一个 diff,而是经常成堆地提交 diff,即一系列的更改,每个更改都是独立的,但都建立在之前更改基础之上。审核员能够根据收到的一堆 diff 单独讨论每个更改,还能正确区分重构与功能更改。

说明:Phabricator 视图:审核一堆 diff(三条),查看已更改的文件、评论、讨论和测试结果。

提交代码之后,系统会将代码转至我们的 CI 和审核工具 Phabricator(简称 Phab)。该工具可以显示评论(包括测试计划),允许开发者在一堆 diff 之间浏览,并支持您评论和审批 diff。(它甚至还支持您为那些评论点赞!)Phab 还负责运行在测试环节中确定的所有测试,并在 diff 上内联显示这些测试,以便每个人都能看到输出结果并参与讨论。

diff 获得批准后,使用“上线”按钮指示 Phab 启动 diff 落地流程。在经过一些最终完整性检查(如确保近期更改尚未损害我们分析应运行哪些测试的能力)之后,代码即可入库 (commit),并会显示在主分支上。

代码落地之后,部署流程随即启动。有关该流程的详情,请关注另一篇博文…

结论

我们希望本文能让您对 Meta 开发者工作流程的一些信息有所了解。所有这些内容均为极端规模的工作定制而成,其中很多都可以作为开源项目使用。

关于 DevInfra

从工程师结束代码的工作直到代码到达我们的应用用户处,Developer Infrastructure (DevInfra) 占据了 Meta 的大部分编码生命周期。我们的使命是提高开发者的效率,以便我们能继续快速推出优质产品。作为业界领导者之一,Meta 构建创新、可靠和快速的开发者工具和自动化基础设施,确保每一秒的工程时间都花在重要的事情上。