Most Rails apps in production are not new. They are 3, 5, 8 years old. They carry scar tissue from the founder who wrote the first version, the agency that built v2, and the four engineers who passed through since. The question for those apps is not “how do we build this?” It is “how do we keep this alive, without losing a week every time we touch it?”

This is a playbook for maintaining an existing Rails app. Not greenfield advice. Not “rewrite in Hotwire.” Practical habits that keep a real codebase shippable and upgradable.

If you are feeling the pain of an app that has been neglected for years, start with what to do when your Rails app crashes in production first. This post assumes you are not currently on fire.

What “Maintenance” Actually Means

Maintenance is not “keep the lights on.” Keeping the lights on is operations. Maintenance is the deliberate, ongoing work that keeps the app:

If any one of those is broken, you are accumulating technical debt faster than you are paying it down. That debt has compound interest - see the hidden ROI of regular Rails maintenance.

The Four Layers of a Rails App You Have to Maintain

Most engineers think “Rails maintenance” and picture bumping gem "rails" in the Gemfile. That is one layer of four. Miss any of the others and the app decays.

1. The runtime

Ruby version, OS, base Docker image, Node / Yarn if you have JS.

2. The framework

Rails itself and its direct dependencies (actionpack, activerecord, etc.).

3. The gems

Everything in Gemfile.lock that is not Rails. This is usually where the real technical debt lives.

4. Your own code

The models, controllers, jobs, views, and initializers your team has written. Especially the initializers. Oh, the initializers.

A good maintenance program touches all four on a predictable rhythm.

The Weekly, Monthly, Quarterly Rhythm

The single biggest upgrade for most Rails teams is moving from “we maintain reactively” to “we maintain on a schedule.” Here is a default rhythm that works for small teams.

Weekly (30-60 minutes)

Monthly (half a day)

Quarterly (1-2 days)

Annually (1-2 weeks)

That is it. Apps that do this never call me in a panic. Apps that do not, eventually do.

Dependency Management: The Core Skill

Dependency management is the single most important maintenance skill in Rails. It is also the most neglected.

Know what you have

bundle outdated --only-explicit
bundle exec bundle-audit check --update

The first command shows you what is out of date among gems you explicitly declared. The second flags known CVEs. Both should run in CI. Neither should scream at your team - if they do, fix the signal before you fix the bug.

Upgrade in small, boring steps

The pattern that works:

git checkout -b upgrade/redis-5
# bump Gemfile
bundle update redis
bundle exec rspec
# if green, ship. if not, debug this one gem.

One gem per branch. One branch per PR. If the test suite fails you know exactly who to blame. This is unglamorous and far faster than “let’s upgrade all the gems” marathons.

Kill dead gems

Every quarter, look at Gemfile and ask “does anything in the app still use this?”

grep -r "GemClass" app lib spec

Remove unused gems aggressively. Every gem is a future CVE and a future upgrade friction point.

Pin deliberately, comment always

# Pinned to 1.12.x because 1.13 drops Ruby 3.0 support.
# Unpin when we are on Ruby 3.2+.
gem "some_gem", "~> 1.12.0"

Pins without comments are booby traps for the next engineer.

Test Health Is Maintenance Health

You cannot maintain an app you cannot test. Most upgrade pain is test pain wearing a costume.

A few baseline requirements for a maintainable Rails app:

If your tests are red, skipped, or flaky, maintenance becomes guesswork. Fix the tests first. Everything downstream gets easier.

Observability: Find Out Before Customers Do

A maintainable Rails app has:

This is not optional. The cost of not having this is your next production outage.

See what to do when your Rails app crashes in production for the incident side.

The Initializer Graveyard

Every old Rails app has an config/initializers directory full of files nobody has opened in years. monkey_patches.rb. custom_marshalling.rb. rack_patches.rb. Each one is a silent veto on every future upgrade.

On a quarterly cadence, walk through config/initializers one file at a time and ask:

Deleting an initializer is one of the highest-leverage maintenance actions in Rails. Each one you remove makes future upgrades measurably easier.

Migrations and the Schema

A healthy Rails app:

Audit the schema once a year. Drop unused columns, indexes, and tables. Big schemas slow down everything - migrations, specs, data backups.

Security Maintenance

Some baseline habits that catch 80% of real Rails security incidents:

If you handle customer data you should also read security best practices for web apps.

When Maintenance Cannot Fix It Alone

Some apps have decayed past the point where weekly patching will save them. Signs:

In those cases, maintenance is not the answer. A Rails rescue or structured upgrade project is. Once you are stable, then you go back to maintenance rhythms.

The Team Cost of Not Maintaining

The real cost of skipping maintenance is not the eventual upgrade bill. It is the gradual slowdown in everything else. Features take longer because the test suite is scary. Bugs take longer to fix because nobody trusts deploys. New engineers ramp up slower because nothing is documented. Every quarter the app gets a little harder to change.

Apps on regular maintenance do not have that slope. They ship faster in year five than apps that “saved time” by skipping it in year two.


Need help setting up a sustainable maintenance rhythm for your Rails app? That is exactly what our Rails Care Plan delivers - monthly patching, incremental upgrades, and a team that actually knows your codebase. If you are starting from a bad place, a Rails technical audit gives you an honest baseline first.

Schedule a consultation or email to talk through your Rails maintenance plan.