blob: 1ed22b43020b370bcb09bec19efddecc8bc731d6 [file] [log] [blame]
# Using Go Modules
19 Mar 2019
Tags: tools, versioning
Summary: An introduction to the basic operations needed to get started with Go modules.
Tyler Bui-Palsulich
Eno Compton
## Introduction
This post is part 1 in a series.
- **Part 1 Using Go Modules** (this post)
- Part 2 [Migrating To Go Modules](/migrating-to-go-modules)
- Part 3 [Publishing Go Modules](/publishing-go-modules)
- Part 4 [Go Modules: v2 and Beyond](/v2-go-modules)
Go 1.11 and 1.12 include preliminary
[support for modules](,
[new dependency management system](
that makes dependency version information explicit
and easier to manage.
This blog post is an introduction to the basic operations needed
to get started using modules.
A module is a collection of
[Go packages](
stored in a file tree with a `go.mod` file at its root.
The `go.mod` file defines the modules _module path_,
which is also the import path used for the root directory,
and its _dependency requirements_,
which are the other modules needed for a successful build.
Each dependency requirement is
written as a module path and a specific
[semantic version](
As of Go 1.11, the go command enables the use of modules
when the current directory or any parent directory has a `go.mod`,
provided the directory is _outside_ `$GOPATH/src`.
(Inside `$GOPATH/src`, for compatibility, the go command
still runs in the old GOPATH mode, even if a `go.mod` is found.
See the
[go command documentation](
for details.)
Starting in Go 1.13, module mode will be the default for all development.
This post walks through a sequence of common operations
that arise when developing Go code with modules:
- Creating a new module.
- Adding a dependency.
- Upgrading dependencies.
- Adding a dependency on a new major version.
- Upgrading a dependency to a new major version.
- Removing unused dependencies.
## Creating a new module
Let's create a new module.
Create a new, empty directory somewhere outside `$GOPATH/src`,
`cd` into that directory, and then create a new source file, `hello.go`:
package hello
func Hello() string {
return "Hello, world."
Let's write a test, too, in `hello_test.go`:
package hello
import "testing"
func TestHello(t *testing.T) {
want := "Hello, world."
if got := Hello(); got != want {
t.Errorf("Hello() = %q, want %q", got, want)
At this point, the directory contains a package, but not a module,
because there is no `go.mod` file.
If we were working in `/home/gopher/hello` and ran `go test` now,
we'd see:
$ go test
ok _/home/gopher/hello 0.020s
The last line summarizes the overall package test.
Because we are working outside `$GOPATH`
and also outside any module,
the `go` command knows no import path for
the current directory and makes up a fake one based
on the directory name: `_/home/gopher/hello`.
Let's make the current directory the root of a module
by using `go mod init` and then try `go test` again:
$ go mod init
go: creating new go.mod: module
$ go test
ok 0.020s
Congratulations! Youve written and tested your first module.
The `go mod init` command wrote a `go.mod` file:
$ cat go.mod
go 1.12
The `go.mod` file only appears in the root of the module.
Packages in subdirectories have import paths consisting of
the module path plus the path to the subdirectory.
For example, if we created a subdirectory `world`,
we would not need to (nor want to) run `go mod init` there.
The package would automatically be recognized as part of the
`` module, with import path
## Adding a dependency
The primary motivation for Go modules was to improve the
experience of using (that is, adding a dependency on)
code written by other developers.
Let's update our `hello.go` to import ``
and use it to implement `Hello`:
package hello
import ""
func Hello() string {
return quote.Hello()
Now let’s run the test again:
$ go test
go: finding v1.5.2
go: downloading v1.5.2
go: extracting v1.5.2
go: finding v1.3.0
go: finding v0.0.0-20170915032832-14c0d48ead0c
go: downloading v1.3.0
go: extracting v1.3.0
go: downloading v0.0.0-20170915032832-14c0d48ead0c
go: extracting v0.0.0-20170915032832-14c0d48ead0c
ok 0.023s
The `go` command resolves imports by using the specific
dependency module versions listed in `go.mod`.
When it encounters an `import` of a package not provided
by any module in `go.mod`, the `go` command automatically
looks up the module containing that package and adds it to
`go.mod`, using the latest version.
(“Latest” is defined as the
latest tagged stable (non-[prerelease]( version,
or else the latest tagged prerelease version,
or else the latest untagged version.)
In our example, `go test` resolved the new import ``
to the module ` v1.5.2`.
It also downloaded two dependencies used by ``,
namely `` and ``.
Only direct dependencies are recorded in the `go.mod` file:
$ cat go.mod
go 1.12
require v1.5.2
A second `go test` command will not repeat this work,
since the `go.mod` is now up-to-date and the downloaded
modules are cached locally (in `$GOPATH/pkg/mod`):
$ go test
ok 0.020s
Note that while the `go` command makes adding a new dependency
quick and easy, it is not without cost.
Your module now literally _depends_ on the new dependency
in critical areas such as correctness, security, and proper licensing,
just to name a few.
For more considerations, see Russ Cox's blog post,
“[Our Software Dependency Problem](”
As we saw above, adding one direct dependency often
brings in other indirect dependencies too.
The command `go list -m all` lists the current module
and all its dependencies:
$ go list -m all v0.0.0-20170915032832-14c0d48ead0c v1.5.2 v1.3.0
In the `go list` output, the current module,
also known as the _main module_,
is always the first line,
followed by dependencies sorted by module path.
The `` version `v0.0.0-20170915032832-14c0d48ead0c`
is an example of a
which is the `go` command's version syntax
for a specific untagged commit.
In addition to `go.mod`, the `go` command
maintains a file named `go.sum` containing
the expected [cryptographic hashes]( of the content of specific module versions:
$ cat go.sum v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZO... v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:Nq... v1.5.2 h1:w5fcysjrx7yqtD/aO+QwRjYZOKnaM9Uh2b40tElTs3... v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPX... v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/Q... v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9...
The `go` command uses the `go.sum` file to ensure that
future downloads of these modules retrieve the same bits
as the first download,
to ensure the modules your project depends on
do not change unexpectedly,
whether for malicious, accidental, or other reasons.
Both `go.mod` and `go.sum` should be checked into version control.
## Upgrading dependencies
With Go modules, versions are referenced with semantic version tags.
A semantic version has three parts: major, minor, and patch.
For example, for `v0.1.2`, the major version is 0, the minor version is 1,
and the patch version is 2.
Let's walk through a couple minor version upgrades.
In the next section, well consider a major version upgrade.
From the output of `go list -m all`,
we can see we're using an untagged version of ``.
Let's upgrade to the latest tagged version and test that everything still works:
$ go get
go: finding v0.3.0
go: downloading v0.3.0
go: extracting v0.3.0
$ go test
ok 0.013s
Woohoo! Everything passes.
Let's take another look at `go list -m all` and the `go.mod` file:
$ go list -m all v0.3.0 v1.5.2 v1.3.0
$ cat go.mod
go 1.12
require ( v0.3.0 // indirect v1.5.2
The `` package has been upgraded to the latest tagged version (`v0.3.0`).
The `go.mod` file has been updated to specify `v0.3.0` too.
The `indirect` comment indicates a dependency is not used directly
by this module, only indirectly by other module dependencies.
See `go help modules` for details.
Now let's try upgrading the `` minor version.
Start the same way, by running `go get` and running tests:
$ go get
go: finding v1.99.99
go: downloading v1.99.99
go: extracting v1.99.99
$ go test
--- FAIL: TestHello (0.00s)
hello_test.go:8: Hello() = "99 bottles of beer on the wall, 99 bottles of beer, ...", want "Hello, world."
exit status 1
FAIL 0.014s
Uh, oh! The test failure shows that the
latest version of `` is incompatible with our usage.
Let's list the available tagged versions of that module:
$ go list -m -versions v1.0.0 v1.2.0 v1.2.1 v1.3.0 v1.3.1 v1.99.99
We had been using v1.3.0; v1.99.99 is clearly no good.
Maybe we can try using v1.3.1 instead:
$ go get
go: finding v1.3.1
go: downloading v1.3.1
go: extracting v1.3.1
$ go test
ok 0.022s
Note the explicit `@v1.3.1` in the `go get` argument.
In general each argument passed to `go get` can take
an explicit version; the default is `@latest`,
which resolves to the latest version as defined earlier.
## Adding a dependency on a new major version
Let's add a new function to our package:
`func Proverb` returns a Go concurrency proverb,
by calling `quote.Concurrency`, which is provided by
the module ``.
First we update `hello.go` to add the new function:
package hello
import (
quoteV3 ""
func Hello() string {
return quote.Hello()
func Proverb() string {
return quoteV3.Concurrency()
Then we add a test to `hello_test.go`:
func TestProverb(t *testing.T) {
want := "Concurrency is not parallelism."
if got := Proverb(); got != want {
t.Errorf("Proverb() = %q, want %q", got, want)
Then we can test our code:
$ go test
go: finding v3.1.0
go: downloading v3.1.0
go: extracting v3.1.0
ok 0.024s
Note that our module now depends on both `` and ``:
$ go list -m v1.5.2 v3.1.0
Each different major version (`v1`, `v2`, and so on) of a Go module
uses a different module path: starting at `v2`, the path must end in the major version.
In the example, `v3` of `` is no longer ``: instead,
it is identified by the module path ``.
This convention is called
[semantic import versioning](,
and it gives incompatible packages (those with different major versions)
different names.
In contrast, `v1.6.0` of `` should be backwards-compatible
with `v1.5.2`, so it reuses the name ``.
(In the previous section, `` `v1.99.99`
_should_ have been backwards-compatible
with `` `v1.3.0`, but bugs or incorrect client assumptions about
module behavior can both happen.)
The `go` command allows a build to include at most one version of
any particular module path, meaning at most one of each major
version: one ``, one ``, one ``,
and so on.
This gives module authors a clear rule about possible duplication
of a single module path: it is impossible for a program to build with both
` v1.5.2` and ` v1.6.0`.
At the same time, allowing different major versions of a module
(because they have different paths)
gives module consumers the ability to
upgrade to a new major version incrementally.
In this example, we wanted to use `quote.Concurrency` from `rsc/quote/v3 v3.1.0`
but are not yet ready to migrate our uses of ` v1.5.2`.
The ability to migrate incrementally
is especially important in a large program or codebase.
## Upgrading a dependency to a new major version
Let's complete our conversion from using `` to using only ``.
Because of the major version change, we should expect that some APIs may have
been removed, renamed, or otherwise changed in incompatible ways.
Reading the docs, we can see that `Hello` has become `HelloV3`:
$ go doc
package quote // import ""
Package quote collects pithy sayings.
func Concurrency() string
func GlassV3() string
func GoV3() string
func HelloV3() string
func OptV3() string
(There is also a
[known bug]( in the output;
the displayed import path has incorrectly dropped the `/v3`.)
We can update our use of `quote.Hello()` in `hello.go` to use `quoteV3.HelloV3()`:
package hello
import quoteV3 ""
func Hello() string {
return quoteV3.HelloV3()
func Proverb() string {
return quoteV3.Concurrency()
And then at this point, there's no need for the renamed import anymore,
so we can undo that:
package hello
import ""
func Hello() string {
return quote.HelloV3()
func Proverb() string {
return quote.Concurrency()
Let's re-run the tests to make sure everything is working:
$ go test
ok 0.014s
## Removing unused dependencies
We've removed all our uses of ``,
but it still shows up in `go list -m all` and in our `go.mod` file:
$ go list -m all v0.3.0 v1.5.2 v3.1.0 v1.3.1
$ cat go.mod
go 1.12
require ( v0.3.0 // indirect v1.5.2 v3.0.0 v1.3.1 // indirect
Why? Because building a single package, like with `go build` or `go test`,
can easily tell when something is missing and needs to be added,
but not when something can safely be removed.
Removing a dependency can only be done after
checking all packages in a module,
and all possible build tag combinations for those packages.
An ordinary build command does not load this information,
and so it cannot safely remove dependencies.
The `go mod tidy` command cleans up these unused dependencies:
$ go mod tidy
$ go list -m all v0.3.0 v3.1.0 v1.3.1
$ cat go.mod
go 1.12
require ( v0.3.0 // indirect v3.1.0 v1.3.1 // indirect
$ go test
ok 0.020s
## Conclusion
Go modules are the future of dependency management in Go.
Module functionality is now available in all supported Go versions
(that is, in Go 1.11 and Go 1.12).
This post introduced these workflows using Go modules:
- `go mod init` creates a new module, initializing the `go.mod` file that describes it.
- `go build`, `go test`, and other package-building commands add new dependencies to `go.mod` as needed.
- `go list -m all` prints the current modules dependencies.
- `go get` changes the required version of a dependency (or adds a new dependency).
- `go mod tidy` removes unused dependencies.
We encourage you to start using modules in your local development
and to add `go.mod` and `go.sum` files to your projects.
To provide feedback and help shape the future of dependency management in Go,
please send us
[bug reports]( or [experience reports](
Thanks for all your feedback and help improving modules.