February 25, 2026
9 min read
Your Rails app still works. Requests come in, responses go out, customers pay their invoices. From a business perspective, everything looks fine.
So when your engineering team raises the upgrade conversation — again — it's tempting to push it to next quarter. There's no fire. No outage. No breach. Why spend engineering cycles on infrastructure when there are features to ship?
This is the trap. The cost of staying on Rails 5 or 6 isn't dramatic. It's erosive. It shows up in slower hiring pipelines, in mounting security exposure you can't fully quantify, in the quiet exodus of your best developers, and in the compounding friction that makes every sprint just a little less productive than the last.
I've led enough legacy Rails upgrades to know that the teams who wait until they're forced to upgrade always spend more — in money, in time, and in talent — than the ones who plan proactively. This article is the case I wish someone had made to my leadership years ago.
Let's start with the most concrete risk. Rails 5.2 reached end-of-life in June 2022. Rails 6.0 hit EOL in June 2023. Rails 6.1 lost security support in October 2024.
That means if you're running any of these versions, the Rails core team is no longer patching security vulnerabilities for you. Not slow to patch — not patching at all.
Every new CVE disclosed against Rails is a vulnerability that will never be fixed in your version. Attackers know this. Automated vulnerability scanners specifically target known EOL framework versions because they represent guaranteed unpatched attack surface.
This isn't theoretical risk. If you process payments, you're subject to PCI DSS requirements for vendor-supported software. If you handle health data, HIPAA mandates timely patching. SOC 2 audits check for this. GDPR expects it. Running an unsupported framework version isn't just a technical footnote — it's a compliance finding that can delay certifications, void contracts, or trigger costly remediation plans.
And it gets worse when your Ruby version is also end-of-life. Rails 5 apps are typically running on Ruby 2.5 or 2.6, both long past EOL. Rails 6 apps often pair with Ruby 2.7 or 3.0, also unsupported. You're not just running one unsupported dependency — you're running a double-EOL stack where vulnerabilities at both the framework and runtime level go permanently unpatched.
The longer you wait, the wider that exposure window gets. Every month without patches is another month of accumulated, unaddressed vulnerabilities.
Rails doesn't exist in isolation. Your application depends on dozens — probably hundreds — of gems. And those gem maintainers are dropping support for old Rails versions.
If you've been on a legacy version long enough, you've already seen this error:
Bundler could not find compatible versions for gem "rails":
In Gemfile:
some_gem (~> 3.0) was resolved to 3.1.0, which depends on
rails (>= 7.0)
At first, it's one gem. You pin the old version and move on. Then it's three gems. Then it's your authentication library. Then it's Sidekiq. Then it's the one gem that patches a critical vulnerability in your file upload pipeline, and you can't upgrade it because it dropped Rails 6 support two versions ago.
This is how dependency rot works. It doesn't break your app all at once. It slowly forecloses your options. You can't adopt new tools. You can't patch individual libraries. You start maintaining private forks of gems just to keep them compatible with your Rails version, which means you're now responsible for security patching code that used to be handled by the open-source community.
Every gem you can't upgrade is a compounding maintenance burden your team carries forward, sprint after sprint, with no end in sight.
Here's the cost that doesn't show up on any dashboard: developer attrition and hiring friction.
The Rails talent pool has contracted and matured significantly. According to recent Stack Overflow surveys, roughly 5% of developers work with Ruby on Rails. The bootcamp pipeline that used to feed junior Rails talent largely pivoted to JavaScript around 2017-2018. What remains is a smaller, more senior, more expensive pool of engineers — and they have options.
Senior Rails developers are choosing where they work. And when a candidate looks at your job posting and sees Rails 5 or 6 with Ruby 2.7, they see a codebase that will teach them nothing new, give them patterns they can't transfer to their next role, and likely saddle them with workaround-heavy development for the duration of their tenure.
I've seen this play out repeatedly: strong candidates drop out of the hiring process the moment they learn the Rails version. The ones who don't drop out often negotiate higher salaries as a "legacy tax" — compensation for the less-desirable work. You end up paying more for a smaller pool of less-enthusiastic engineers.
Meanwhile, the developers already on your team are watching the ecosystem evolve without them. Rails 7 and 8 introduced significant improvements — Hotwire, Turbo, built-in encryption, async queries, Solid Queue, Solid Cache. Your engineers read about these at conferences and in blog posts, then come back to a codebase where they can't use any of it. The best ones start updating their résumés. The ones who stay are the ones who've stopped growing — and that has its own long-term cost.
Legacy Rails versions impose a constant productivity drag that's almost impossible to measure but impossible to ignore once you see it.
Your developers are writing workarounds for problems that have been solved in newer versions. They're consulting outdated documentation. They're debugging issues in gems that have been fixed upstream but can't be upgraded. They're manually implementing patterns that are now built into the framework.
Every new hire needs extra onboarding time to understand why things are done this way instead of the standard way. Every code review includes discussions about whether a particular approach is "the best we can do on this version" versus what would be possible on modern Rails.
The CI pipeline is slower because you're stuck on older Ruby versions with less efficient garbage collection and no YJIT. The test suite is more fragile because testing libraries have moved on and you're pinned to older versions with known issues. Deployments are more complex because your infrastructure dependencies have their own compatibility constraints.
None of these things individually seem like a crisis. But stack them up across a team of five, ten, twenty engineers working forty hours a week, and you're looking at a substantial percentage of your engineering budget being consumed by friction that wouldn't exist on a supported version of Rails.
The most dangerous misconception about staying on an old framework version is that the cost is static — that the price of upgrading next year is roughly the same as upgrading this year.
It's not. Technical debt compounds.
Every month you stay on Rails 5 or 6, the gap between your version and the current supported version grows. Rails 7.0 was a significant jump. Rails 7.1 introduced more changes. Rails 7.2 adopted a new maintenance policy. Rails 8.0 pushed the framework further. Each of those version boundaries represents migration work, deprecation handling, and behavioral changes your team will need to address.
More importantly, every month on an old version means more code gets written against old APIs. More tests get written assuming old behavior. More architectural decisions get made within the constraints of an outdated framework. The codebase grows, and every line of growth makes the eventual upgrade harder and more expensive.
I've worked on Rails upgrades that should have been one-week projects but ballooned into four-month ordeals because the team waited too long to start. The framework gap itself wasn't the main problem — it was the years of accumulated code that assumed the old version would be there forever.
If you've read this far, you might be thinking: "Sure, but the upgrade itself is risky and expensive."
It can be. But it doesn't have to be. Rails has one of the best-documented upgrade paths of any web framework. The deprecation warning system is designed to let you upgrade incrementally — one version at a time, fixing warnings as you go, validating each step in production before moving to the next.
The cost of a planned, incremental upgrade is predictable and manageable. The cost of an emergency upgrade — triggered by a security breach, a failed audit, a critical gem incompatibility, or the departure of the one engineer who understood the legacy workarounds — is none of those things.
If you're the person making this decision, here's the math that matters:
What you're paying now (whether you realize it or not): higher recruiting costs, slower engineering velocity, growing security exposure, compliance risk, and the opportunity cost of features your team can't build because they're fighting the framework instead of using it.
What an upgrade costs: a bounded, plannable project with well-understood steps, typically executed over weeks to a few months, that eliminates all of the above and positions your team to actually benefit from the framework they chose in the first place.
The longer you wait, the more expensive column A gets and the more expensive column B gets. There is no scenario where waiting makes this cheaper.
Your Rails app still works today. But "works" and "sustainable" are not the same thing. The hidden costs are already accumulating. The only question is whether you address them on your timeline or on someone else's.
We begin every project with a $2,000 flat-fee assessment to evaluate your codebase and deliver a fixed proposal. If you proceed, the assessment fee is deducted from the project cost.
Request an Assessment →