Dependencies are libraries that projects use but that the project’s developers don’t have to write themselves. Instead, developers can download those dependencies directly into projects and immediately use them without having to write the code within those libraries themselves.
It’s certainly possible to write code without ever using libraries, but hardly anyone does that. That’s because so many useful libraries exist that provide helpful functionality and make the work of development much easier and faster for developers. Dependencies also help with code maintainability because the code for those dependencies can be managed and updated separately from projects that use them.
Dependency managers are useful because projects today tend to have a large number of dependencies, and each dependency may also have a long list of their own dependencies. That can quickly become impossible for developers to manually download and maintain themselves. Dependency managers help developers more easily download and keep track of the dependencies in their projects.
6 Popular Package Managers
- Pip installs system and project-level Python packages.
- Maven manages dependencies and the build process for Java projects.
- Homebrew simplifies package installation for Mac and Linux operating systems.
- NuGet manages dependencies for .NET projects.
- Gradle manages dependencies and the build process for Java projects.
Dependency managers often use definition files to track all the dependencies in a project. That usually consists of a single document that lists the names of dependencies and their version numbers. Definition files make it easier for development teams to prevent downloading different versions of dependencies when they work on the same codebase together — doing so can lead to confusing development problems, said Kristen Foster-Marks, senior software engineer at online education company Pluralsight.
“When that kind of drift happens between your environments, it can be really hard to determine whether bugs are a result of bugs in the code that you’ve written yourself or if it’s actually because we’ve got different versions of packages installed,” Foster-Marks said. “It leads to this common, ‘It works on my machine,’ problem but where it doesn’t work on your machine.”
Instead, when development teams use package managers, developers can be sure that their project is running identically to their teammate’s project because the package manager handles maintaining that consistency for them.
Properties to Look for in a Package Manager
There are two main types of package managers to consider, said Pradyun Gedam, software engineer at Bloomberg. Some package managers — like Maven, npm, NuGet and Gradle — install dependencies for specific projects and programming languages, while other package managers like Homebrew target specific operating systems. Others, like pip, sit somewhere in between. Usually, developers can narrow down package managers by considering whether the package they want to install is systemwide or only specific to a single project.
There’s also a difference between package managers that help developers curate dependencies, like the Debian package manager, and those that don’t, like npm.
What Are Package Managers?
“Many times these curated distributions also patch the software — these patches can be helpful, but they can also be problematic,” Gedam said. “They change behaviors in a way that you weren’t expecting.”
On the other hand, developers who use uncurated distributions may be vulnerable to getting malware introduced in dependencies and have to be vigilant about updating dependencies on their own.
Gedam contributes to the open-source pip package manager, and has worked on issues like resolving dependency conflicts and improving user experience. The user experience is an important aspect of package managers because it’s important that users understand the error when something goes wrong with a dependency installation, Gedam said.
“If you explain what’s wrong to the user, there’s a better chance that they understand what’s happening and can fix it rather than having to spend a bunch of time trying to understand what this error even means,” he said. “Beyond that, how things are presented to the user is important. Is it very verbose? Is it too terse?”
All package managers will do these things a little bit differently. Below are six popular package managers and how they work.
Popular Package Managers
The pip package manager is mostly geared to the Python programming language. Users interact with a command-line interface to download packages to their local machines from the Python Package Index, a collection of over 300,000 software projects. Users can also use pip to install dependencies from other repositories, including GitHub and private collections maintained by companies.
While the Python language already comes with a rich collection of libraries, pip also gives developers access to many more development frameworks and tools like NumPy, a popular library for doing mathematical calculations with large arrays and matrices.
Developers who have Python already may not need to install pip separately because many Python installers include pip with the language installation. Packages are installed by pip across the machine’s entire system by default. Users who want dependencies to be contained to specific projects can use pip to install dependencies within a virtual environment, which limits the scope of the installed dependencies to the project rather than affecting the entire system.
Using pip within a virtual environment also eliminates the issue with dependency versioning, where programs have difficulty referencing the correct version of the dependency it needs if more than one version is installed. That’s because the virtual environment prevents the project from accessing any dependencies installed outside its environment.
Managing dependencies can easily get out of hand, but pip helps developers keep track of installed packages with the
list command, which displays all installed packages in the environment along with their version numbers. Developers can also save the dependencies required for a program to run in a requirements.txt file, which other developers running the same program can use to download the correct packages.
The Apache Maven tool is primarily used with Java projects, but it can also be configured to use with projects written in languages like C# and Ruby. Maven can be installed directly onto developer machines, and it also comes preinstalled with some integrated development environments developers use to write code, such as Eclipse.
Developers can use Maven to keep track of project dependencies using a file named pom.xml. That’s a configuration file where developers can list the project’s dependencies in XML format by including each dependency’s group identifier, artifact identifier and sometimes version number. Maven will automatically download any dependencies listed in the pom.xml file that are not already added to the project. Dependencies are by default downloaded from the Maven central repository, but developers can also specify custom repositories.
Maven also supports the concept of inheritance between pom files. This can come in handy when development teams are building large systems of software consisting of multiple projects that share many common dependencies. Shared dependencies can be listed in parent pom.xml files, which are automatically imported into any projects that reference that parent file within their own pom.xml files. This helps developers keep shared dependencies consistent across projects within a system.
When projects have a large number of dependencies, they can use Maven to see the dependency hierarchy, which is a view that lists the project’s dependency tree, including dependencies of dependencies that are used in the project. This view shows how Maven resolved any dependency conflicts — for example, when different versions of the same dependency are listed in different places.
In addition to keeping track of dependencies, Maven also manages the build process for projects. The pom.xml specifies the build definitions needed for the project to build into an executable, which the developer can kick off by typing Maven commands from the command line.
Homebrew is a completely open-source package manager for macOS and Linux machines, similar to pip. Developers can use Homebrew to install and manage packages for their machines by interacting through the command line. Developers can see the full list of Homebrew packages available for installation using the
brew search command, for instance.
Because Homebrew is a tool used to install packages on a machine’s entire system rather than to manage individual project dependencies, the most common packages developers install using Homebrew are other software used for development. Among the most popular packages are database management systems like PostgreSQL and MySQL, runtime environments and languages like Node.js and Python, and other package managers like Yarn.
install command and typing the name of a specific package or create a package.json file that lists all project dependencies and use the npm install command to install any dependencies that the project doesn’t yet contain. This file is structured using the JSON format.
As long as developers run the npm
The npm package manager also allows developers to differentiate between dependencies that need to be bundled with the code to run in the production environment and dependencies that are only needed in the development environment, which are labeled in the package.json file as devDependencies. Code testing frameworks may be listed under devDependencies but not the dependencies used for production.
NuGet is a package manager that supports projects built using the .NET framework, like projects written in C#. NuGet is included with installations of Visual Studio, the most common integrated development environment for writing .NET code, and can also be added as an extension to the popular Visual Studio Code editor.
When used within Visual Studio, developers can browse for packages using the search tool in the NuGet package manager. Public packages are pulled from the NuGet repository and developers can also specify private repositories.
On Visual Studio, developers can use NuGet to explore packages through a graphical user interface rather than interacting on the command line, although developers also have the option to interact with NuGet using a command-line interface. Developers can see the names, versions and descriptions of what each package does before deciding which packages to install to their project.
NuGet keeps track of a project’s dependencies in the project’s packages.config file, an XML format file that lists each dependency’s identification and version number. Unlike npm, NuGet only allows one version of a dependency to be included with each project and the package manager will resolve any version conflicts automatically.
Like npm, Gradle also allows developers to specify the scope of dependencies. Dependencies can be separated into those that are only used for testing purposes and those that get bundled with the code for release to the production environment.
Projects using Gradle can also track all necessary dependencies in a file. Unlike Maven’s XML format file, Gradle uses the JSON format to list dependencies.
Developers can write custom rules that take precedence when Gradle goes about resolving dependency conflicts. For example, developers can ask for specific versions of certain dependencies to be removed and replaced with another version, which can be useful when there are known bugs with particular dependency versions. Gradle resolves dependency conflicts differently than Maven, usually finding the latest version of the dependency rather than simply choosing the dependency that is closest to the project in the dependency tree.