I've been working on a large React.js monorepo recently. The application has 20-30 developers working on it, all split into many teams. Having such a large team with a lot of changes makes debugging code challenging. A big source of bugs for developers within our teams is package versions. In this guide, I will share all the tips and tricks I've learnt in order to debug and figure out exactly what version of a package my application is using.

Packages and Versions - The Basics

First, let's start with the basics. Let's say you have a package.json file with this reference:

"dependencies": {
    "my-packages": "^1.1.1"
}

Looking at the snippet above, the first thing you may notice as odd is the caret. Manually having to bump and commit every single package change is extremely tedious. If you work in a big repo and you have 500+ packages you will want to automate the process as much as possible. The caret ^ helps make managing these dependencies easier.

In the example above, the caret before 1.0.1 means that whenever you run a yarn, yarn will search and use the most recent minor version (the second number) for the specified major version (the first number) that's available for that package. In our example ^1.1.1 will match any version of that package within the 1.x.x range, e.g. 1.2.0 and 1.3.0.  If a version 2.0.0 of your package exists, the caret will do nothing and you will have to manually bump it.

The shortcode you can use to auto-bump packages is the tilde. The tilde ~ will match on the most recent patch version (the third number) for the specified minor version (the second number). In our example, ~1.1.1 will match all 1.1.x versions. It won't auto-upgrade on version 1.3.0.

Words of Warning

Using the tilde or caret makes life easier for small changes, however, on occasion, someone in the team may forget to bump a breaking change correctly, you publish your app and it magically breaks for no reason. If you follow semvar correctly this should never happen, however, we all live in the real world where people make mistakes. If your application suddenly breaks for no good reason, the first thing I suggest you do is have a look in the yarn.lock file.

The yarn.lock file is definitely your friend when it comes to debugging your application and figuring out exactly what versions of packages your application is using. One big tip is to never assume you know what versions of packages your applications use and always check your yarn.lock file first. From the previous example, if I look within my yarn.lock for this application I will see an entry like this:

"my-packages@^1.1.1":
  version "1.2.4"
  resolved "https://registry.npmjs.org/my-packages/-/my-packages-1.2.4.tgz"
  integrity sha512-kTt1Cb
  dependencies:
    query-string "^5.0.0"

If we dissect this entry we can see that my-package has some dependencies. Specifically 'query-string'. Checking what dependencies and what versions they are should be one of your first goto points of call when trying to debug in your application. I can't count the times that I've seen an issue and I thought the application was using the latest and great version, only to check my yarn.lock to find out otherwise.

Another useful property is the 'version' property. Version tells you the exact version your app is using for that package. If you start using the tilde and caret, then within your package.json you won't know the exact version your application is using. When you encounter an issue making sure you know the exact version and commit the package is using is essential, so again when I get a bug this is another thing I check straightaway.

The other thing about the yarn.lock file is to make sure there are no duplicate entries for your package. In the last year, I've encountered situations where I've needed to debug some code I've never looked at before. I've seen an entry in the package.json for a package that has an issue. After several hours of head-scratching and not being able to attach my debugger, I've realised the packages were either, not being used anymore and no one tidied up the config, or, the package I need to debug is actually referenced by something completely different. These issues were all solved by looking for duplicate entries within yarn.lock.

Updating Packages

There are two main ways of updating a package. The simple way is to manually update a version using 'upgrade'. This way is OK but it won't magically auto-bump any sub-packages that have the tilde or caret.

yarn upgrade my-packages^63.6.2

A better way to update your packages and to make sure all the sub-dependencies are updated is to use 'upgrade-interactive'.

yarn upgrade-interactive --latest

When you use yarn upgrade-interactive you can pick what packages within your application you want to update. If you follow these tips then I promise you will start to diagnose your application in a lot less time.