FIXED SCOPE
AI & System Readiness Audit

Architecture review, risk surface, prioritised action plan. No obligation.

PAID - 2 WEEKS
Sharp Sprint

Fixed scope, senior engineers, working software. Skip the long discovery.

Contact us

Ruby on Rails Modernization: Five Upgrade Steps Without a Rewrite

A US healthcare platform depended on a legacy Rails app running Ruby 2.6 and Rails 5.1. Teamvoy moved it through five Rails upgrade steps, expanded the automated regression safety net, and kept normal product work moving.

How do you replace an unsupported foundation without rebuilding years of billing, care, and operational logic from scratch?
How do you replace an unsupported foundation without rebuilding years of billing, care, and operational logic from scratch?
How do you replace an unsupported foundation without rebuilding years of billing, care, and operational logic from scratch?
How do you replace an unsupported foundation without rebuilding years of billing, care, and operational logic from scratch?
split hero: left side shows the title'Five Rails upgrades without a rewrite' with a subtitle, right side a dark terminal window with upgrade commands.
Project detailValue
ClientA US healthcare platform
IndustryHealthcare technology
Starting pointRuby 2.6, Rails 5.1
Current project milestoneRuby 3.3.12, Rails 7.1.6
Main implementationApproximately five weeks
Production rolloutCompleted after staging validation
StatusModernization ongoing; further Rails upgrades required
Primary serviceRuby on Rails Development Services
overview of rails modernization milestones: 5 rails transitions, ~5 weeks main implementation, ~590 automated spec files.

01. Summary

Ruby on Rails modernization moved a US healthcare platform’s application from Ruby 2.6 and Rails 5.1 to Ruby 3.3.12 and Rails 7.1.6 without a full rewrite

Teamvoy expanded the regression suite first, audited the dependency graph, upgraded one compatibility layer at a time, and validated major changes through CI and staging before production rollout. The visible result was five Rails upgrade steps in approximately five weeks of main implementation. The less visible result was the safety net: automated spec files increased from 199 to approximately 590, concentrated around billing, insurance, authentication, controllers, libraries, and infrastructure. That figure measures spec files, not a threefold increase in test coverage percentage. The team also made a deliberate scope decision. jQuery, React 16, Turbolinks, Webpacker 4, and MobX stayed in place because replacing them would not have solved the immediate Ruby and Rails support problem. The project reduced backend and runtime risk while leaving a separate frontend modernization backlog.

02. Problem

The legacy Rails app had become a product-delivery constraint, not just an untidy codebase

The platform uses the application for client and therapist workflows, scheduling, billing, feedback, and other operations involving sensitive healthcare and payment-related information. Ruby 2.6 and Rails 5.1 were outside their normal support windows, while outdated gems and framework integrations made future changes harder to sequence.

Three conditions shaped the work:
  • Billing work still had to ship. The client could not freeze normal product development while a replacement system was built.
  • Years of business rules had to survive. Billing, insurance, authentication, permissions, care workflows, and external integrations already encoded production behavior that a rewrite would have to rediscover and revalidate.
  • The existing test suite did not protect every high-cost path. A framework regression could be difficult to distinguish from old application behavior without more characterization and regression coverage.

A full rewrite would have created two active systems, a longer validation period, and a larger regression surface. Teamvoy chose an incremental Rails upgrade instead: protect critical behavior, audit compatibility, make one controlled change, validate it, and continue.

03. Solution

The modernization separated test expansion, dependency work, Ruby changes, Rails version changes, and framework defaults

This made failures easier to attribute and kept each release small enough to review, validate, and roll back through an ordinary code revert and redeploy.

Regression Tests Before the First Ruby on Rails Upgrade

The first phase focused on coverage rather than version numbers. Teamvoy restored and expanded RSpec protection around P0 billing, insurance, and authentication flows, enabled SimpleCov, and created dedicated Billing & Insurance coverage groups. Test-writing ran in two streams: business-critical billing and insurance behavior, and controllers, libraries, and infrastructure. The number of automated spec files moved from 199 to approximately 590. That did not prove that every path was covered. It gave the team a much stronger signal when a gem, runtime, autoloader, configuration default, or framework behavior changed. Official Rails guidance recommends having good test coverage before upgrading and moving through Rails versions incrementally. The project applied the same principle to production risk: change fewer variables at once, then use CI and staging to identify what actually broke.

A Readiness Audit Set the Incremental Rails Upgrade Order

Before changing Rails, Teamvoy reviewed the Gemfile and dependency graph, Rails configuration, PostgreSQL integration, background jobs, frontend stack, CI, deployment process, and external integrations. The audit produced an ordered plan and exposed blockers such as inconsistent dependency requirements and obsolete Sidekiq integration patterns. Dependency sequencing turned out to be harder than changing Rails itself. Some gems had to be upgraded or removed. Others needed temporary pins while the application still ran on Ruby 2.6. The ffi dependency, for example, required compatibility handling before the runtime could move. This order prevented the framework upgrade from becoming a single branch containing every compatibility fix. Each blocker could be tested and reviewed against the version that introduced it.

Ruby, Rails, and Framework Defaults Moved Separately

