Ruby on Rails upgrade service

It’s more work than a quick bundle update rails

Your problem

You’ve got an application on Ruby on Rails 5.2 or older, and official security update support for this version has stopped. Alternatively, you’re on version 6.0 and you’re now only getting critical security updates rather than all security updates. For the sake of security alone it is time to upgrade to Rails 6.1, or even to Rails 7.0 if you want to be completely up-to-date.

Spending (a lot) of your developers’ time on an upgrade and having them discover the pitfalls as they go is the default approach, but it may not be a good investment of your developer time. Not getting security updates anymore is an even worse idea. You don’t want to make headlines because an unpatched system led to a data leak and tarnished your good reputation with your customers.

Besides the security risks, not upgrading will have your application fall behind the ecosystem. A little bit is not an issue, and could even be an advantage by letting other folks discover bugs and incompatibilities, but if you’re more than one major version behind then your team will start to notice. They can’t install the latest version of other dependencies that integrate with Rails due to conflicts, or what they install will be outdated by default and thus will require a future update. This means they can’t use useful new features in other libraries and they increase their future maintenance burden: your technical debt is actively generating more technical debt, like a bad loan shark debt. Installing an emergency security update will become harder and harder because it might require you to rush an upgrade of 10 different dependencies.

If one of your developers were to perform the Rails upgrade themselves, they could potentially spend weeks on the process. Weeks that they won’t spend growing your business. Your customers, users or competitors might not give you the space to make this necessary investment in your infrastructure at the cost of delaying other work.

The solution

Upgrading Ruby on Rails has definitely gotten easier over the years, but it’s still a lot of custom work. It confronts you with your technical debt and demands payment. If it’s your first upgrade, you’re going to discover the pitfalls by falling into them. When you’ve done an upgrade multiple times you’ll have a better idea of the things to watch out for, so your experience makes you faster.

Applying an upgrade requires mostly generic upgrade knowledge and only a little bit of domain knowledge that is unique to your application. This makes it a good onboarding job for an experienced developer who’s performed the upgrade before: it helps them learn how your application works while they apply a valuable upgrade.

If don’t have a convenient new hire with upgrade experience, then the next best thing is to hire a consultant who specializes in upgrades. This way your team won’t take a productivity hit, the work gets done faster, and you still get the needed upgrade. It only costs a some money that pays itself back while your developers keep growing your business.

Our offer

We are specialized in performing Ruby on Rails upgrades. We’ve got experience and a process that helps catch most preventable issues, minimizing the risks of breaking things in production. We’ve described the outline of our process below. You are free to have your developers borrow this approach (there is no magic, just sensible work) but we’re confident that it would be more effective for them to just focus on growing your business while we tackle the upgrade for you. This way they only need to spend a few hours making judgment calls and reviewing our work, rather than doing all the work themselves.

Due to the custom nature of Rails applications, it’s impossible to give a precise estimate of how long any one upgrade will take. Even we have some rules of thumb, but no guarantees. This makes upgrades scary from a budgeting perspective. Think about it. If you give us carte blanche to invoice unlimited hours until we’re done, then you will have no way to control the costs. If we would instead promise you a fixed price, then the risk of things taking longer would be 100% ours. We don’t know your application very well ahead of time, so that’s a pretty significant risk. We’d have to compensate by charging you for the worst case outcome, meaning you’d overpay in most cases. That’s not nice for you either.

Our approach is to be transparent and reduce risks for everyone, so you end up with a good price for good work.

We have a process that lets you know all the things we intend to do. We follow it and thus give you clear feedback on what we are doing and where in the process we are. You will have a rough idea of your budget: is 100 hours the limit of what you can afford, or can you afford to invest 20 hours per week for multiple months until all agreed upon technical debt is solved? If your budget is small and fixed, we can turn most of the non-critical tasks into tickets that will have to be solved later but are out of scope for the upgrade. For the in-scope tasks, we will spend a bit of time to identify potential issues and possible solutions. Based on budget we can balance a pragmatic solution vs an ideal solution. By focusing on solving the most important things first, we maximize the value you get for your budget. The difference between a small and large budget is basically how far down the priority list we can go for you.

