Ramblings of an aging IT geek
← Ramblings of an aging IT geek
golang

go modules are nearly here, and i am quietly retiring dep

First impressions of Go's new built-in module support and why I am moving a project off dep onto modules.

A code editor showing Go source

Go is finally getting real dependency management built into the toolchain, and after years of the vendoring wild west I find myself genuinely pleased about it. Modules land properly in the 1.11 release that is essentially upon us, and I spent an evening this week converting one of my smaller services across from dep to see how it feels.

For context, if you have somehow been spared it: Go's story on dependencies has been a long, slightly embarrassing saga. GOPATH forced your code into one global tree. Then a parade of community tools tried to paper over it, and dep emerged as the semi-official answer with its Gopkg.toml and Gopkg.lock. dep was fine. It worked. But it always felt like a holding pattern while the core team figured out what they actually wanted, and now they have, via Russ Cox's vgo proposal, and it is different enough that dep's days are numbered.

The conversion was almost insultingly easy. From the root of the project, with a 1.11 toolchain:

$ go mod init github.com/jmylchreest/widgetd
go: creating new go.mod: module github.com/jmylchreest/widgetd
go: copying requirements from Gopkg.lock

It read my existing dep lockfile and seeded go.mod from it, which is a kind touch. Then a build:

$ go build ./...
go: finding github.com/sirupsen/logrus v1.0.6
go: downloading github.com/sirupsen/logrus v1.0.6

and it resolved, downloaded and pinned everything, writing a go.sum with cryptographic checksums alongside. The resulting go.mod is refreshingly small:

module github.com/jmylchreest/widgetd

require (
    github.com/gorilla/mux v1.6.2
    github.com/sirupsen/logrus v1.0.6
)

A few things that stand out compared to the dep era.

The biggest shift is that you no longer need to live inside GOPATH. The module is defined by go.mod at the repo root, and you can keep the project wherever you like. After years of fighting $GOPATH/src/github.com/... symlink gymnastics this alone is worth the move.

Versions are selected by what the team calls minimal version selection, which is a deliberately boring algorithm. It picks the lowest version that satisfies every requirement, rather than the newest. That sounds backwards until you realise it makes builds reproducible without a lockfile doing the heavy lifting, and it means an upstream releasing a new version does not silently change your build. I like boring. Boring is reliable.

The go.sum checksums mean you get tamper-evident dependencies for free, which is the sort of thing the dep era left as an exercise for the reader.

It is not all finished. The tooling around go mod tidy and the proxy story is clearly still settling, and there is going to be a long tail of libraries with no version tags that modules treats as pseudo-versions with a commit hash and timestamp. Plenty of CI pipelines will need updating. But the direction is right, and crucially it is in the toolchain, not bolted on beside it.

So I am retiring dep on my own projects as I touch them. Not in anger, it served its purpose, but the official answer has arrived and it is better. I deleted the Gopkg.* files and the vendor directory from widgetd with a small, satisfying git rm, and did not miss them.