In the pre yarn.lock / package-lock.json era there was only one way to make sure your project does not break tomorrow because of some dependency library introducing a breaking change: setting precise package versions in package.json :

"dependencies" : { "express" : "4.17.1" , "sqlite3" : "4.0.8" , "left-pad" : "1.2.0" }

There was a glaring problem there however: nothing guaranteed that the dependencies of dependencies wouldn’t move on. Every fresh npm install - for example when the project is deployed to production - could potentially break. Those were shady times, for the brave to venture.

Now that there is yarn and package-lock.json things have finally settled with all versions - not just the explicit dependencies - locked down. As a side effect, there is not much point in keeping versions specified in package.json . Indeed, once you add a dependency its version is looked up in the lock file and lock file only. We might as well just set all dependency versions to latest :

"dependencies" : { "express" : "latest" , "sqlite3" : "latest" , "left-pad" : "latest" }

As the time goes on you keep updating dependencies. You should do anyway. Upgrading everything to latest might break things and sometimes those things are easy to fix. Other times - not so much. And sometimes latest versions introduce bugs that you simply can not do anything about.

So you might choose not to upgrade some dependencies. And this is the time to go back and swap the latest on those packages - remember we’ve set all version to latest - to whatever version that was known to work.

The above approach, if applied routinely, will leave you with package.json where most of the versions are set to latest with may be a handful more strict ones:

"dependencies" : { "express" : "latest" , "sqlite3" : "latest" , "left-pad" : "1.2.0" }

This has benefits:

yarn upgrade does not upgrade strict versions. So it can be run often without touching the troublesome ones.

does not upgrade strict versions. So it can be run often without touching the troublesome ones. offending packages are visible at a glance in package.json

git blame on the line with a specific version reveals a commit with any details of why upgrading this package was problematic (e.g. link to github issue). This is a nice workaround for the lack of comments in package.json .

UPDATE

As pointed out by Aslak Hellesøy in the comments, this approach only works for applications, and not for libraries as 3rd party library’s package-lock.json is not honoured.