格式化 2500 万行 Ruby 代码:Stripe 的 `rubyfmt` 故事
Stripe 最近分享了他们使用 rubyfmt 在一夜之间格式化整个 2500 万行 Ruby 代码库的经验。这一雄心勃勃的项目突显了在极大规模下维护代码质量和开发者生产力所涉及的复杂性和考量。这项工作展示了对统一代码风格的重大承诺,利用自动化工具来简化开发工作流程并减轻工程师的认知负担。
决定在一次原子操作中重新格式化如此庞大的代码库,而不是增量进行,体现了一种经过计算的风险回报评估。它强调了一个信念:对于大型工程组织来说,统一的代码风格至关重要,即使这涉及到一个大到 GitHub 都难以渲染的 diff。
挑战的规模:2500 万行
Stripe Ruby 代码库的庞大规模——2500 万行——是一个引起广泛讨论的话题。许多人发现这个数字令人震惊,引发了关于此类系统性质和架构的问题。
"我对这 2500 万行的部分感到震惊!对于一个代码库来说,这是一个完全无法想象的代码量。我真的很想了解更多相关信息。" — @varun_ch
一家大型金融处理公司使用 Ruby 也引起了关注,一些人表达了担忧:
"一家大型金融处理公司用 Ruby 编写处理资金的系统。太可怕了。" — @andrewstuart
然而,这种规模也为工具提供了独特的机会。正如一位评论者所指出的,与我们处理的数据规模相比,代码本身通常非常小,这使得大规模操作变得可行。
"关于代码的一个洞察是,与我们处理数据的规模相比,代码是微小的。即使我们在等待 token 流回时,即时的 git 操作和“对所有代码运行此工具”也是常态。这个洞察可能看起来显而易见——但如果你在工作时能意识到这一点,你就可以为自己和团队发明一些非常神奇的工具。" — @cadamsdotcom
rubyfmt 方法:一次性 vs. 增量
Stripe 选择在一个周末进行“一次性”重新格式化的决定,是为了避免合并冲突而采取的刻意策略。团队在测试套件的基础上获得了信心,但也承认了如此大规模变更带来的艰巨性。
这种方法与一些开发者在处理大型代码库时更倾向的增量策略形成对比。增量方法可能涉及仅在新的 PR 触及文件时才对其进行重新格式化,或者分较小的批次进行。
"我很惊讶他们选择了全量重新格式化。即使是在周末进行,在他们的规模下这也肯定会干扰许多开启中的 PR。我过去曾在几个规模较大的代码库中(几万到几百万行)引入过格式化工具,我总是通过脚本增量进行,重新格式化所有未在任何开启中的 PR 中被触及的文件。初始运行重新格式化了 95% 的文件。" — @hobofan
这种“大爆炸”式方法的优点是整个代码库实现了即时一致性,消除了部分文件已格式化而其他文件尚未格式化的过渡期。rubyfmt 工具本身是用 Rust 编写的,这是对性能要求极高的开发者工具的常见选择。
确保正确性:完整性检查
任何自动化格式化工具的一个关键方面是确保它只改变空格,而绝不改变代码的语义。Stripe 团队对 rubyfmt 的信心得到了强大测试的支撑。例如,Dart 格式化器采用了严格的内部完整性检查:
"dart 格式化器有一个内部完整性检查。它并行遍历未格式化和已格式化的字符串,跳过任何空格。如果任何非空格字符不匹配,它会立即中止。这确保了格式化器改变的唯一内容是空格,使得在庞大的代码库上盲目运行它变得不再那么可怕。" — @munificent
这种类型的保障是无价的,尤其是在处理复杂的语言特性或新语法时,因为它能防止由于格式化错误而导致微妙的 bug 潜入代码库。
代码格式化的哲学
投入在格式化上的精力引发了关于其最终价值的辩论,特别是在 AI 越来越多地参与代码生成的时代。
"格式化代码的意义到底在哪里。" — @throwatdem12311
"当然,它不再需要是人类可读的,随着 AI 开始为我们写账单的黎明到来,‘只写不读’的时代终于到了。为什么要费劲去格式化 2500 万行的废料,而且 AI 为什么要浪费 token 让代码看起来像人类写的呢?" — @CrzyLngPwd
然而,代码的可读性这一人为因素对于协作和维护仍然至关重要。一个幽默的轶事突显了个人偏好与团队标准之间的紧张关系:
"首席开发人员不喜欢费劲去格式化代码,所以我写了一个名为 makenice 的工具,将他那糟糕的意大利面式乱码格式化成具有良好缩进和布局的内容,以便我们普通人解析。他非常愤怒……所以我写了 makenasty,将代码格式化成他看起来喜欢的方式。我只向团队中的几个人分享了 makenasty/nice,他们非常喜欢,因为它允许在可读内容和团队负责人喜欢的内容之间轻松转换。" — @CrzyLngPwd
这说明虽然格式化看起来像是一个小细节,但它会显著影响开发者体验和团队凝聚力。未来甚至可能会看到从基于文本的格式化完全转向将代码作为解析树进行存储和操作:
"这真的让我想起,原则上没有什么能阻止我们存储解析树并通过类似 git 的方式公开它们,这样我们甚至不需要格式化,更不用说还要解决一整类基于格式化的合并冲突了。我的意思是,格式只是你数据(我的意思是代码)之上的一个主题。" — @eigenblake
战略性工具与生产力
rubyfmt 的故事证明了投资于开发者生产力工具的价值。通过首先解决最复杂的语法,rubyfmt 团队采取了一种构建强大格式化器的稳健策略。
"考虑到复杂性,假设很简单:先解决最难的语法,其余的自然会随之而来。这总是令人欣慰的。我见过有人陷入了为常见情况设计而忽略了大部分代码将是处理非常见情况的陷阱。" — @nitwit005
这种方法确保了工具能够处理它将遇到的所有代码范围,提供可靠且一致的输出。自动在数百万行代码中强制执行统一风格的能力,将开发者从手动格式化的顾虑中解放出来,让他们能够专注于逻辑和功能。
Stripe 使用 rubyfmt 在一夜之间成功重新格式化其庞大的 Ruby 代码库,这在开发者工具和大规模代码管理方面是一项重大成就。它强调了统一代码风格的重要性、强大自动化工具的力量,以及在扩张的工程环境中保持高生产力和代码质量所需的战略决策。