The Two Different Approaches We Take To Upgrade An Application

The Two Different Approaches We Take To Upgrade An Application

Since 2017 we have been focusing on upgrading Ruby on Rails applications opens a new window . It’s been quite a fulfilling learning process as we continuously improve our workflow best practices, and internal tooling.

In this article I will go into detail to explain how we determine the best approach to create the roadmap for a successful upgrade project.

Two Paths

Depending on the size of the application, our client, their workflow, their architecture, their practices, and the number of dependencies, we usually take one of two approaches with our client engagements:

  1. The Roadmap
  2. The Initial Engagement

1. The Roadmap

For medium to small applications we usually recommend The Roadmap opens a new window to quickly understand the steps you need to take to upgrade your Ruby/Rails application.

The Roadmap usually takes two weeks of our time and answers a few key questions:

  • What is the code coverage percentage for your application? How extensive is your test suite?

  • What are the most complicated files in your codebase? What are the most complicated files which are not thoroughly tested?

  • What are some of the known security vulnerabilities that are present in your codebase?

  • How long is it going to take to upgrade your application to the next minor version of Ruby and/or Rails? How much effort and time are we talking about?

During these two weeks, our team performs analytical work, estimation steps, and static code analysis on your codebase.

We use our knowledge base and experience to identify what steps are needed to get you to the next minor version of Ruby/Rails. We always recommend you upgrade one minor version at a time opens a new window . An action step in our roadmap usually looks like this:

Detail of an Action Step in Points

Then we go through a blind estimation process that involves at least two software engineers assessing the complexity of every action step.

Each action step gets estimated with a worst/best case scenario mindset. For example: If everything goes well, it is a 1-point story. If we have to go down the rabbit hole, it is a 5-points story.

For this phase of the process we use our open source tool: Points opens a new window .

This is what the blind estimation interface looks like:

Estimation modal UI for Points

Finally we translate complexity into effort/time. In order to do that, we use our historical database of time/effort spent in past Ruby/Rails upgrade projects.

In the final report, you will see: For every version jump, two estimates in terms of weeks. For example:

If our team were to work on this minor version jump, we would take between 3 and 5 weeks working with two senior software engineers.

We don’t invest any time doing exploratory work nor kickstarting the Rails upgrade branch.

1.1. Roadmap Deliverables

At the end of the two weeks you get these deliverables:

  • A PDF Report with the following sections:
    • Known Vulnerabilities
    • Major Issues
      • Code Complexity
      • Test Suite
    • Action Plan
    • Estimates per Version Jump
  • A One-Hour Presentation with this format:
    • We explain the main challenges of the upgrade project
    • We explain our methodology in detail
    • We answer any questions your team may have

This is what the code complexity vs. code coverage opens a new window section looks like in the report:

Code Coverage vs. Code Complexity Screenshot. Report generated by Skunk.

This is what the vulnerabilities section looks like in a roadmap:

List of Vulnerabilities Section in the Roadmap

2. The Initial Engagement

For medium to large applications we usually recommend a 5-week initial engagement to:

  1. Quickly understand the steps you need to take to upgrade your Ruby/Rails application

  2. Kickstart your upgrade branch with some backwards-compatible changes

Compared to The Roadmap, an initial engagement provides more value in the form of backward-compatible changes, an upgrade branch that includes significant progress towards completing the upgrade, and an action plan with better insights and more accurate estimates.

At the same time, an initial engagement is more costly than a roadmap, because we are investing 5 weeks of development/exploration time (instead of 2 weeks) and actively shipping action steps (changes to production!) which are present in the roadmap.

In these 5 weeks we will:

  • Set up your application locally. We will communicate with your team to try to determine whether your onboarding steps are accurate or not. For example, we often find that application setup steps in the README are out of date or incomplete.

  • Kickstart the Ruby/Rails upgrade branch. We will set up dual-booting in that branch and we will start by running bundle install and next bundle installthanks to next_rails opens a new window !

  • Get your application to boot with the next version of Ruby/Rails (think successfully running next bin/rails runner 'puts 1 + 1' in the console)

  • Get the application branch to run the test suite with the next version of Ruby/Rails (for example next bin/rspec spec)

  • After getting a complete output from your test suite (running with the next version of Ruby or Rails) we will record a number of failures to be included in the roadmap. If the root cause is not straightforward, we will dig deeper to determine the root cause.

  • Ship small pull requests (with backward-compatible changes) to the main branch. You will get a series of tiny PRs that will be easy to review, test, and ship to production.

  • Present the roadmap at the end of the first 3 weeks and answer any questions you may have

2.1. Initial Engagement Deliverables

At the end of the 5-week engagement you get these deliverables:

  • A PDF Report with the following sections:
  • A One-Hour Presentation with this format:
    • We explain the main challenges of the upgrade project
    • We explain our methodology in detail
    • We answer any questions your team may have
  • Code Changes
    • An upgrade branch that can run next bundle install
    • An upgrade branch that can run next rake test
    • Several, meaningful backwards-compatible pull requests shipped to production
    • Several, peer-reviewed fixes in the upgrade branch

Which approach is the best for your Ruby or Rails application?

