Rails 6 to 7 migration across every repository

How to Run a Rails 6 to 7 Migration Across Every Repo

Matthew Holmes

Matthew Holmes

July 30, 2026 · // 9 min read

A Rails 6 to 7 migration usually starts on schedule. The flagship monolith gets a named owner, two sprints of budget, and an upgrade branch that merges roughly when planned. The other twenty-odd Rails services get a row in a tracking doc.

Those other services are the ones that show up in an audit. The payments reconciliation worker. The internal admin app. The three API services extracted in 2022 and barely opened since. Each one carries its own Gemfile, its own accumulated config/application.rb overrides, and its own quiet dependence on Rails 6 default behaviour. bin/rails app:update is interactive and produces a different diff in every repo, so the work gets handed to whichever team owns the service and scheduled against that team’s roadmap.

Rails 6.1 reached end of life on October 1, 2024. Every service still sitting on it has gone almost two years without an upstream security patch.

A Rails 6 to 7 migration requires five changes in every repository: bump rails and its adjacent gems, apply the new framework defaults, move the application onto the Zeitwerk autoloader, replace the APIs Rails 7 deleted, and raise the Ruby runtime. Each of the five is well documented. Repeating all five across thirty services in a single quarter is where fleet-wide upgrades come apart.

What Rails 7 actually breaks

The classic autoloader does not exist anymore. Rails 6.0 and 6.1 shipped both autoloaders to ease the transition. Rails 7 removed classic mode and deleted the config.autoloader= setting along with it. A repo whose filenames and constants disagree with Zeitwerk’s convention fails at boot instead of failing a test. Multi-constant files, acronym directories like app/services/api, and require_dependency calls inside initializers all surface at this point.

Framework defaults arrive as a separate, flippable file. Setting config.load_defaults 7.0 turns on the whole set at once. The generated new_framework_defaults_7_0.rb initializer exists so each default can be adopted one at a time. These defaults change runtime behaviour rather than API surface, which is why a green test suite is weak evidence that the upgrade is done.

The asset pipeline splits into two viable paths. Sprockets still works in Rails 7. Propshaft is the lighter option and performs no preprocessing, so it breaks any app that compiles Sass or embeds ERB inside an asset. That decision belongs to each repo, and forcing one answer across the fleet creates rework.

Some APIs are simply gone. ActiveRecord::Base.connection_config becomes connection_db_config, the private ActiveSupport::Dependencies interface was deleted, and ActionController::Parameters#to_h now expects permitted parameters. Individually these are search-and-replace edits. They appear in a different combination in every service.

The runtime moves too. Rails 7.2 requires Ruby 3.1 or newer, and Rails 8.x requires Ruby 3.2 or newer. Ruby 3.1 reached end of life in March 2025 and Ruby 3.2 followed on March 31, 2026, so any repo landing on a modern Rails also needs a runtime bump that CI, Docker images, and deploy tooling have to agree on.

Why fleet-wide Rails upgrades stall

The per-repo engineering is understood well enough to write down. What does not fit on a page is the part where thirty repos require thirty scheduling conversations with teams that have their own commitments. So the migration guide gets written, a tracking issue gets opened, and the platform team spends two quarters following up.

A platform engineer at a 600-repo organization described the shape of it this way: “We’ve got over 600 repositories, and it’s no joke to go back and upgrade libraries like Log4j or Spring Boot every couple of weeks.”

The Rails 7 target has already expired

Here is where every Rails series stands as of July 2026.

SeriesSupport status
Rails 6.1End of life, October 1, 2024
Rails 7.0End of life, April 2025
Rails 7.1End of life, October 1, 2025
Rails 7.2Security fixes end August 9, 2026
Rails 8.0Bug fixes ended May 2026, security fixes end November 7, 2026
Rails 8.1Bug fixes until October 2026, security fixes until October 2027

A Rails 6 to 7 migration finished today lands on a series that stopped receiving security fixes in October 2025. Rails 8.1 is the only series still receiving bug fixes.

That reframes the goal. Rails 7 is a waypoint on the path to a supported release, and the useful question is how fast an organization can move its entire fleet one major version, because that number determines whether it ever catches up. Ruby 3.4 is the runtime worth targeting on the way, with support through March 2028.

