BUILDING TO SCALE
FR | EN
Talk about your project

Upgrading Ruby on Rails: What Breaks and How to Avoid It

Most painful Rails upgrades fail on the same few things: skipped versions, thin test coverage and incompatible gems. Here is a safer way to approach one.

Published October 24, 2026 · 5 min read

A Rails upgrade rarely fails because of one dramatic incompatibility. It fails because several small things pile up: a skipped version, a gem that stopped being maintained, a test suite that cannot tell you whether anything broke. Here is the approach we use to keep an upgrade boring.

Upgrade one minor version at a time

Jumping from an old version straight to the latest feels efficient and is the most common cause of a stalled upgrade. Each minor release deprecates behavior that the next one removes. Going one step at a time means every deprecation warning you fix is still relevant, and every failure points to a small, recent change.

Make the tests trustworthy first

Before touching the Gemfile, check that the test suite is green and that it covers the paths that matter: sign-in, payments, background jobs, the main write paths. If coverage is thin, spend the first days adding characterization tests around the riskiest code. An upgrade without that safety net is a gamble you only discover you lost in production.

Run both versions side by side

A dual-boot setup lets the same application run against the current and the next version of Rails, so you can fix incompatibilities on the main branch, in small commits, while production stays on the old version. A conditional in the Gemfile is enough to start:

if ENV["NEXT_RAILS"] == "1"
  gem "rails", "~> 7.1.0"
else
  gem "rails", "~> 7.0.0"
end

Run the suite in both modes in your CI. The branch that is green on both can be merged continuously, and the final switch becomes a one-line change.

Read the deprecation warnings before upgrading

Most breaking changes announce themselves one version early. Enable deprecation reporting in test and development, collect the warnings, and fix them while still on the old version. The upgrade itself then removes very little that you were still using.

Expect trouble from gems, not from Rails

The framework is usually the easy part. The hard part is a gem that pins an old version of Rails, was abandoned, or monkey-patches internals. For each dependency, check whether a compatible release exists, whether a maintained alternative exists, or whether the feature is small enough to own. Do this inventory at the start: it is what decides the real schedule.

Move framework defaults separately

Running the update task generates initializers that hold the new framework defaults for the target version. Resist the urge to flip all of them at once. Keep the old defaults, ship the version upgrade, and then enable the new defaults one at a time, each with its own deploy. If something behaves differently, you know exactly which setting caused it.

Plan the Ruby upgrade as its own step

Ruby and Rails versions are linked, but they are two different risks. Upgrade Ruby when the current Rails version supports it, ship it alone, and only then move Rails. Two small changes you can roll back separately are safer than one large one you cannot.

Roll out like any other risky change

Deploy to a single environment first, watch error tracking and performance for a day, and keep the previous release one command away. Database migrations that come with the upgrade should follow the same rules as any other: no long locks, no changes that block the previous version, as described in our article on zero-downtime migrations.

An upgrade is easier with a clear picture of where you start. A short Rails audit gives you the dependency inventory, the test gaps and a realistic order of work before you commit to a schedule.