Last week, GitHub announced that their main application is now running on Rails 5.2.1. Along with this upgrade, they have also improved the overall codebase and cleaned up technical debt.
How GitHub upgraded to Rails 5.2.1?
The upgrade started out as a hobby with no dedicated team assigned. As they made progress and gained traction it became a priority.
They added the ability to dual boot the application in multiple versions of Rails, instead of using a long running branch to upgrade Rails.
Two Gemfile.lock were created:
- Gemfile.lock for the current version
- Gemfile_next.lock for the next version
This dual booting enabled the developers to regularly deploy changes for the next version to GitHub without any affect on how production works. This was done by conditionally loading the code:
if GitHub.rails_3_2? ## 3.2 code (i.e. production a year and a half ago) elsif GitHub.rails_4_2? # 4.2 code else # all 5.0+ future code, ensuring we never accidentally # fall back into an old version going forward end
To roll out the Rails upgrade they followed a careful and iterative process:
- The developers first deployed to their testing environment and requested volunteers from each team to click test their area of the codebase to find any regressions the test suite missed.
- These regressions were then fixed and deployment was done in off-hours to a percentage of production servers.
- During each deploy, data about exceptions and performance of the site was collected.
- With this information they fixed bugs that came up and repeat those steps until the error rate was low enough to be considered equal to the previous version.
- Finally, they merged the upgrade once they could deploy to full production for 30 minutes at peak traffic with no visible impact.
This process allowed them to deploy 4.2 and 5.2 with minimal customer impact and no down time.
Key lessons they learned during this upgradation
Upgrading will be easier if you are closer to a new version of Rails. This also encourages your team to fix bugs in Rails instead of monkey-patching the application.
Keeping an upgrade infrastructure
Needless to say, there will always be a new version to upgrade to. To keep up with the new versions, add a build to run against the master branch to catch bugs in Rails and in your application early. This make upgrades easier and increase your upstream contributions.
Regularly address technical debt
Technical debt refers to the additional rework you and your team have to do because of choosing an easy solution now instead of using a better approach that would take longer.
Refraining from messing with a working code could cause a bottleneck for upgrades. To avoid this try to prevent coupling your application logic too closely to your framework. The line where your application logic ends and your framework begins should be clear.
Be sure to assume that things will breaks
Upgrading a large and trafficked application like GitHub is not easy. They did face issues with CI, local development, slow queries, and other problems that didn’t show up in their CI builds or click testing.
Read the full announcement on GitHub Engineering blog.