Rails progressed through five public-facing steps: 5.1 to 5.2, 5.2 to 6.0, 6.0 to 6.1, 6.1 to 7.0, and 7.0 to 7.1. Ruby moved from 2.6 to 2.7.8 before reaching 3.3.12. The intermediate Ruby release reduced the number of simultaneous compatibility changes, especially around Ruby 3 keyword arguments and older gem assumptions. Framework defaults were treated as separate migrations. The application first ran on each newer Rails version while preserving compatible behavior. New defaults were then enabled in small groups. Rails 7 defaults, for example, were switched on in three controlled batches before the temporary defaults initializer was removed. The same approach applied to autoloading and assets. Migrating from the classic autoloader to Zeitwerk exposed class names and file paths that older Rails versions had tolerated. Uglifier was replaced with Terser, Active Storage configuration was added, and filter, PDF-layout, partial-lookup, redirect-protection, and cache-format behavior was corrected as distinct compatibility work.

hero section introducing a six-card upgrade sequence: protect, audit, upgrade ruby, upgrade rails, migrate, enable defaults on a pastel gradient background with teamvoy branding.

The Frontend Stayed Outside the Modernization Boundary

Teamvoy deliberately retained jQuery, React 16, Turbolinks, Webpacker 4, MobX, and the broader frontend architecture. These technologies were also old, but replacing them was not required to remove the immediate Ruby and Rails baseline risk. That choice reduced scope and made regressions easier to trace. It also preserved technical debt. The project did not present a mixed-generation stack as the final state; it treated it as a controlled trade-off that kept product work moving while the runtime and framework changed underneath it.

hero banner with two rounded cards: left white card titled'Runtime and framework foundation' listing bullets; right dark card titled 'Working frontend architecture' listing bullets like jQuery, React 16, Webpack.

Technology Used in the Modernized Application

LayerCurrent project milestoneWhy it mattered
Runtime and frameworkRuby 3.3.12, Rails 7.1.6Replaced the original Ruby 2.6 / Rails 5.1 baseline and enabled further upgrades
Data and application runtimePostgreSQL, pg 1.5, PumaPreserved the existing application and data model rather than creating a replacement platform
Background processingSidekiq 6.5, RedisRequired compatibility updates across the framework/runtime sequence
Authentication and domain librariesDevise, Pundit, PaperTrail, Paranoia, CarrierWaveCarried business and audit behavior that had to survive the upgrade
TestingRSpec, FactoryBot, Capybara, WebMock/VCR, SimpleCovProvided regression evidence before and after each compatibility step
CI and rolloutGitHub Actions, Docker, staging validationKept changes independently testable and deployable
Frontend retainedjQuery, Turbolinks, Webpacker 4, React 16, MobXStayed out of scope to limit regression risk and avoid combining two modernization programs

The Main Upgrade Took About Five Weeks. The Program Continues.

The safety-net phase ran first, followed by approximately five weeks of main Ruby and Rails implementation. Major steps then moved through staging regression testing before production rollout. Normal product development could continue because compatibility fixes, framework hops, Ruby changes, and default batches remained independently deployable.

timeline of rails modernization milestones from phase 1 to next versions with phase labels and short notes (safety net to ongoing).

Rails 7.1.6 was a project milestone, not the end of the lifecycle. Official Rails support for the 7.1 series has ended. The supplied project plan identified Rails 7.2 as the next intermediate compatibility step, but that series is also outside the current maintenance window as of October 13, 2026. The roadmap must continue beyond it to a release covered by the official Rails maintenance policy. Ruby 3.3 remains in security maintenance according to the official Ruby branches schedule. The case therefore describes completed upgrade work without claiming the overall modernization program is finished.

04. Results

The project replaced an unsupported starting baseline with a substantially newer Ruby and Rails foundation while the product remained active

Its measurable outputs are version progression, delivery sequence, and the expanded regression suite – not unsupported claims about response time, infrastructure cost, release frequency, or feature velocity.

MetricBeforeProject milestoneStatus
Ruby runtimeRuby 2.6Ruby 3.3.12Verified in supplied brief
Rails frameworkRails 5.1Rails 7.1.6 through five upgrade stepsVerified in supplied brief
Automated spec files199Approximately 590Verified count; not coverage percentage
Main implementationNo approved baselineApproximately five weeksVerified supplied window
Product deliveryRewrite or freeze avoidedNormal product work could continueVerified
Remaining modernizationUnsupported starting baselineFurther Rails upgrades still requiredOngoing

The strongest outcome was a repeatable process. The team can continue from a larger regression safety net, a newer runtime, updated framework-critical dependencies, Zeitwerk-compatible code, and an upgrade sequence already tested in CI, staging, and production.

Four Modernization Facts

  1. Five Rails transitions: 5.1 → 5.2 → 6.0 → 6.1 → 7.0 → 7.1.
  2. Two controlled Ruby stages: 2.6 → 2.7.8 → 3.3.12.
  3. Approximately 590 automated spec files: up from 199, without presenting the change as a coverage percentage.
  4. A deliberately limited scope: the frontend rewrite stayed outside the modernization workstream.

05. Conclusion

This approach worked because Teamvoy treated modernization as risk management rather than a dependency update

Tests protected the workflows with the highest business cost. Dependencies moved before the framework when necessary. Ruby, Rails, and Rails defaults changed separately. Unrelated frontend work stayed outside the boundary. The result was not “everything modern.” It was a production application moved several framework generations forward without a rewrite, with clearer regression evidence and a repeatable path for the remaining upgrades.

The fastest way in: book a 15-minute call with a Chief Technology Officer this week.
PREFER email?
Our Ruby on Rails Development Services cover version upgrades and application maintenance,
while Application Modernization Services address the wider delivery and architecture plan.
cropped-avatar
Bohdan Varshchuk
Chief Technology Officer

Start the conversation with Teamvoy