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:
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.
- 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.
- Positional
enum form in app/models/profile.rb — the keyword form was
removed in Rails 8.
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:
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.
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:
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.3requiresactivemodel >= 5.1, < 7.2→ needs Mongoid 9.xglobalize 6.3.0requiresactiverecord >= 4.2, < 7.2→ needs Globalize 7.xomniauth 1.8.1requiresrack < 3, and carries CVE-2015-9284 (requestforgery in the request phase) → needs OmniAuth 2.x + omniauth-facebook 11.x
rails_12factoris also still in the production group; it has been abandonedsince 2016 and is unnecessary on modern Rails.
One landmine worth fixing regardless of the upgrade
backend/Gemfilepinsactive_model_serializerswith~> 0.9, which alsopermits 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 anybundle updatetodaywill 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.8is a one-line fix and is worth doing on its own. Movingto AMS 0.10 is a real project — 24 call sites across 44 serializers still use
the 0.9-only
embed:/embed_in_root:/self.rootAPI — and needs backendand 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:
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.ymlwas already just an ENV read.config/initializers/new_framework_defaults_7_1.rb. Leftoverupgrade scaffold that
load_defaults 7.1already covers. It assignsallow_deprecated_singular_associations_name, which 8.1 removed, so it isan outright boot failure.
enumform inapp/models/profile.rb— the keyword form wasremoved in Rails 8.
config.active_job.queue_adapter = :test. The suite relies onActiveJob::TestHelperinstalling the test adapter, which no longer happenson 8.x;
perform_enqueued_jobssilently 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:
active_model_serializersto~> 0.9.8rails_12factorload_defaults 8.1(note:cache_format_versionmust not be set to 8.1,it only accepts up to 7.1 — drop the explicit line)
gem "csv", which left the stdlib in 3.4 and isused by the data export job
Risks, and what I could not verify
Being upfront about the limits of the prototype:
dimension (8.1 supports >= 3.2, so the two are independent). The Ruby step
needs its own run.
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.
SECRET_KEY_BASE,BASE_URL,SMTP_EMAIL_FROM,TOMORROW_IO_KEY.here — there are no BigDecimal fields.