Angi is a household name for good reason, and the millions of individuals seeking home improvement, repair, and household services rely on the Handybook app. Angi needed to upgrade the large and super busy app form Rails 4.1 and Ruby 2.3 to the latest versions of Ruby and Rails.
The upgrade plan focused in on minimizing work in progress, and incremental everything approach, data-driven decision making, and over communicating.
And that Rails upgrade manifesto led to both a successful Ruby and Rails upgrade while also reducing overall tech debt.
Denver, CO
3,000+ employees
Angi is experiencing accelerated growth. Keeping application dependencies up-to-date is always important, but it's absolutely essential in today's developer market. Attracting and retaining top talent is difficult already; add an outdated stack into the mix, and it's nearly impossible. Security & compliance risks can be mitigated by patching old dependencies, but there is no shortcut to improving developer happiness. With this in mind, Angi wanted a partner to establish a process by which major dependency upgrades can be performed safely & continuously.
Angi's services are in high demand, and Handybook is an essential application. Featuring deep integration with third-party services and 40+ microservices, Handybook has a lot of moving parts. Aggressive caching, database sharding, and horizontal scaling are all important factors allowing the application to meet demand.
Hint (UpgradeRails.com's original owner) defined an upgrade manifesto with four focus areas:
Minimize work in progress
Upgrading Rails in a large, very active codebase is a risky business. Falling behind is easy, and if you're not careful, you may find yourself in merge hell. Practically speaking, this means no long-running branches. Instead, all changes must be PR'd to the main branch and be merged swiftly.
Incremental everything
No task is too small to deserve asking the question, "how can we break this up?" We minimize WIP and reduce business risk by taking many small, incremental steps. Put in practice, this means changes for compatibility are made in the smallest possible units, and when deploying changes into production, we perform a canary deployment, starting at 1% traffic and ramping up slowly.
Use data to drive decision making
Sometimes it isn’t possible to determine the correct solution for a given challenge by static analysis, even with excellent git history. In these situations, it's tempting to guess. But don’t do it; it's a trap! Instead, use dynamic analysis (often in production). Gather more information, and make decisions based on fact, not conjecture.
Over-communicate
We'll be making significant sweeping changes (incrementally) to the entire codebase. If something goes wrong and the symptom is outside our purview, we want to know about it. By continuously communicating project status, every member of the organization becomes a secret agent, reporting helpful information, resulting in a smoother upgrade.
This led to a defined upgrade plan:
bootboot
gem to dual-boot with two separate sets of dependenciesWe partnered with Angi in their effort to improve developer experience & productivity by investing in the platform. Upgrading a large and busy Rails application is tedious work and it's easy to introduce new tech debt that compounds making future upgrades even more difficult.
By following a well-defined process backed by the upgrade manifesto, we achieved an overall reduction in technical debt, leading to a number of positive outcomes.
Complete partial Strong Parameters migration
Reduce tech debt in massive, high traffic app
Carefully adjust complex Ruby objects in persistent data stores
Migrate from octopus
to octoball
while reducing edge-case bugs & unexpected behavior