Three Tips to Make Your Next Dependency Upgrade Easier

Over time, even well-managed web apps can see their dependencies fall behind. The JavaScript community moves fast, and if you don’t stay on top of it, you can wind up with a package.json from the stone ages (two months ago). A few weeks ago, my team upgraded an aging Ember 1.13 app to Ember 2.10. Here are three tips to help make your dependency upgrade process go smoother than ours.

Tip #1: Take Small Steps

When faced with a list of dependency upgrades a mile long, it’s hard to know where to begin. After upgrading our entire dependency tree, the list of features that were broken far outweighed the list of those that still worked.

We tried several strategies before finding one that worked consistently. Instead of tackling the biggest broken features first and getting them out of the way, we upgraded the easy pieces first. Our early commits weren’t as dramatic as they could have been, but they were easy to understand thoroughly. With each repaired feature, we built momentum that made repairing the next one just a bit easier.

As an added bonus, when we eventually did get around to major breaks like authentication (don’t ever fall behind on your auth library 😖), there were fewer deprecation warnings cluttering up the console.

Tip #2: Rely On Your Tests

Running the test suite during our upgrade was a humbling experience. It turns out that radically changing the underlying frameworks and libraries causes some major test failures. Who knew? But that test suite became our best friend.

Because we had a robust integration test suite driven by Capybara and Poltergeist, it was easy to identify areas of the app that were really broken and focus our energy there rather than chasing down every warning. It also saved us from hours of manual testing. Your test suite will be angry with you when you upgrade your dependencies, but it’ll help you out in the end.

Tip #3: Don’t Be Afraid of Broken Commits

“Only commit when the tests pass” is a good rule of thumb for most projects, but it doesn’t work well for large dependency upgrades. When upgrading a large app, waiting until the entire test suite passed is likely to limit you to a handful of giant commits. Those serve nobody well.

They’re frustrating to the current you because it’s hard to come up with a decent commit message. They’re also pretty terrible for future you. Digging through a commit log full of “Upgraded all the things!” messages looking for anything in particular is a rough way to spend an afternoon.

Instead of waiting for everything to be in great shape before committing, we changed tactics. Our new rule during the upgrade was to commit whenever we were confident in one improvement. This could be little things like updating controllers from foo: function() {…}.property('…') syntax to foo: Ember.computed('…', function () {…}) syntax, or larger changes like getting a failing test to pass. Instead of waiting for everything to be perfect, we committed when anything got better. As a result, the commit log was easier to read and easier to dig through when necessary.

Bonus Tip: Keep Your Dependencies Updated

The best way to handle a major dependency upgrade on your web app is to avoid it in the first place. It’s much, much easier to spend a couple of hours every few weeks fixing the minor issues that pop up when you upgrade one or two dependencies than it is to wait six months and find yourself with an entirely broken app.

There are always reasons to avoid upgrading. None of them are really good enough. Save yourself some trouble and stay on top of those upgrades!

By changing the way we work a little bit, we were able to tackle a major dependency change without breaking (much of) a sweat. How do you keep your apps in tip-top shape?