Skip to content

Ruby 3.2 and Rails 7.1 are both EOL — upgrade path to Ruby 3.4 / Rails 8.1 #869

Description

@suttondemlong

Summary

The backend runs Ruby 3.2.3 and Rails 7.1.5.2. Both passed end of life before
this was filed, so neither receives security patches:

Component Version EOL
Ruby 3.2.3 2026-03-31
Rails 7.1.5.2 2025-10-01
PostgreSQL (CI + compose) 12.8 2024-11-21
MongoDB (CI + compose) 4.4.9 EOL

I spent some time working out what an upgrade actually involves and prototyped
it end to end. The short version: it is a much smaller job than the version
gap suggests
— four app-level code changes, plus gem bumps. I have a working
prototype green on Rails 8.1 and I'm happy to do the work if maintainers want
it.

Why 8.1 specifically

Rails 7.2 reached EOL on 2026-08-09 and 8.0 goes EOL on 2026-11-07, so both are
stepping stones at best. Rails 8.1 (EOL 2027-10-10) is the only target worth
the effort, and it turns out the intermediate versions can be skipped entirely.

What currently blocks it

Three gems cap the framework below 7.2. These are hard dependency-resolution
failures, confirmed by running the resolver, not guesses:

  • mongoid 8.1.3 requires activemodel >= 5.1, < 7.2 → needs Mongoid 9.x
  • globalize 6.3.0 requires activerecord >= 4.2, < 7.2 → needs Globalize 7.x
  • omniauth 1.8.1 requires rack < 3, and carries CVE-2015-9284 (request
    forgery in the request phase) → needs OmniAuth 2.x + omniauth-facebook 11.x

rails_12factor is also still in the production group; it has been abandoned
since 2016 and is unnecessary on modern Rails.

One landmine worth fixing regardless of the upgrade

backend/Gemfile pins active_model_serializers with ~> 0.9, which also
permits 0.10
. A fresh resolve selects 0.10.16. That release is a rewrite with
a different JSON output format, and the Ember client consumes the 0.9 format
via ActiveModelAdapter + EmbeddedRecordsMixin. So any bundle update today
will silently change the API wire format and break the web client, with no
change to app code and nothing in the test suite to catch it.

Pinning to ~> 0.9.8 is a one-line fix and is worth doing on its own. Moving
to AMS 0.10 is a real project — 24 call sites across 44 serializers still use
the 0.9-only embed: / embed_in_root: / self.root API — and needs backend
and frontend to change together. It should stay out of scope here.

What the upgrade actually requires

Starting point is good: the suite is 315 examples, 0 failures, ~95% line
coverage, and runs in about 9 seconds.

Only four app-level changes are needed, and each one works on 7.1 as well as
8.1, so they can ship and deploy ahead of any version bump:

  1. Rails.application.secrets → ENV (8 call sites). Removed in Rails 7.2;
    the app currently fails to boot on 8.1 at the first initializer. Every value
    in config/secrets.yml was already just an ENV read.
  2. Delete config/initializers/new_framework_defaults_7_1.rb. Leftover
    upgrade scaffold that load_defaults 7.1 already covers. It assigns
    allow_deprecated_singular_associations_name, which 8.1 removed, so it is
    an outright boot failure.
  3. Positional enum form in app/models/profile.rb — the keyword form was
    removed in Rails 8.
  4. config.active_job.queue_adapter = :test. The suite relies on
    ActiveJob::TestHelper installing the test adapter, which no longer happens
    on 8.x; perform_enqueued_jobs silently stops running jobs.

With those four changes plus the gem bumps, I get 315 examples, 0 failures on
Rails 8.1.3.1
with Mongoid 9.1, Rack 3.2, Sidekiq 8.1, Devise 5 and Puma 8 —
including at full load_defaults 8.1, with zero deprecation warnings.

Proposed sequencing

Each step is independently reviewable and deployable:

  • Pin active_model_serializers to ~> 0.9.8
  • The four Rails-8-readiness changes above (still on Rails 7.1)
  • Gem bumps + Rails 8.1; drop rails_12factor
  • load_defaults 8.1 (note: cache_format_version must not be set to 8.1,
    it only accepts up to 7.1 — drop the explicit line)
  • Ruby 3.4; also declare gem "csv", which left the stdlib in 3.4 and is
    used by the data export job
  • Infra: PostgreSQL 12 → 16/17, MongoDB 4.4 → 8.0 in CI and compose

Risks, and what I could not verify

Being upfront about the limits of the prototype:

  • Ruby 3.4 is unverified. I ran the prototype on 3.2.3 to isolate the Rails
    dimension (8.1 supports >= 3.2, so the two are independent). The Ruby step
    needs its own run.
  • OmniAuth 1 → 2 is the largest residual risk and the test suite does not
    cover it.
    v2 makes the request phase POST-only with CSRF protection, and
    Facebook login is initiated by the frontend, so a green backend suite proves
    nothing here. This needs manual verification and possibly a frontend change.
  • Deployment config needs checking by someone with Heroku access:
    SECRET_KEY_BASE, BASE_URL, SMTP_EMAIL_FROM, TOMORROW_IO_KEY.
  • Mongoid 8 → 9's BigDecimal → Decimal128 storage change does not apply
    here — there are no BigDecimal fields.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions