Formatting 25 Million Lines of Ruby: The rubyfmt Story at Stripe
Stripe recently shared its experience of formatting an entire 25 million-line Ruby codebase overnight using rubyfmt. This ambitious project highlights the complexities and considerations involved in maintaining code quality and developer productivity at an immense scale. The undertaking demonstrates a significant commitment to consistent code style, leveraging automated tooling to streamline development workflows and reduce cognitive load for engineers.
The decision to reformat such a vast codebase in a single, atomic operation, rather than incrementally, speaks to a calculated risk-reward assessment. It underscores the belief that a unified code style is paramount for a large engineering organization, even when it involves a diff so massive that GitHub struggles to render it.
The Scale of the Challenge: 25 Million Lines
The sheer size of Stripe's Ruby codebase—25 million lines—was a point of considerable discussion. Many found this figure astonishing, prompting questions about the nature and architecture of such a system.
"I’m shocked at the 25M line part! That is a completely unfathomable amount of code for one codebase. I really want to know more about that." — @varun_ch
The use of Ruby for a major financial processing company also raised eyebrows, with some expressing concern:
"A major financial processing company writes it money handling systems in Ruby. Terrifying." — @andrewstuart
This scale, however, also presents unique opportunities for tooling. As one commenter noted, compared to the data we process, code itself is often quite small, making large-scale operations feasible.
"An insight about code is that compared to the scale we operate on data, code is tiny. Instantaneous git operations and “run this tool over all the code” are the norm even while we wait for tokens to stream back. That insight might seem obvious - but if you stay cognizant of it as you work, you can invent some pretty amazing tooling for yourself & team." — @cadamsdotcom
The rubyfmt Approach: All-at-Once vs. Incremental
Stripe's choice to perform an "all-at-once" reformat over a weekend was a deliberate strategy to avoid merge conflicts. The team relied heavily on their test suite for confidence, but acknowledged the daunting nature of such a massive change.
This approach contrasts with incremental strategies, which some developers prefer for large codebases. An incremental method might involve reformatting files only when they are touched by new PRs, or in smaller batches.
"I'm surprised they went with a all-at-once reformat. Even when doing it over a weekend this is bound to mess with a lot of open PRs at their scale. I had to introduce a formatter in a few sizeable codebases in the past (few 100k to few million LOC), and I always did it incrementally via a script that reformatted all files that are not touched in any open PR. The initial run reformatted 95% of all files." — @hobofan
The benefit of the big-bang approach is immediate consistency across the entire codebase, eliminating the transitional period where some files are formatted and others are not. The rubyfmt tool itself is written in Rust, a common choice for performance-critical developer tooling.
Ensuring Correctness: The Sanity Check
A critical aspect of any automated formatter is ensuring it only changes whitespace and never alters the code's semantics. The Stripe team's confidence in rubyfmt was bolstered by robust testing. The Dart formatter, for instance, employs a strict internal sanity check:
"The dart formatter has an internal sanity check. It walks through the unformatted and formatted strings in parallel skipping any whitespace. If any non-whitespace characters don't match, it immediately aborts. This ensures that the only thing the formatter changes is whitespace, and makes it much less spooky to run it blind on a huge codebase." — @munificent
This kind of safeguard is invaluable, especially when dealing with complex language features or new syntax, as it prevents subtle bugs from creeping into the codebase due to formatting errors.
The Philosophy of Code Formatting
The effort put into formatting sparked a debate about its ultimate value, particularly in an era where AI is increasingly involved in code generation.
"What is even the point of formatting code anymore." — @throwatdem12311
"Surely, it no longer needs to be human-readable, and the era of write-only code is finally upon us with the dawn of AI writing our mealtickets. Why bother formatting 25m lines of slop, and why is AI wasting tokens on making code look human-readable anyway?" — @CrzyLngPwd
However, the human element of code readability remains crucial for collaboration and maintenance. A humorous anecdote highlighted the tension between personal preference and team standards:
"The lead developer didn't like to bother with formatting code, so I wrote a tool called makenice to format his nasty spaghetti gibberish into something with good indents and layout to make it easier for us normal people to parse. He was furious... so I wrote makenasty to format code into the way he appeared to like. I only shared makenasty/nice with a couple of the team, who loved it, as it allowed easy conversion between something readable and something the team lead like." — @CrzyLngPwd
This illustrates that while formatting might seem like a minor detail, it significantly impacts developer experience and team cohesion. The future might even see a shift away from text-based formatting entirely, towards storing and manipulating code as parse trees:
"Really reminds me that there's nothing in principle stopping us from storing parse trees and exposing them via something git like so we can avoid even needing to format, let alone also needing to resolve a whole category of merge conflicts based on that formatting. I mean a format is just a theme over your data -- I mean code." — @eigenblake
Strategic Tooling and Productivity
The rubyfmt story is a testament to the value of investing in developer productivity tools. By tackling the most complex syntax first, the rubyfmt team adopted a sound strategy for building a robust formatter.
"Given that complexity, the hypothesis was simple: tackle the hardest syntax first and the rest will follow. Always nice to see. I've seen people fall into the trap of designing for the common case, not realizing most of the code will be to deal with the less common cases." — @nitwit005
This approach ensures that the tool is capable of handling the entire spectrum of code it will encounter, providing a reliable and consistent output. The ability to automatically enforce a consistent style across millions of lines of code frees developers from manual formatting concerns, allowing them to focus on logic and functionality.
Stripe's successful overnight reformat of its massive Ruby codebase with rubyfmt stands as a significant achievement in developer tooling and large-scale code management. It underscores the importance of consistent code style, the power of robust automated tools, and the strategic decisions required to maintain high productivity and code quality in an expansive engineering environment.