什么时候是开始使用 Rust 的好时机?
Convex 的创始团队有幸领导了世界上一些最常用的基于 Rust 的系统的开发:
- Magic Pocket,Dropbox 的地理分布式数据存储系统。该系统已在近百万个并发存储节点上运行,并直接管理 EB 的磁盘存储,所有这些都没有发生任何重大事件。
- Nucleus,Dropbox 同步引擎的全新改写。该代码库在超过 50 亿台设备上运行,管理数万亿个文件,应对各种奇怪的最终用户文件系统配置,并具有无可挑剔的可靠性记录。
- Convex,零设置、无限可扩展的后端,专为响应式应用程序开发人员的需求而设计。好的,这还不是最常用的代码库之一(目前)。这是我们现在正在建设的。
为什么要切换?
如果您正在阅读本文,您可能不需要对 Rust 的优势有太多的说服力。Rust 一直是我们所从事的一些项目取得成功的重要贡献者。
- 性能
- 正确性
Nucleus 项目大量利用代号为 Trinity 的测试框架,该框架确定性地验证了执行线程复杂交错的不变量。Rust 对期货的支持以及实现我们自己的对抗性调度程序的自由使这个项目成为可能,而在另一种语言中,尤其是具有语言运行时的语言,它会困难得多。
我们将继续推进 Convex 测试分布式和并发系统的最新技术。
- 生产率
Rust 库也往往具有极高的质量,Cargo 提供的构建系统使处理大型 单体仓库monorepos 变得轻而易举。我们承认,我们已经浪费了无数时间来处理 npm 中的构建系统异常或与 Bazel 搏斗,而 Cargo 根本不是这种情况。
用 Rust 编程的一个潜在的意想不到的好处是,它实际上让我们成为了更好的原型!过去,在用 Go 重写用于生产之前,我们已经用 Python 等语言构建了分布式系统的原型。拉斯特重构是如此容易得多,也使我们能够发展成原型完全成熟的生产系统,而无需重写。重构通常就像进行更改然后追踪编译器错误的尾部一样简单,直到它完成。Rust 拥有我们使用过的任何语言中最有用的编译器建议。
- 多功能性
在其他语言中嵌入 Rust 很容易,但在 Rust 中嵌入其他语言也很容易。Convex 允许客户将数据库查询表达为成熟的 JavaScript/TypeScript 函数,这些函数在我们的 Rust 后端的 V8 隔离中执行。我们的团队对WASM以及 Rust 与 WASM 团队之间的密切联系感到特别兴奋。我们期待着在 Convex 中安全嵌入其他语言的未来机会。
什么是工作的最佳工具
大公司经常发生的争论之一是对一致性的渴望,还是让工程师使用最好的工具来完成工作。想要使用可以让您个人更有效率的语言或加入热门新事物是很容易的。不幸的是,这通常以组织成本为代价。本地决策可能对工程产生广泛的影响,通常您的代码在您离开后仍然存在很长时间。可维护性通常也比大型项目的初始开发工作更重要。这种紧张关系一直是我们团队对以前项目的争论的根源。
- 开发者可替代性
入门问题的一个诱人替代方案是分拆一个由崭露头角的 Rust 专家组成的小型子团队,负责处理需要 Rust 性能或类型安全级别的关键任务组件。这是我们在 Magic Pocket 项目中犯的一个错误,少数工程师用 Rust 重写了存储引擎,而团队的其他成员继续用 Go 构建系统的其余部分。当 Rust 组件是一个小型 skunkworks 项目时,这种拆分是必要的,但它在此后持续了多年。
Rust 组件是他们自己成功的牺牲品——它们运行良好,不需要太多维护,当最初的作者离开团队去开发 Nucleus 时,已经没有多少工程师留下来成为这些方面的专家——大型系统。这在一段时间内减缓了 Rust 组件的创新,并让剩下的团队对 Rust 的使用感到复杂。
当我们开始 Convex 时,我们发誓不再重复这个错误,并确保所有工程师都在 Rust 上工作并掌握该语言。
- 作为组织负担的库包支持
在项目的边界上,事情稍微复杂一些。大公司有数百个相互通信的内部系统:监控、RPC 框架、发布过程、身份验证等。工程师需要将这些现有子系统移植到每种新的支持语言。Nucleus 的原型 API 服务器 Tomahawk 最初是用 Rust 重写的。Nucleus 团队承担了在 Tomahawk 内部实施内部系统支持的重担。最终,移植整个生态系统的工作量太大,所以我们用 Go 重写了 Tomahawk,Go 是公司事实上的后端语言。有时,移民的障碍实在是太高了。
在 Convex,我们可以从一开始就让事情变得干净:所有后端代码都在 Rust 中;所有前端代码都在 Typescript 中;操作工具主要使用 Python。作为一家初创公司,我们需要专注于完成工作,而不是支持多种语言。
- 迁移后
当一个团队对另一个团队提出功能请求时,他们自己介入并进行更改也很常见。通过简单的代码审查而不是一系列 Jira 任务来协调更改通常要快得多,而且希望该功能的团队投入时间来实现它是公平的。当团队之间存在(编程)语言障碍时,这是一个重大挑战。
初创公司不能在团队之间建立人为的障碍。我们在 Convex 做出了有意识的努力,使代码库对所有开发人员来说尽可能平易近人,并确保所有开发人员都拥有足够的专业知识,可以在需要工作的任何地方工作。
引入Rust注意点
我们是 Rust 的大力倡导者,并从该语言中受益匪浅,但必须谨慎地将新语言引入组织。
对于新团队
- 首先决定你是否真的想使用 Rust。你会从类型安全和性能中受益吗?惊人的。您是否主要是一个前端商店,其 IO 绑定的工作负载无论如何都会执行大致相同的工作?也许你最好使用像 Go、Java 或 Kotlin 这样的垃圾收集语言。
- 接受会有加速的时间。不管人们告诉你什么,像 Rust 这样的语言需要相当长的时间才能变得富有成效。确保你的团队中已经有一些专家可以帮助其他人提升而不是同时放慢整个团队的速度。当没有现有水平的 Rust 专业知识作为指导时,我们已经看到项目进展不佳。
- 决定您将拥有多少种语言,并负责长期支持它们。意识到以多种语言维护库和操作系统的开销。
- 愿意说不。有时,正确的解决方案就是凑合你已经拥有的东西。
- 决定是否值得重写或使用新语言。仅仅重构现有的代码库就足够了吗?您是否确定了真正重要的热点并尝试优化它们?您对您想要进行的更改为客户和开发人员带来的好处有信心吗?
- 从一个不在关键开发路径上的较小的子项目开始。这将使您能够灵活地推迟时间表并适应意外的互操作性问题。
- 一旦较小的项目取得成功,请确保整个团队都在努力——您不希望专业知识分散在少数工程师之间。
- 如果一切顺利,承诺在整个公司更广泛地传播这种语言。
- 承担建立邻近系统支持的个人责任。仅仅告诉监控团队为您构建一套全新的客户端绑定可能是不行的——您可能必须自己引导这项工作。
- 确保核心系统的持续开发。核心系统需要一支工程师团队,他们能够在出现问题时进行创新或干预。即使没有紧迫的功能需求,也总是有改进的地方。