THE MIGRATION LINE
INCIDENT // GITHUB-2020SHIPPED

GitHub fixed over 11,000 deprecation warnings across 400,000 lines of code to reach Ruby 2.7 — because 2.7 was the only door to Ruby 3.

GITHUB·2020·RUBY / LANGUAGE / UPGRADE / DEPRECATION·✓ Sourced — github.blog
System stress over timeBreach at T+3
11,000+Deprecation warnings
~90s → ~70sBoot time
40Teams involved
~2 hours, zero downtimeDeploy window
BASELINE

Four hundred thousand lines, one Ruby version behind

GitHub runs on a Ruby on Rails application of more than 400,000 lines of code, worked on by enough people that hundreds of pull requests merge every day. In 2020 it ran on Ruby 2.6, and the team wanted it on Ruby 2.7.

The reason wasn’t that 2.7 was faster, though it turned out to be. The reason was Ruby 3.0. Ruby Core had deprecated the old way keyword arguments were handled, and 2.7 was the release that warned you about it. Skip 2.7 and you don’t skip the work — you just meet it later, all at once, at the door of a version you can’t decline forever.

COMPLICATION

Eleven thousand warnings, and nobody's name on them

Running the application under 2.7 produced more than 11,000 deprecation warnings. Each one marked a place where code passed a hash where Ruby 3 would expect keyword arguments, or the reverse — individually trivial, collectively a wall.

QUESTION

How do you upgrade a language under a thousand engineers who won't stop shipping?

The obvious approach is a long-running branch: fork the codebase, fix everything on the fork, merge when green. On an application where hundreds of PRs merge a day, that branch starts rotting the moment you cut it.

So the question GitHub actually had to answer wasn’t “how do we fix 11,000 warnings.” It was: how do we fix them on the main branch, in production, while everyone else keeps shipping — and how do we make sure the count only goes down?

ANSWER

Dual-boot, then make the warnings find their owners

The answer had two halves. First, dual-boot: the application was configured to run on both Ruby 2.6 and 2.7, selected by an environment variable. Every fix had to be backward-compatible with 2.6, which meant every fix could merge straight to main. No fork, no rot.

Second, they made the warnings self-addressing. The team monkey-patched Ruby’s Warning module to capture each deprecation into a WarningsCollector, then used CODEOWNERS to route every warning to the team that owned the file — generating issues that named the file path, the exact warning text, and the test suite that tripped it. Because each warning now arrived with somebody’s name on it, the wall became a queue, and the queue drained over several months.

Along the way the fixes went upstream too: GitHub sent patches to the gems that were emitting warnings, including Rails, Capybara, factory_bot, view_component, rails-controller-testing, posix-spawn, github-ds, and ruby-kafka.

ROLLOUT

Months of work, deployed in two hours

The deploy was deliberately unheroic. Ruby 2.7 went to 2% of traffic first — which immediately surfaced a frozen string exception. They rolled back; fewer than 10 users on a single endpoint were affected. They fixed it, went back to 2%, waited 15 minutes, then 30% of Kubernetes partitions, then 60%, then the non-Kubernetes partitions, then everything, with a 30-minute watch at the end.

Total time to put months of work into production: about two hours, with zero downtime. Boot time in production fell from roughly 90 seconds to roughly 70, and object allocations dropped from about 780,000 to about 668,000.

TAKEAWAY

What the warnings were really for

The performance win was real but incidental. What GitHub actually bought was position: an application that could now walk into Ruby 3.0 instead of being dragged. The upgrade paid for itself in upgrades it made cheap.

A deprecation warning is a bill with a due date; the only question is whether you pay it in installments on your schedule, or in full on someone else’s.

Source — read the original

A plain-language, AI-drafted and human-edited retelling of the article published on github.blog, reorganized and explained in our own structure and words, with original analysis in the editor's note above. The facts, numbers, and decisions belong to the original author and are not altered. For the full depth, read the source.

← All systems