What a Rails codebase won't tell you, and why we ask the client first

A founder emails us with a Rails app that’s been running since 2015. It makes money. Nobody wants to touch it. That’s usually the whole brief.

Before we write a single line, we spend five to seven days doing something that looks less like engineering and more like detective work. We read the Gemfile. We run the test suite, if one exists. We check the schema. And we talk to the client, a lot, because the codebase tells us what the app does, not why it does it that way, and that gap is where most maintenance engagements quietly go wrong.

This is what that first week actually looks like on a real, revenue-generating Ruby on Rails application.

Comparison of a code-only Rails audit versus an audit that includes direct client input

Before day one: we ask for what already exists

We start by asking for anything the client already has. READMEs, old tickets, a Slack thread about “that one weird workaround,” an onboarding doc nobody’s opened since 2019. Stale documentation isn’t something we skip past on the way to the real work. Even a wrong doc tells us what the original team meant to build, and the distance between that intention and what the code does today is useful information on its own.

Then we ask three questions directly: What does this app do, in your own words? What’s been painful lately? What are you afraid to touch? Nobody writes “afraid to touch” in a commit message. Only the client knows it.

Day 1-2: the version combination tells its own story

We pull the Ruby version, the Rails version, and the key gems from the Gemfile, Gemfile.lock, and .ruby-version. No .ruby-version file at all is already a small red flag about how the app’s been maintained.

The combinations repeat themselves across clients. Ruby 2.5 with Rails 5.2 means both the language and the framework are years past their support window. Ruby 2.7 with Rails 6.0 is marginally better, but Rails 6.0 hit end of life in mid-2024. Even Ruby 3.1 with Rails 7.0 has aged out of the current bug-fix window. Rails’ own policy gives you roughly two years of security patches and about one year of bug fixes after a release, so we map every app against that clock before we do anything else.

We also scan for unbounded gem constraints, >= with no ceiling, gems pinned to nothing at all, and any source pulling over plain HTTP instead of SSL. That combination is usually a preview of what a routine bundle update would do to the app if nobody was watching.

Day 1-3: reading the code, and knowing when to stop guessing

By day three we have a first-pass audit, done by a person, not a linter. The patterns repeat: a User model pushing 2,000 lines because it handles billing, permissions, notifications, and reporting all at once. Business logic scattered across before_save, after_commit, and around_update callbacks until the actual flow of a request is nearly impossible to trace.

Here’s where a lot of maintenance work goes wrong, and it’s not a code problem. When we hit a pricing rule or a discount edge case that isn’t obvious from the code, we don’t quietly infer it and move on. If someone on the client’s team still knows how it’s supposed to behave, we ask and treat their answer as the source of truth. If nobody remembers, which happens more often than you’d expect on a decade-old codebase, we reconstruct our best understanding from the code and tests, then take it back to the client to confirm before we build anything on top of it.

Timeline diagram of a Rails maintenance first week covering versions, code audit, tests, security, data, delivery workflow, and roadmap

Day 2-4: how safe is it, actually, to change anything

Without a reliable test suite, every change to a Rails codebase carries risk nobody can put a number on. We check whether test/ or spec/ has been touched recently and whether it covers the flows that actually matter, like payments and sign-ups, or just a handful of model validations from 2021.

We also ask which flows cause the client’s support team the most grief right now. That list becomes our regression-test priority, ahead of whatever gaps look most obvious to us from the outside. A test suite that covers the wrong things confidently is arguably worse than one that admits it covers nothing.

Day 3-5: secrets, credentials, and the incident nobody wrote down

We run a focused security pass: credentials management, session config, CSRF, dependency risk. In codebases built before Rails 5.2 shipped encrypted credentials, plaintext database passwords sitting in config/, sometimes committed for years, are common enough that we stop being surprised.

Then we ask the client something a code scanner never will: has there been a past incident, a suspected breach, an access grant nobody remembers revoking? bundler-audit catches known vulnerable gem versions. It won’t catch the ex-contractor who still has SSH access.

Day 3-6: indexes, background jobs, and the ghosts we’re not chasing

We compare foreign keys and frequently joined columns against existing indexes and usually find gaps on the tables that matter most, orders, subscriptions, events. On one engagement, adding partial indexes and a materialized view took a dashboard from 8 to 12 seconds down to roughly 2.1 seconds. That’s real, but we don’t go chasing every slow query we find in a profiler before asking the client what’s actually slow for their users. We’ve also found a sync job quietly spawning around 155,000 background jobs per cycle, invisible from the outside until someone finally looked.

We tune performance where the client feels the pain, not where it’s easiest to demo.

Day 4-6: how code actually ships, versus how the docs say it ships

We look at recent pull requests, not the deploy documentation. One team we worked with shipped once a month in large, risky batches, and every release ate days of support time cleaning up regressions. Another shipped small changes several times a week and had fewer surprises and faster feedback. Where we think the cadence should change, we bring it as a discussion. The client knows their team’s constraints better than we do after five days on the codebase.

Day 5-7: a roadmap the client actually helped write

We group everything into themes and hand over a plan, not a wall of findings:

PriorityThemeExample Actions
ImmediateSecurity & StabilityRemove stored secrets, patch critical versions, fix failing tests
Short-termCode QualityExtract responsibilities from fat models, stabilize CI, add missing indexes
Medium-termUpgradesStaged path, e.g. Rails 5.2 → 6.0 → 6.1 → 7.0, following official upgrade guidance
Long-termModernizationRetire legacy jQuery front-end, restructure namespaces, rework heavy reports

We walk through it with the client before acting on any of it. What looks most urgent from a code-risk view isn’t always what’s most urgent for the business that week, and that call belongs to them.

Illustrated priority roadmap table showing immediate, short-term, medium-term, and long-term Rails maintenance tasks

Why we don’t skip the client conversation to move faster

We could audit a codebase in isolation and hand back a report in three days instead of seven. Some agencies do exactly that. We think it produces confident-sounding guesses dressed up as findings, because the code only shows you what happened, never why someone decided it should work that way.

Whatever we had to reconstruct from scratch, an undocumented deploy step, a pricing rule nobody wrote down, we write it up properly and leave it with the client at the end. The next team, whether that’s us in a year or someone else entirely, shouldn’t have to rediscover it from zero.

If this sounds like your Rails app

We work as an extended engineering team through staff augmentation, handling bug fixes, new features, and paying down technical debt after the first-week assessment closes. We’ve done this on B2B SaaS apps running Rails 4.2 and marketplaces still on Rails 5.2, and the real cost of letting maintenance slide tends to surprise founders more than the audit itself.

Even a heavily used, years-old Rails application can be stabilized incrementally. It rarely needs a rewrite. It needs someone to actually look, and to ask before they assume.

Talk to us about a first-week Rails audit.

Sachin Gevariya

Sachin Gevariya

Sachin Gevariya is a Founder and Technical Director at Essence Solusoft. He is dedicated to making the best use of modern technologies to craft end-to-end solutions. He also has a vast knowledge of Cloud management. He loves to do coding so still doing the coding. Also, help employees for quality based solutions to clients. Always eager to learn new technology and implement for best solutions.

Say Hello To Essence

Tell us about your project and we are ready to transform your idea into stunning digital experiences

[contact-form-7 id="6"]
Contact form for CTA - Footer