How We Fix the Broken Build During a Rails Upgrade
After we’ve addressed all the deprecation warnings and successfully dual-booted an application, the next step in a Rails upgrade is often the most challenging: fixing the broken build.
At this point, the test suite is red, and our job is to bring it back to green. This phase can feel chaotic, but over the years at FastRuby.io , we’ve developed a process that makes it systematic and (relatively) predictable.
In this post, I’ll break down how we approach this step, what patterns we’ve noticed, and how we debug the trickiest failures.
Our “Fix Broken Build” Process
Attack errors before failures
When a test suite runs, errors (exceptions, missing methods, or crashes) block progress. They prevent other tests from running and can hide further issues.
We always fix these first.
Once errors are gone, failures (assertion mismatches) become easier to interpret. Fixing errors first gives you a more complete picture of what’s broken.
Start with model tests
We usually begin with model tests. They’re the most isolated part of the Rails stack, so getting them green first often reduces cascading breakages in controllers, services, and views.
In our experience, fixing model tests early makes the rest of the suite easier to reason about, because models tend to be used everywhere else.
Batch fixes by root cause
When a build has hundreds of failures, resist the urge to fix them one by one.
Instead, look for patterns in the error messages or stack traces.
Group related failures together. For example, all undefined method 'permit' errors might come from strong parameter changes, or all nil:NilClass errors might trace back to a missing association.
We create tickets or subtasks for each root cause, then tackle them as a batch. This saves time and prevents duplicate work across the team.
Expect non-linear progress
Fixing test suites is rarely linear.
You might fix one issue, only to reveal three more. That’s normal.
Every fix allows more tests to execute, exposing new paths and new problems. Keep iterating, your progress curve will look more like a staircase than a straight line.
Don’t estimate by number of failures
A common mistake: thinking “we only have 10 failures left, we’re almost done!”
Those last few are almost always the hardest.
They tend to involve subtle behavior changes in Rails internals, gem incompatibilities, or deep application logic. Don’t estimate time based on count, estimate based on complexity.
Debugging: The Real Work
Debugging is the core of what we do at FastRuby.io. Here’s our list of tools and habits that make this phase manageable.
Read the error carefully
Sometimes, the error or failure message gives you the answer directly.
Don’t skip over it. Rails exceptions often include hints about renamed methods, changed argument signatures, or missing associations. Sometimes a failure shows something like a whitespace missing in a test, and that didn’t occur in the previous version.
Revisit deprecations
Some deprecations don’t surface in test logs, or their effects only appear under the newer version.
Run both environments side by side and compare logs, especially for warnings that didn’t appear before.
Trace the stack
Identify the first line in the stack trace that touches your code, not Rails or a gem. That’s usually where the bug actually happens.
Use a debugger
Use your favorite debugger to inspect variables, and step through.
A single p variable.inspect can save you 20 minutes of guessing.
Utilize the dual boot
One of the main reasons that we dual boot an application while working on the upgrade is so that we can easily switch back and forth between the versions to gather information on errors and failures. If we add a debugger and switch back to the current version we can see what the expected behavior was in the test when it was passing.
This can help immensely when figuring out where or why things are failing in the next version.
Check for Rails internal changes
Behavior changes between versions can break assumptions, some of these are gotchas that we come across frequently:
- Callbacks running in a different order
- Default scopes or eager loading changes
- Parameter filtering differences
- ActiveRecord query caching tweaks Check the official Rails upgrade guides or changelogs for hints.
6. Investigate gems
If the error happens inside a gem:
grepfor the method or constant name inside the gem’s codebase.- Check the gem’s changelog or issues for Rails compatibility notes.
- Try updating to a newer version, or pin to a compatible one if needed.
- In rare cases, patch or monkey-patch the gem temporarily while upgrading.
7. Fix broken mocks or test assumptions
Sometimes it’s not your app, it’s the tests themselves.
Mocks or factories might rely on old defaults that changed in newer Rails versions. Update tests to reflect the new reality.
Sometimes we even find tests that were not doing what it seemed like they were doing.
8. Log and compare behavior
When you’re stuck, add Rails.logger.info or simple print statements to compare old vs. new version behavior. Sometimes a one-line diff in runtime data reveals the cause.
9. Keep the environment consistent
Check your test data: fixtures, seeds, factories, and database defaults.
A missing column default or outdated schema can trigger dozens of cascading failures.
Bonus Tips
- Commit often: Fix a batch, commit, and move on. If a later fix breaks things, you’ll have a clean rollback point.
- Use CI aggressively: Run tests frequently in both old and new versions to detect regressions early.
- Document patterns: When you discover a recurring issue, write it down. Future upgrades will thank you.
- Pair debug tough cases: Sometimes two sets of eyes find what one brain can’t.
- Build internal tooling: Simple scripts that grep logs or group errors can dramatically speed up the fix process. LLMs are especially good for this.
Conclusion
Fixing a broken build after a Rails upgrade is where the real engineering happens.
It’s not glamorous, it’s detective work, patience, and discipline. But this is where stability is restored, where the red turns green, and where the upgrade becomes real.
Need help upgrading your Rails app?
That’s what we do every day. Get in touch with us to get a code audit or an upgrade estimate.