Go Modules: The Best of Both Worlds

We recently started using go modules at work to handle dependencies. The experience has been mostly great, but unfortunately a lot of tooling hasn’t added support for modules. Most of the team uses vscode, which depends on module support in open-source tooling, some of which is incomplete. At the time of writing, refactoring/rename tools don’t work, and many of the other tools are very slow when modules are enabled. I wanted to find a way to use modules for dependency management, but use GOPATH for development. It turns out that it’s fairly easy to get the best of both worlds if you follow a few simple rules:

  1. Put your project in GOPATH in the correct location defined by the go.mod file. If your module line specifies foo.com/bar/baz, you need to put your project in $GOPATH/src/foo.com/bar/baz. It’s the module line that matters, not the URL to the actual project.
  2. Use GOMODULE111=on when you’re dealing with dependencies. By default, when you’re inside of GOPATH, modules will be disabled. You need to manually enable them in order to add and update dependencies with go mod ... subcommands, or go get. These dependencies are what our builds use, and will be deterministic.
  3. One catch: when you alter dependencies with modules, you must run go mod vendor, still with GOMODULE111=on. This will save your dependencies to ./vendor, which will allow non-module-aware tooling to function.

A good test to see if things are working correctly: you should be able to open a module-enabled project in vscode and use gorename to change a symbol name.

PS - We’re hiring go folks!

Brandon Dimcheff
Chief Architect

Brandon Dimcheff is a software engineer born and raised in Ann Arbor, Michigan. He uses go for his day job, has fallen in love with Kubernetes, is an aspiring functional programming language nerd, and is an advocate of open source.