One of my recent gigs was bringing out an update to a React Native app. This involved getting it ready for newer versions of iOS and their recent requirements on the app store, as well as Android’s 64-bit app requirement.

It looked pretty clear like the update would involve updating the react-native dependency. Specifically, updating to version 0.59 at least would be necessary.

Thing is, the codebase I was starting with was on 0.55.4. Given the how fast these things move and APIs change, this is no small upgrade.

What’s the best policy for managing dependency upgrades?

There are a lot of wonderful pieces out there on what the best policy for managing dependencies is. This is not one of them.

My aim is to relate my experience having done an upgrade and provide some lessons learned. A common theme I see passed around is that “if it hurts, do it more often”.

Why is upgrading dependencies so daunting?

Going through a series of dependency upgrades can be a long, difficult experience.

The most common difficulty in doing these kinds of upgrades that arises is the brittle nature of interconnected code. When we code, we learn that changing one section can cause another to break. This is why we have disciplines to avoid these problems, such as the many different flavours of testing.

But what about open source dependencies? This code is neither tested nor “maintained” by us (assuming we’re not maintaining that specific library), so to speak. We depend on contributors to keep these up and running.

That said, my role in this project comes in managing their versions. See, third-party code will oftentimes depend on other third-party code. In our case, an update to react-native might impact how react-navigation performs, since the latter has development dependencies on the former.

Some lessons learned while making the upgrades

With all that in mind, let’s go over some of the things I learned while performing such an upgrade:

You’re not alone

This very likely isn’t the first major upgrade that has been done on this project, and it for sure won’t be the last before it, like art, is abandoned.

That said, there are wonderful, helpful tools to help you with upgrades, and at least get you started. For this React Native task, I found a helpful upgrading tool from the community. It allows you to set a current and target React Native version and shows a detailed diff that helps you along the way.

Upgrade helper in action

This kind of upgrade tool will help set you on the right upgrade path! It isn’t meant to be a catch-all but a guiding hand to help with project setups.

This isn’t just limited to React Native or even Javascript for that matter! Communities from all over tech will have guides out there for making these upgrades as smooth as possible.

Breaking the task into small victories

When trying to upgrade this React Native app, I started by breaking the task down into two:

  • Get the iOS build running
  • Get the Android build running

And then focusing on the former, putting the Android build completely out of my head. I’ll get there when I get there.

When I started on the iOS build, I was able to break it down further:

  • Adapt to CocoaPods autolinking
  • Fix compiler errors
  • Fix UI elements broken in the upgrade process
  • Fix bugs introduced by the upgrade process
  • Fix compiler warnings

Each of these can then in turn be broken down further into smaller tasks. It may seem like these pile up, but they become considerably quicker. Each of these then becomes a smaller task, which when completed, becomes a resounding victory! These helped me stay motivated through a slew of error messages.

Sometimes fixing an issue in one area can lead to the exposure of another. This is okay.

I often become overwhelmed when these start to pile up. It sometimes feels like spinning plates! But remember, these plates don’t fall. What helped me was to stay focused on my broken-down issues, only switching when those became blockers.

Making big version leaps in upgrading

If we’re going to be upgrading, which will cause some issues, why not go ahead and upgrade to the latest version of a dependency?

…is the question I asked myself before starting.

In truth, this isn’t too far off. There can be good reasons to upgrade to the latest version of a dependency. For example, with React Native, getting the latest versions running means that there are compatibility issues with the latest iOS and/or Android build processes that are addressed and therefore fixed. At the time of writing, the latest version of React Native is 0.62.2. I decided to therefore go all the way!

Keeping a work-in-progress version control branch

When working on a feature or a bug, I have the habit of keeping a bleeding-edge branch, usually called carmen-bleeding-edge or something. This branch is meant to be as work-in-progress as possible. Nothing pushed here is guaranteed to work until I prepare a proper branch for merging. It’s usually broken down into small, clear commits that are very likely extremely broken.

Doing the above has also helped me feel comfortable with rebasing, and able to present version control histories that I can then clean up.

When to stop

This is a tricky one. When I started on this project, I thought that by the end every single dependency on the latest and greatest version. This turned out not to be the case.

I had to be realistic and acknowledge the fact that as much as I may have liked to, I also had to be timely with my delivery of the running project. This meant that even though security patches were applied across the board, I didn’t upgrade to the latest major version of react-native-vector-icons.

Another thing to consider is backwards compatibility with devices. This is more specific to React Native projects, but it’s important to consider that users on older versions of Android or iOS can be left behind during upgrades like these.

Now that I’ve stopped, am I done?

Not quite! As previously written, this is a process that is rarely done on the first try, or in one go.

I was able to clean up some dependencies that were no longer being used, fix ones that were broken, and introduce some processes towards making working on this React Native app easier on the next developer that comes into the project. You’d be doing a favour to a future you!

The most important thing I took away was to not let myself get easily overwhelmed or discouraged. We’ve all been there!

Yo, do you have any favourite techniques to make your dependency upgrades easier? Do please let me know!

Buy me a coffee