Dependencies management in 2019: a review
Tags: dependencies, review, programming
Published: 2019-03-03 22:58:09

---
title: Dependencies management in 2019: a review
published: true
description: Let's have a look at the different solutions for several popular languages
tags: dependencies, review, programming
---

![dependencies](https://thepracticaldev.s3.amazonaws.com/i/yjhbf76ednfulqhb6pjk.jpg)

Hello folks,

The aim of this post is to make a (mostly) complete and accurate view of the different solutions available to you when you produce code and want to manage your dependencies like a boss, for different languages. We'll see how languages have different approaches to dependency management.

I'll be writing about these 9 languages:

* PHP
* Python
* Rust
* Go
* Javascript
* Elixir
* Nim
* C#
* Ruby

# Let's start with an easy one: **PHP**

## Composer

{% github composer/composer %}

Composer is the _de facto_ standard for dependencies management in PHP. It's very hard today to get anything done in PHP without it. I recommend reading [this article](https://medium.com/@tfidry/managing-your-dependencies-in-php-321d584441ab) from [Théo Fidry](https://medium.com/@tfidry) if you want to know more about the limitations of composer and the workarounds (like using PHARs).

## PEAR

{% github pear/pear-core %}

This is the old school player. It installs dependencies to the system. Only use it if your distribution is not shipping the extension in a package already. Note that you can use composer to install a PEAR package, see [this part](https://phptherightway.com/#pear) of phptherightway for more.

That's it for PHP. I'm pretty sure there are no other PHP dependencies management applications out there, but if I'm wrong please let me know in the comments section.

# Second language: **Python**

## Pip

{% github pypa/pip %}

pip is the goto package manager for python, it has its drawbacks but is a very popular tool to install Python dependencies. BTW, you should [Stop using sudo pip install](https://dev.to/elabftw/stop-using-sudo-pip-install-52mn). Use pip with moderation (and the `--user` flag!).


## Venv

The official module for managing your project's dependencies inside a virtual environment, thus not polluting the rest of the OS.

## Virtualenv

{% github pypa/virtualenv %}

This project started in 2011 and is very useful to isolate dependencies for a project. Part of this project is now `venv` in the standard python library. So you can think of `virtualvenv` as a more feature complete `venv`.

## Pipenv

{% github pypa/pipenv %}

Pipenv started in early 2017 and was very quickly popular. It's my favorite, too. It works very similarly to `composer` with a lockfile and all the dependencies are installed in a special folder. To run the script in the environment, you need to prepend `pipenv run`. See [my previous post about it](https://dev.to/elabftw/stop-using-sudo-pip-install-52mn). Note that [the official Python doc](https://packaging.python.org/tutorials/managing-dependencies/) is recommending `pipenv` for managing dependencies.

Something interesting to note, is that all of the above-mentioned projects are all hosted in the [github.com/pypa](https://github.com/pypa) namespace, the [Python Package Authority](https://www.pypa.io/en/latest/), a group dedicated to maintaining the python packagers. I think it's nice to have this kind of "authority" for improving the ecosystem in Python.


# The third language we'll talk about is: **Rust**

## Cargo

Well, here it's a nobrainer, because (AFAIK) there is only one solution, and it's `Cargo`.

{% github rust-lang/cargo %}

Cargo is great. Cargo is shit. YMMV. Honestly, it's pretty cool to have one official tool for dependencies management, at least it saves you the time you would have spent choosing one! It installs dependencies per-project but can also be used for system-wide packages. Interestingly, it uses the [TOML](https://github.com/toml-lang/toml) syntax for the configuration file, whereas other projects chose YAML or JSON.

# Let's move on to the fourth language: **Go**

`go get` is is the way to install a dependency in Go. But the issue is that it'll be installed system-wide (in $GOPATH). Say hello to conflicts and breaking changes because you can only have one version of a lib…

This led to other approaches like git submodules or project specific $GOPATH and a lot of developers being angry at Go.

However, with Go version 1.11 (released in August 2018) there is now something called `modules`. Check out the [wiki page](https://github.com/golang/go/wiki/Modules) to know more about Go modules. I believe this will be the future of Go dependencies management, because fuck system-wide dependencies.

# What do I see? Is that the fifth language? Yep: **Javascript**

Aaaaahhh, javascript and the dependencies, what a wonderful world! :)

![node_modules](https://i.imgur.com/8VCIQhG.png)

## Npm

{% github npm/cli %}

Everyone knows `npm`. You define your dependencies in a `package.json` and install them in the `node_modules` folder. Since version 5, there is a `package-lock.json` allowing you to have reproducible builds.

## Yarn

{% github yarnpkg/yarn %}

Yarn had reproducible builds well before `npm`. It also doesn't suffer from the sometimes poor decisions taken by npm's project leaders. It's fast, works well. It's my tool of choice.

## Bower

Come on now! We said 2019 in the title! Forget about `bower`.

I will not talk about [Parcel](https://parceljs.org/) or [Webpack](https://webpack.js.org/) as these are bundlers, not package managers.

Note that you can use either `yarn` or `npm` with the exact same `package.json`!

One thing to note about packages in javascript, is that it has become completely crazy, with micro libraries consisting of one line of code more or less, being used by hundred of thousands of other packages. For fun, go into a Javascript project and try this command:

`ls node_modules | grep '^is*'`

How fun is it to have `is-obj`, `is-object`, `is-plain-obj`, `is-plain-object` or things like `isarray`, but also `is-arrayish`… Do we really need a package `is-windows` when all it does is:

`return process && (process.platform === 'win32' || /^(msys|cygwin)$/.test(process.env.OSTYPE));` ?

And good luck if you want to look at the code of all of these libraries for your project. That lead to the [left-pad chaos](https://www.theregister.co.uk/2016/03/23/npm_left_pad_chaos/).

So my advice here is to think hard before installing a javascript dependency, as it might come with hundred of these little library, and one day, one of them will become malicious (see [malicious packages on npmjs.com](https://www.npmjs.com/advisories)).

You can use [Depcheck](https://github.com/depcheck/depcheck) to try and cleanup a bit your dependencies.



# Let's talk about a more exciting language, like: **Elixir**

Elixir is a bit like Rust when it comes to dependency management: one official tool that gets the job done: [Mix](https://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html) (or [Hex](https://hex.pm) for Erlang). Nothing more to say here, it works well enough and lets you focus on other things.

Okay so what do we have left? **Nim**?

[Nim](https://nim-lang.org/) is a pretty new and cool language, and the name of the package manager is sooooo cute <3: [Nimble](https://github.com/nim-lang/nimble). Which reminds me of this guy:

![Nibbler](https://i.imgur.com/Jyn7TSM.png)

Nothing to see here, just use the one provided and get coding!

# The next language is **C#**

## Nuget 

{% github NuGet/NuGet.Client %}

This is the standard package manager that comes with Visual Studio. It exists since 2010 and is developed by the .NET foundation. I don't have much more to say about it because it's really windowsy and that's clearly not my platform of choice when it comes to anything vaguely related to computing.

# Last but not least: **Ruby**

## Rubygem

{% github rubygems/rubygems %}

This is the standard way to get dependencies for a ruby project. It can be seen as `pip` for ruby, because it installs packages system-wide. You can install them for your user with the `--user-install` flag (better: add `gem: --user-install` to `~/.gemrc` to always install gems in your home).

So what about managing dependencies per-project you'll ask?

## Bundler: a gem to bundle gems

{% github bundler/bundler %}

You get a `Gemfile` and `Gemfile.lock` file, and you're sure the project will run everywhere with the same versions of gems.


But there is something interesting about the ruby ecosystem: it exists a lot more projects to switch between different ruby versions (chruby, rbenv, rvm, uru) than to install dependencies.


# Conclusion

As you can see, some languages have it all figured out for you (rust, nim), while for others you need to make a choice (python, javascript), and for some you have system-wide and project-wide solutions (python, ruby). I don't quite understand why Go, a "recent" language didn't do a better job at dependency management. But this fits well with the view that "Go was created like we were still in the 70's".

# Take home messages

* Install your dependencies per project with a reproducible build (lock down versions of packages) to avoid surprises and to get something portable.

* Try to avoid installing too many dependencies (javascript, I'm looking at you!).

* Don't ship the dependencies with your project but ship the lockfile ;)






That's all folks, please leave a comment if you think I forgot something important or if I got something wrong ;)

Have fun coding!