In places with a lot of uncertainty, we will take a small fixed amount of time to try things in order to gather information and make more educated choices afterwards. For example: if it appears like you will need to replace your entire unmaintained CMS for an upgrade, it’d be nice to spend a day or two researching the top replacement candidates to see which one appears to be most compatible and get a feel for how much time it could cost to migrate. If you’re budget constrained then it might be better to see if we can patch up your old CMS to last at least one more Rails version. We will try to give you enough information so you can make the best choices for your particular application and budget.

We’ve used every single Rails version since 1.0.0. That’s over 16 years of Rails experience. We’ve upgraded many different applications for our clients, and developed a process that works pretty well for us. Our clients have been happy with our work and often used the upgrade as a starting point for a retainer agreement to support them afterwards. We’ve got a great Maintenance Service for this.

Wes is a seasoned developer with solid knowledge of the Ruby and Ruby on Rails ecosystem. His healthy habits and critical yet positive eye brought more clarity to our codebase and processes, making developing more joyful for everyone. Wes is a good communicator, keeping track of important discoveries and decisions, asking input where necessary, pointing out possible improvements where sensible. And with him on board as a senior mentor, I’m sure our juniors will have what they need to make lasting contributions.

– Willem van Engen-Cocquyt, Lead Developer at Questionmark

Your team has enough work in their backlog that only they are trained and qualified to do. Let us handle the Rails Upgrade for you, so they can continue their work. If we end up spending 100 hours on a major upgrade, this means we’ve saved your team at least 100 hours they have used to grow your business.

If you’re currently constrained by a fixed budget, we’ll go for pragmatic solutions to get you the upgrades you need. If budget is less of a concern, we can work with a weekly time budget that still allows you to control your costs.

The important thing to remember is that you control the time risk you’re willing to take and we give you our best effort to deliver value. By splitting a Rails upgrade into multiple smaller tasks, we deliver value frequently so you can see the progress and know how far along in the process we are.

Pricing follows the same rates as for our Maintenance Service: most upgrades will require 40+ hours to properly worth through, so you always get our rate of €90 per hour (excluding VAT where required) for Rails upgrades.

Because we offer multiple services, we try to keep our mornings available for routine maintenance and communication and use our afternoons for uninterrupted development time. This means that we’ll usually spend up to 20 hours per week working on your Rails upgrade.

As with all of our services, we have a Happy Client Guarantee. If you are not happy with the price/value ratio of what we have delivered, you can delete our changes and tear up the invoice. This is your guarantee that we won’t do anything but provide great value.

Email us to get your Rails application upgraded and become one of our happy clients.

Our Rails upgrade process

This is our upgrade process. You’re free to borrow it. If at any point you decide you’d rather have us do it then please email us.

Pre-upgrade preparation

Before you perform an upgrade, you want to make sure your application is ready for the upgrade. The goals are:

  • Was the previous upgrade completed fully? (i.e. are all configurations up-to-date, and are all deprecations resolved?)
  • Are all non-Rails dependencies up-to-date? (You don’t want to have unexpected blockers)
  • Are there incompatible dependencies that need to be replaced first?
  • Is the current Ruby version compatible with the next Rails version?
  • Do you have enough automated test coverage to blindly trust your tests? If not, which parts of the application worry you?
  • Do you have monkeypatches that don’t have version guards? (It would be a shame if you don’t discover issues until production)
  • Does the new version add/remove frameworks you care about? Examples: if you intend to upgrade from v5 to v7 you might want to skip Webpacker. If you’re not interested in file uploads, then you can skip setting up, configuring and migrating ActiveStorage across each version.
  • Do you track errors in production? Some issues will only affect your production setup, so if you’re not tracking errors there you won’t know until your users/customers complain about it. Please use a service such as Honeybadger, AppSignal or Bugsnag to be aware of things that go wrong in production. Doing this way before the upgrade also helps identify pre-existing issues that are unrelated to the actual upgrade.

