Even though one of the famous Go proverbs says a little of copying is better than a little of dependency, sooner or later you will find yourself importing one into your project. You just cannot solve all the problems your application may be facing, and often it's better to defer to ready, battle-tested solutions. But how should you manage your dependencies? Since we've recently (finally!) saw the premiere of an official tool, dep, let's take a look on that.

One of the things that many Go programmers like about the language is that it uses tabs instead of spaces at all times. Many people mention it very quickly when talking about the bool features of the language. Why? It's not because tabs are better (let's not start a war now), but because the choice has already been made and everyone has to follow - there is no place for discussions as there is just one, the right way.

Unfortunately, the language creators dropped the ball a bit when it comes to solving the problem of vendoring the dependencies. There was never a single, recommended tool and it allowed the community to create multiple solutions that tackle this issue. Don't get me wrong, those projects that are available are truly awesome, but they will always raise some arguments about which one is the best. This is why I'm excited to see one tool to rule them all in dep.

Setting it up

In order to start using dep we need to install it first:

$ go get -u github.com/golang/dep/cmd/dep

Then, to use it with your project you first need to initialize it:

$ dep init 

This creates two files: Gopkg.toml which is often referred to as manifest and Gopkg.lock. There is an important thing that we need to remember from now on: the manifest is something that we own (and edit), and the lock is handled by dep. So in case of my diwia project, I have three dependencies defined:

// Gopkg.toml
...
[[constraint]]
name = "github.com/boltdb/bolt"
version = "1.3.1"

[[constraint]]
name = "github.com/pkg/errors"
version = "0.8.0"

[[constraint]]
name = "github.com/spf13/cobra"
version = "0.0.1"

While all the dependencies, including the transitive ones (the dependencies of my dependencies), with exact revision hashes, are defined in the other file:

// Gopkg.lock
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.

[[projects]]
name = "github.com/boltdb/bolt"
packages = ["."]
revision = "2f1ce7a837dcb8da3ec595b1dac9d0632f0f99e8"
version = "v1.3.1"

[[projects]]
name = "github.com/inconshreveable/mousetrap"
packages = ["."]
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
version = "v1.0"
...
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "e265cd42611c0178be36724ffe88805554c2e3e83849e2f0419878e006807106"
solver-name = "gps-cdcl"
solver-version = 1

The second subcommand that we need to get familiar with is ensure which checks what dependencies are being used (imported) in your code and ensures that they are listed in the manifest. Also, it checks the manifest for any unused dependencies and removes them from that file.

One important thing that you might have already noticed is the versions next to the dependencies. This comes from git tags assigned in the repositories, which allows you to track them much better. What is also important is that dep will not take the latest version of the repository from master branch if it noticed that there is some tag defined. It recognizes the tagged version as the last stable and selects that one as your dependency.

This is basically all you need to know about dep to get started and have all your dependencies stored in vendor/ directory. But there's more!

A step forward

Since dep is not yet a very mature project, there are still some things that it might be doing better. One of the issues people have with it, that even if you import github.com/somebody/bad-repo/awesome-subpackage, it will fetch the whole somebody/bad-repo from GitHub. This bugs me as well because the vendor/ directory grows a lot (and so does your repository) for no reason. Fortunately, you can run prune to get rid of all unused packages. In case of one of the projects I'm working with, we're talking about trimming 96MB to just 13!

Last but not least, there is a command called status which allows you to check if the dependencies you are using in the project updated their versions:

$ dep status
PROJECT                               CONSTRAINT  VERSION        REVISION  LATEST   PKGS USED
github.com/boltdb/bolt                ^1.3.1      v1.3.1         2f1ce7a   2f1ce7a  1  
github.com/inconshreveable/mousetrap  *           v1.0           76626ae   76626ae  1  
github.com/pkg/errors                 ^0.8.0      v0.8.0         645ef00   645ef00  1  
github.com/spf13/cobra                ^0.0.1      v0.0.1         7b2c5ac   7b2c5ac  1  
github.com/spf13/pflag                *           v1.0.0         e57e3ee   e57e3ee  1  
golang.org/x/sys                      *           branch master  665f652   665f652  1

Awesome! I definitely need to play around with dep more, but I'm sure it will grow into one of the must-have elements of the Go toolchain.