As usual, the answer is: It depends! We need to assess the size of your application, development workflow, application’s architecture, best practices, and the number of dependencies.

We usually work with clients who have large monoliths that have been around for more than 5 years. More likely, our clients have applications that have been up and running in production for more than 10 years!

As a quick rule of thumb, if your application is beyond these parameters we will usually recommend an initial engagement:

  • More than 150 models
  • More than 300 total gems

If your applications is under these parameters we will recommend a roadmap:

  • Fewer than 150 models
  • Fewer than 300 total gems

This is a quick rule of thumb, sometimes we will consider these parameters but we will still recommend an initial engagement based on the state of your test suite, your development workflow, your application’s architecture, or your best practices.

For example, our open source Rails application “Points” would be a good candidate for a roadmap.

If we use bundle-stats, we can see the number of total gems is 147 gems:

✗ bundle-stats
+--------------------------------|------------|----------------+
|                           Name | Total Deps | 1st Level Deps |
+--------------------------------|------------|----------------+
|                          rails | 46         | 13             |
|                     sass-rails | 31         | 5              |
|                    rspec-rails | 28         | 7              |
|                         devise | 27         | 5              |
|                    web-console | 25         | 4              |
|                   dotenv-rails | 24         | 2              |
|              factory_bot_rails | 24         | 2              |
|                   jquery-rails | 23         | 3              |
|                jquery-ui-rails | 23         | 1              |
| omniauth-rails_csrf_protection | 21         | 2              |
|       rails-controller-testing | 18         | 3              |
|                       jbuilder | 15         | 2              |
|                     apparition | 14         | 2              |
|            capybara-screenshot | 13         | 2              |
|                omniauth-github | 12         | 2              |
|                     standardrb | 12         | 1              |
|                       capybara | 11         | 8              |
|               database_cleaner | 9          | 1              |
|                   acts_as_list | 7          | 1              |
|                     webdrivers | 7          | 3              |
|                         pundit | 5          | 1              |
|               shoulda-matchers | 5          | 1              |
|                 bootstrap-sass | 4          | 2              |
|                      mimemagic | 4          | 2              |
|                         listen | 3          | 2              |
|                      simplecov | 3          | 3              |
|                  coffee-script | 2          | 2              |
|                          faker | 2          | 1              |
|                        bourbon | 1          | 1              |
|                     next_rails | 1          | 1              |
|                           puma | 1          | 1              |
|             rack-mini-profiler | 1          | 1              |
|                     turbolinks | 1          | 1              |
|                       uglifier | 1          | 1              |
|                         byebug | 0          | 0              |
|                         matrix | 0          | 0              |
|                   newrelic_rpm | 0          | 0              |
|                             pg | 0          | 0              |
|          recursive-open-struct | 0          | 0              |
|                      redcarpet | 0          | 0              |
|                         spring | 0          | 0              |
+--------------------------------|------------|----------------+

      Declared Gems   41
         Total Gems   147
  Unpinned Versions   25
        Github Refs   0

And using the rails_stats gem we can see that the number of models is 6:

rake stats\[points/\]

Directory: /Users/etagwerker/Projects/fastruby/points

+----------------------+-------+-------+---------+---------+-----+-------+
| Name                 | Lines |   LOC | Classes | Methods | M/C | LOC/M |
+----------------------+-------+-------+---------+---------+-----+-------+
| Mailers              |     4 |     4 |       1 |       0 |   0 |     0 |
| Models               |   229 |   173 |       6 |      29 |   4 |     3 |
| Policies             |    67 |    50 |       3 |      12 |   4 |     2 |
| Jobs                 |     2 |     2 |       1 |       0 |   0 |     0 |
| Controllers          |   502 |   419 |      10 |      62 |   6 |     4 |
| Helpers              |    98 |    82 |       0 |      12 |   0 |     4 |
| Channels             |     8 |     8 |       2 |       0 |   0 |     0 |
| Javascripts          |   274 |   210 |       0 |      16 |   0 |    11 |
| Configuration        |   678 |   150 |       1 |       0 |   0 |     0 |
| Spec Support         |   328 |   165 |       0 |       6 |   0 |    25 |
| Feature Tests        |  1253 |   963 |       0 |       1 |   0 |   961 |
| Model Tests          |   296 |   242 |       0 |       1 |   0 |   240 |
| Policy Tests         |    37 |    29 |       0 |       0 |   0 |     0 |
| Controller Tests     |   662 |   528 |       0 |       0 |   0 |     0 |
| Helper Tests         |    10 |     9 |       0 |       0 |   0 |     0 |
+----------------------+-------+-------+---------+---------+-----+-------+
| Total                |  4448 |  3034 |      24 |     139 |   5 |    19 |
+----------------------+-------+-------+---------+---------+-----+-------+
  Code LOC: 1098     Test LOC: 1936     Code to Test Ratio: 1:1.8

If you want to know more about how we measure the size of a Rails application, we wrote an article about that: How We Estimate The Size of a Ruby Application opens a new window

Conclusion

I hope that you found this article interesting and that you now know more about how we approach “Big Rails” upgrade projects. If you are interested in getting an action plan to upgrade your Ruby or Rails application, send us a message through our contact form opens a new window ! 🚀

Get the book