Keep your Rails application up to date

Patrick McKenzie posted about how businesses that use Rails 2.3 should move to a supported option as soon as possible. I agree with him. If you're on 2.3, switching to the supported fork is the right thing to do.

At the same time, many developers justify a lack of a plan for upgrading applications that are under active development. Sticking to an old version of Rails needlessly hamstrings you, your development team and ultimately your business.

The business value of keeping up to date

We don't need the new features in Rails, so it's not worth upgrading.

Assume for a moment that this is true. Assume that features like the asset pipeline, strong parameters and using bundler to manage dependencies are not worth the time to upgrade.

Here's what you get if you stay up to date aside from the newest features:

  • Library authors eager to make their code work with your application. If you're maintaining a library under active development, then you're going to be much more motivated to work with the latest version of Rails than be backwards compatible with 2.X. Using libraries maintained by other people is fantastically less expensive than maintaining your own backwards-compatible fork.

  • People that know how the code in your application works. You can ask for help on the inner workings of ActionPack right now because developers know how it works. Developers don't know the implementation details of older versions of Rails internals. This knowledge allows you to implement new features (i.e. deliver business value) faster.

  • The habit of applying upgrades. If you upgrade dependencies often, you'll feel much less trepidation when you need to apply a security patch that fixes an apocalyptic vulnerability (because we never have those in Rails, right?). Having the confidence to apply security upgrades with haste will leave your application and infrastructure less vulnerable to automated attacks.

  • Dodging Unicorn Bugs. If a recent version of a dependency conflicts with the latest version of Rails, it's very likely that someone else has the same problem. It's likely there's a fix in progress. There is no one in the world that knows why your custom backwards-compatible fork of ActiveRecord doesn't work in combination with Delayed Job and an older version of Active Admin. You're on your own. Having the same bugs as everyone else allows you to fix them in hours rather than weeks.

Big bang upgrades are horrifying

Do you take good care of your teeth?

Alice doesn't keep very good oral hygeine. She brushes her teeth once per day and hasn't visited a dentist in years.

Bob on the other hand brushes twice daily and flosses every day. He visits the dentist once every six months for a checkup and takes good care of his teeth.

When Bob visits the dentist, he gets a little polish for the areas he can't reach. The visit is painless. He's in and out of the chair in less than ten minutes.

When Alice visits the dentist for the first time in years, she needs to be anaesthetized and leaves with a mouth full of blood. Even with the drugs, this is an excrutiating process that takes up to half an hour, and probably has to be repeated a few times.

Bob's oral hygiene regimen and his regular appointments are not as bad as one of Alices sessions in the chair. The big bang cleanup is a lot more painful than the aggregated overhead of regular maintenence.

The metaphor bears fruit when applied to keeping your Rails project up to date. Sticking to old versions of Rails forces you into corners where you forego the help of the community and dig yourself deeper and deeper into technical debt. Without upgrading regularly, the following risks in eventually upgrading get worse by the day:

  • Will upgrading to the newest version introduce new and exciting bugs?
  • Will all of my dependencies work with the newest version of Rails?

The more of an upgrade gap you have to cross, the higher each of these risks are. If you're upgrading a single minor version, it's easier to isolate the cause of observed bugs, both in your application and your dependencies.

A sane upgrade policy

  • In your feed reader of choice, subscribe to the Riding Rails blog.
  • Whenever a new version of Rails is announced, create a branch, follow upgrade instructions and get your tests passing.
  • Wait about one month, keeping your branch up to date with master. This is to ride out any major bugs spotted in the new version. If there are problems with your dependencies and the newer version, use this lead time to find work arounds and patches.
  • Once the version is more stable and your dependencies check out, merge back into master.
  • Repeat in parallel for all Rails codebases that you're actively working on.

The more often you do this, the less painful each upgrade it will be. Having one person own the process (for a given upgrade) makes it easier to ensure it will get done, but the team should all be pro-getting the codebase up to the latest version.

Following this sort of upgrade path in small, regular bursts of work is much less painful than finding yourself in an unsupported, unmaintainable codebase three years from now.

There are strategies for making the upgrade through a larger gap than minor versions but they're all about as much fun as having a dentist carve the inside of your mouth open. The best way to mitigate that situation is to not be in it.

- Najaf Ali