How to run a Rails 6 to 7 migration across every repository

  1. Inventory first. Enumerate every repo with a Gemfile.lock pinning Rails 6.x, and record the Ruby version, autoloader mode, and asset pipeline for each one.
  2. Switch to Zeitwerk while still on Rails 6.1. Set config.autoloader = :zeitwerk, rename files and constants to match, and ship it as its own PR. This removes the largest boot-time failure mode from the version bump itself.
  3. Bump one minor at a time. Go 6.1 to 7.0 to 7.1, clearing deprecation warnings at each stop before moving on.
  4. Raise Ruby before Rails where the gem stack allows. A runtime bump is the lower-risk change and it unblocks the later Rails versions.
  5. Adopt framework defaults deliberately. Load the new defaults, then override the individual ones that would change existing behaviour, each with a dated TODO.
  6. Gate on more than a green suite. Default changes show up as production behaviour, so require a smoke test on a deployed staging environment per service.
  7. Track the fleet, not the repo. Status belongs in one place: which services are merged, which are blocked, which have not started.

Steps one through six are per-repo craft. Step seven is where the calendar is actually spent.

Where automation changes the shape of the work

Tidra is an AI coding agent for both implementation and coordination of code changes across your organization. For a Rails major-version migration, that means one change plan, generated code in every targeted repo, and a single view of which pull requests have merged.

The plan is described in plain language and reviewed before any code is generated. A Rails 6 to 7 initiative reads roughly like this:

Migrate every Rails 6.1 service to Rails 7.1 as the first hop toward 8.1.
Assume Zeitwerk is already enabled; skip any repo still in classic mode.

For each repository:
1. Set `gem "rails", "~> 7.1.0"` and align sprockets-rails, puma, bootsnap,
   and selenium-webdriver to Rails 7-compatible versions.
2. Apply the `bin/rails app:update` framework-default diffs to
   config/application.rb, config/environments/*.rb, and
   config/initializers/new_framework_defaults_7_1.rb.
3. Replace deleted APIs: connection_config -> connection_db_config,
   ActiveSupport::Dependencies.constantize -> String#constantize,
   and add permit calls where ActionController::Parameters#to_h requires them.
4. Set `config.load_defaults 7.1`, then override individual defaults that would
   change existing behaviour, each with a dated TODO.
5. Update .ruby-version and .github/workflows/*.yml to Ruby 3.4, and leave the
   Dockerfile base image unchanged if it is managed centrally.
6. Run the test suite and report remaining failures per repo without fixing them.

Every change lands as a pull request the owning team reviews. Repos that need different answers — the Propshaft question, a gem with no Rails 7 release — come back as exceptions to handle by hand rather than as silent failures. One head of platform engineering put it plainly: “We have a backlog of changes we never completed, now we can get them done.”

See how Tidra handles code migrations at scale.

What automation will not do for a Rails upgrade

Zeitwerk naming is the honest limit. Where two constants legitimately want the same filename, or where a service loads code through a custom inflector, the rename is a judgment call about the domain model and it needs a human. Gem replacements that carry data are the other one. Moving off Paperclip to ActiveStorage means a backfill and a cutover, not a Gemfile edit.

Framework defaults are the third. A flipped default that changes cache key generation or timezone handling will pass CI and fail in production, which is why the staging smoke test in step six stays manual per service. Generated code shortens the writing. It does not remove the reviewing.

Run the Rails 6 to 7 initiative across your fleet.

The number that matters

Three quarters per major version means an organization is permanently one version behind the one it is trying to reach. Rails 8.1 stops receiving bug fixes in October 2026, and the series after it will arrive before most fleets finish the current hop.

Worth taking into the next planning session: of the Rails services running in production today, how many would pass an audit on framework support, and who owns the ones that would not?

Run the Rails 6 to 7 initiative across every repo: tidra.ai/initiatives/rails-6-to-7/

FAQ

Can a Rails app go straight from 6.1 to 8.1?

Rails core guidance is one minor version at a time, clearing deprecation warnings at each step. A small service with a thin gem stack can sometimes jump several series at once. Doing it on a large app combines every failure mode into one unreviewable diff.

Does Zeitwerk have to be adopted during the Rails 7 upgrade?

No, and adopting it earlier is easier. Rails 6.0 and 6.1 both support Zeitwerk through config.autoloader = :zeitwerk, which allows the autoloader migration to ship and stabilize as its own change. Rails 7 removed classic mode entirely, so a repo still in classic mode will not boot after the bump.

Is Propshaft required in Rails 7?

No. Sprockets remains supported in Rails 7 and stays the safer choice for apps that rely on asset preprocessing. Propshaft is worth adopting where assets are already precompiled or bundled outside Rails.

What Ruby version does Rails 7 need?

Rails 7.0 and 7.1 require Ruby 2.7 or newer, Rails 7.2 requires Ruby 3.1 or newer, and Rails 8.x requires Ruby 3.2 or newer. Since Ruby 3.2 reached end of life on March 31, 2026, Ruby 3.4 is the practical floor for a migration being planned now.