The Two Different Approaches We Take To Upgrade An Application
Since 2017 we have been focusing on upgrading Ruby on Rails applications . 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
For medium to small applications we usually recommend The Roadmap 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 . An action step in our roadmap usually looks like this:
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 .
This is what the blind estimation interface looks like:
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 section looks like in the report:
This is what the vulnerabilities section looks like in a roadmap:
2. The Initial Engagement
For medium to large applications we usually recommend a 5-week initial engagement to:
-
Quickly understand the steps you need to take to upgrade your Ruby/Rails application
-
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
andnext bundle install
– thanks tonext_rails
! -
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:
- Known Vulnerabilities
- Technical Debt 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
- 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
- An upgrade branch that can run
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
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 ! 🚀