In this post, I'm going to give a brief intro to package managers, dependencies, and semantic versioning.
What are dependencies?
Any given development project will likely have numerous dependencies. "Dependency" is just a word to denote a framework or a library that someone else has written to make your life easier. Thanks, dependency authors! Dependencies allow us to write less code and spend less time figuring out logic from scratch. Sometimes you will hear dependencies referred to as packages.
Dependency hell
A given dependency itself could have a bunch of its own dependencies. For example, say I have a project with ExpressJS as its only first-level dependency. The package-lock.json
file, which keeps track of the dependencies of a given package, is 375 lines long. My node_modules
file, which is where all those sublevel dependencies are installed, has 49 modules added to it. This is just on account of installing one dependency.
Imagine if you had to install Express, plus all of its dependencies, and then all the dependencies of those dependencies... No thanks. Dependency hell is what that is!
Enter package managers.
Package managers
Package managers are tools (written and maintained by developers) that automate the task of installing and updating the dependencies of our project.
When we use a package manager (like npm or yarn) to install a package in our NodeJS project, the given package is added to the dependencies
of our package.json
file -- and all of that dependency's dependencies are also installed. You can find all of secondary dependencies in your node_modules
folder, as well as in the package-lock.json
or yarn.lock
file, depending on which package manager you're using.
(Package managers pull dependencies from package registries. npm has its own registry that is actually the default registry for yarn as well. Github released their own registry in mid-2019.)
Package versions and team collaboration
If we're using Github to work on a team project, conventionally, we keep the node_modules
directory in our .gitignore
file. This means that anyone who wants to work on the project must run npm install
or yarn
after cloning from Github to install the relevant dependencies.
Here, some potential problems come into play with versioning. Any given package has a version number that looks something like this: 1.2.3. Each part of that number represents the major, minor, and patch version of a package, respectively.
Semantic versioning is the name of an agreed-upon system that helps developers decide what kind of new version they'll be implementing with a given change: i.e. will it be a major, minor, or patch release? (Semver.org has some detailed info on how developers should distinguish between the versions, and a thorough explanation of semantic versioning theory.)
Additionally: in your package.json
file, the version number by a given dependency can be preceded by a carat (^
) or a tilde (~
). ~1.2.3
means to install only the newest patch version of a package, so up to 1.2.9
. ^1.2.3
means to install the newest minor or patch version, so up to 1.9.9
. A normal install will automatically add a carat to this version. You can remove this carat to indicate that the install should exclusively download the curently listed version.
So ideally, developers working on a project will be using the same version of a package, because, depending on whether it's a major, minor, or patch update, certain parts of the dependencies could behave in new ways that might break some code.
npm & yarn
For a while, npm was the de facto package manager for NodeJS projects, and it's still many developers' go-to as well as the default for Node. However, prior to 2016, users were having issues maintaining consistent semantic versioning across teams.
In 2016, Facebook released yarn, which introduced a yarn.lock
file. This promised to solve the issue of different developers installing different versions of a package in the same project, leading to bugs. npm soon followed with its own lock file, package-lock.json
.
(There is still some controversy and confusion around how lock files do and should work, but that's beyond the scope of this article. This article is a good start for learning more.)
Leading up to 2016, npm dealt with a few major issues, and if you had asked me in 2016 which package manager I preferred, I would have probably said yarn. Since then, however, it seems like npm has made major upgrades to catch up to yarn and make up for the issues it created in the past.
And while npm and yarn appear to be the main players out there for JavaScript package management, they're not the only options. pnpm is a newer package manager. If you are developing multiple projects that all use the same version of a particular package, npm and yarn would save the whole package onto your disk for each project. pnpm saves room on your development drive by only saving a package once and using a "hard link" to refer to it in whatever project's node_modules
file it needs to be in. Check out this blog for more info about how that works.
Top comments (0)