Note that you perform Rails upgrades one minor version at a time, without skipping steps. This allows you to follow the upgrade guides and benefit from gradual deprecation of features before they are removed or replaced. Reducing the number of things that change at a time helps to quickly find and resolve issues when they do occur.

How long the preparation takes depends on the application, and how you choose to deal with the obstacles we encounter. For example: the Paperclip gem is not compatible with Rails 6, with the suggested migration path being to migrate to ActiveStorage (which uses a very different approach to file storage and image transformation). Doing this migration could take days or even weeks depending on the amount of data you have and the features you need. Switching to the community-maintained kt-paperclip takes a minute and is compatible with Rails 6 and 7.

Note that our Full Maintenance Service clients will have most of their ducks in a row, because it’s designed to get your application ready for upgrades and identify potential issues ahead of time. Other clients will be confronted with their technical debt and should expect to spend time on this accordingly.

Main upgrade

When your application is ready for the upgrade, it’s time to start. Our process, with each step consisting of one or more commits. Keeping changes small and well documented helps you discover where issues originated.

  • Upgrade the Rails version in Gemfile and run bundle update rails. It might be that you need to bump some other gems that are tightly locked to the Rails version and could not be bumped during the preparation.
  • Run rails app:update and accept all new/updated files. Then use source control to find what was changed compared to your version.
  • Process the changes in configuration: start with non-functional changes (such as updated comments, things that got moved) and end with functional changes (options that got renamed or removed, new options that got added). We tend to use this opportunity to rearrange settings to match the default order, to make future upgrades easier.
  • With a bit of luck you can now boot a console and/or run your tests.
  • Work through each failing test and other issues you encounter. If possible focus on one thing at a time. Did the upgrade change something? Was it mentioned in the changelog or migration guide? If so was that by design (to remove insecure options, for example) or was it a mere change in default/preference? Or did things get renamed and do you have to do them in a different way now?
  • Continue until your application boots and your tests are green.

Congratulations, you’ve now done the minimum required to call this upgrade successful.

In our experience, if you’ve prepared properly, a minor Rails upgrade can be performed within 10 hours. A major Rails upgrade is usually done within 20 hours. That said, if you’ve got a codebase that’s much larger than a typical application or you tightly integrate with Rails internals that got overhauled, then this rule of thumb might be too optimistic.

Post-upgrade cleanup

Next up are the less obvious things you should do to call this migration complete:

  • Go through the migration guide for the specific Rails version to address things that did not result in failing tests. These might only appear in production, or they might be subtle enough that you don’t have tests for this behavior.
  • Apply all new framework defaults, one at a time, where relevant and appropriate. When to apply some will be judgment calls that need to be coordinated with the team. For example: changes for cookie storage from the Marshal to JSON format affect user login sessions. There is a migration path via Hybrid cookies, but the team will know best how long you’ll need that bridge solution to affect enough users to not cause inconveniences.
  • Address all new deprecations as if they were errors. In fact, there’s a setting you can enable that does this for you: config.active_support.deprecation = :raise
  • Use a tool such as rubocop-rails to detect if new framework defaults can be applied that have been overlooked. ActiveRecord’s required/optional belongs_to declarations in Rails 5/6 are a good example of this.
  • Migrate to new frameworks or tools that are unlocked on the new Rails version. Examples are system tests (which replace feature tests in Rails 6) and replacing Webpacker with more modern JS/CSS handling tools such as import maps or jsbundling (in Rails 7).

During this process, having all tests pass will signal that things are good to go, unless it’s obvious that things only affect production environments. In that case, coordinate with the team to check if there would be an issue.

After upgrading Rails, it will be useful to look at updating Ruby to the latest version supported by your environment. Sometimes it just works, other times you need to plan that as an extra task because it involves a lot of changes.

This step is the most optional one, and easiest to postpone if you’re on a very tight budget.

Remember that you can email us to get your Rails application upgraded, or for a helping hand to get you through when you get stuck.