
In my previous post, I discussed the benefits of upgrading to Rails 3 if you happen to have an app still running on Rails 2. The Agile League has done several Rails 3 upgrades on large projects, and here is a list of the attributes that we have found to be indicative of increased migration complexity. Or in other words, these are the things we look for when determining how difficult a migration will be, in approximately decreasing order of importance. I’m hoping this list will be useful to other developers looking to upgrade.
Number of Gems
It seems like having more gems would make it easier to upgrade. After all, all you have to do is get the latest version of the gem, right? That happens about half the time, but the other half of the time, you find that the custom code using the gem is relying on something that was changed or removed in the latest version of the gem. Many gem authors made fairly major changes to support Rails 3, and non-documented methods that you were relying on might be gone or changed. Or even worse, they might be changed very subtly, causing hours of troubleshooting. So rather than just running bundle update, I’ve found that I need to give attention to each gem: reading its README and wiki for any upgrade instructions or installation instructions that may have changed, and checking through the project to see how the gem is used. Sometimes there are even multiple upgrade steps a gem needs to go through in order to get it up to date, with testing required at each step.
How far out of date it is?
Another indication of the time required to upgrade is the current version of Rails and Ruby, and whether any recent conventions have been used. For example, if the project has already incorporated Bundler, then that’s a great indication that the project isn’t really too far out of date. The same is said about the version of Rails that the application is currently using. Since a Rails 3 migration has to be performed in phases (add Bundler, bring to latest 2.3, then to 3.0, then 3.1, then 3.2), an application that is up to date in some aspects will allow you to skip some of those steps and save substantial time.
The javascript library version is also important. Rails UJS requires at least jQuery 1.7. Or if the project is using Prototype, check the version of Prototype. It’s easy to forget to factor in time required for upgrading javascript libraries when it’s the server side piece that’s changing, but several basic form helpers won’t work as you expect if Rails UJS isn’t happy. It is especially important if there are any instances of remote_form_for, form_remote_tag, or link_to_remote being used, which I’ve sometimes found to be tricky to upgrade regardless.
Number of Monkey Patches or Amount of Cleverness
Overriding that private ActiveRecord method may have seemed like a great idea at the time, but in Rails 3 that method might be gone, or it might be in a different place, doing a slightly different thing. The same goes for any clever usages of any aspects of Rails or 3rd party gems. It generally requires some extensive research to figure out how to achieve the same effect in the newer versions. I generally look through the code manually, but if there were a way to automatically detect monkey patches, it could provide some nice insight here. If you know a way to do this, please leave a comment.
Number of Views
Probably the most tedious part of an upgrade is finding all the places affected by the Rails 3 decision to automatically html escape strings that it can’t guarantee are safe. The number of Views is a good indicator of complexity here because at the very least, you need to look at each one and make sure it displays properly, sprinkling it with html_safe and raw as needed. Other view changes include the requirement to use <%= for form_for and fields_for, which isn’t hard to fix, assuming you realize it needs to happen.
Number of Routes
The rails_upgrade gem will automatically generate a new routes file for you to use with Rails 3, but in my experience, it is about 60-80% accurate. That means you need to go through and manually fix all the routes anyways, which can be a big pain if you try to do it one at a time as you encounter application errors. We’ve found that generating routing specs for all the routes in Rails 2, and then fixing those specs in Rails 3, can save you a good bit of time here.
Number of Mailers
The Mailer syntax changed in Rails 3 as well. If you have 1 or 2 mailers, this isn’t too big a deal — just make sure there are Mailer specs in place that test everything about the mail before you update the syntax. However if you have 20-30 Mailers, this can take a surprisingly long time if specs don’t already exist for all the mailers.
Positive Factors
Having great test coverage will certainly help you find problems more quickly than random testing, especially if the specs have good coverage on any clever code, or code relying on certain gem features. However, in some cases, you could spend a large amount of time just fixing all the specs. So it’s hard to just look at number of specs as a metric, although up to a point, having more specs is better than fewer.
General code consistency is another positive factor. If every menu is created through a single menu helper, for instance, then there’s only one place in the code to update if the menu is being improperly html escaped. However, if there are 10 different ways to build menus, then obviously, there’s 10 places to change.
Having some people to help QA, or giving users a Rails 3 beta to try out will help you shake out any remaining small problems, such as a missing html_safe, or subtle differences that are hard to find, rather than trying to do all the QA yourself. This is especially true since it’s hard to remember whether you tested certain features after the latest changes you made or not. Maybe you tested the login screens thoroughly on Rails 3.0, but not as much on Rails 3.2. Manual testers can help you find all those things that automated tests don’t point out.
Alternate Methods
It would be interesting to see if checking the specific factors I’ve called out is any more accurate a predictor of time required to update a Rails project than just counting the lines of code, or running the code analysis tool du jour. But model logic or POROs could be terribly complicated in a way that is completely independent of the Rails version, so in many cases I would think these items are the most relevant factors.