Major Ruby on Rails upgrades can be a headache when your project has a lot of dependencies or gets too far out of date. I’m pretty new to the Ruby and Rails ecosystems. And when I had to perform upgrades on several projects recently, I was disappointed by the lack of guidance available.
To be fair, upgrades come along infrequently, and each project can be quite different from the next. So today, I’ll share some general guidelines I discovered along the way that can hopefully help other developers in similar situations.
One quick note: All my Ruby experiences (and consequently these guidelines) revolve around using Bundler to manage Ruby gems.
1. Stay Up to Date
To be ready for a smooth Rails upgrade, keep other dependencies up to date. A clean and healthy codebase will ease upgrading Rails. Really, this is just proper general maintenance.
I like to mentally categorize gems into major and minor dependencies:
- Major dependencies are things like Rails or other frameworks that are woven tightly throughout the fabric of the application. These dependencies are often actively maintained, moving along with or without you.
- Minor dependencies are more stagnant, with low potential for bugs or security vulnerabilities. These tend to be things like developer tools or collections of helper functions.
Watch how you use version pinning! Especially in long-running projects, it can be tempting to pin dependencies to a version that you know behaves well with the rest of the codebase. This is fine for minor dependencies, but keeping major dependencies pinned is asking for trouble in the future.
2. Be Familiar with Your Dependencies
Be familiar with the versioning of your major gems. Transitive dependency lockups can be incredibly frustrating. Being familiar with how to find stable versions, sub-dependencies, and support models will expedite fixing these issues. You should also know things like how frequently they’re updated, whether they use a long-term support model, and how far back they support old versions.
I find rubygems.org very helpful for this. Search for a gem, and the site will answer many questions and provide metadata and links to the changelog, source code repository, and more.
3. Practice Test-Driven Maintenance
Learn to lean on your test suite. A good, comprehensive test suite will make your upgrade experience much easier.
My typical workflow starts by trying an upgrade and reading any dependency version conflicts that it outputs. Then I identify a problematic gem and investigate its versions and sub-dependencies. Finally, I get that gem updated and start again. Between each iteration, it’s important to get the test suite green and resolve (or at least note) any deprecation warnings. I consider this “test-driven maintenance.”
When the only thing that has changed is one version in the gemfile, you can dig into that gem’s changelog to figure out why it might have broken your test. You don’t want to finish upgrading your dependencies only to find that you have a broken test suite but no idea which gem is breaking which tests.
4. Know Your Blind Spots
Be sure to remember what your tests don’t cover. Aside from general application functionality without test coverage, look for configuration and setup code that might be more “set it and forget it.”
One place this bit me recently was with ActiveRecord migrations. I made an upgrade from Rails 4 to 5 without versioning my migrations, and it caused headaches the next time I tried to spin up a fresh development environment.
Other things that might fall into this category:
- Configuration files like server provisioning code
- Third-party tie-ins that are harder to test directly
- Developer tools that might not interact directly with the application but are used in the development process
Keeping dependencies up to date is essential. Major dependencies like Rails can be quite a chore to upgrade. I hope this advice can help you avoid some of the pitfalls I’ve experienced.