Go has a strong emphasis on “don’t break things” and so they somewhat discourage the idea of a “v2”. However, sometimes you legitimately need to make a breaking change to your API even though it’s hit production.
Here’s what you need to know to not have a bad time:
Go Version Bumping Cheat Sheet
-
Update the package name in
go.mod
to have a/v2
(or/v3
,v4
, etc) suffix, and commit it.module github.com/example/project/v2 go 1.12
-
Start with
v2.0.0-pre.1
. If you get everything right on the first try you can delete it and retag it asv2.0.0
without any problems.# add the git tag git tag v2.0.0-pre.1 git push --tags # remove it git tag -d v2.0.0-pre.1 git push origin :v2.0.0-pre.1 # add the v2.0.0 proper when you're ready git tag v2.0.0 git push --tags
-
Fetch your package from a dependency. Use
@<tag>
or@master
, not@latest
.export GO111MODULE=on go get -u github.com/example/project/v2@v2.0.0-pre.1
-
Update import paths in your depedencies, README, GoDoc links, and other documentation.
import ( "github.com/example/project/v2" )
(note that the import name stays the same)
[GoDoc](https://pkg.go.dev/github.com/example/project/v2)
I recommend using sd to do this, because it makes it a snap:
sd 'github.com/example/project' 'github.com/example/project/v2' go.* *.go */*.go */*/*.go sd 'github.com/example/project' 'github.com/example/project/v2' */*/*/*.go */*/*/*.go
-
Run
go mod tidy
to be sure no lingering non-v2 references remain.
More details below, including cache-busting and tag / version debugging, if you need it.
Have a nice day! :D
Start with v2.0.0-pre.1
That’s called a “psuedo-version”.
Typically before you push straight into a backwards-compatibility guaranteed v2.0.0, you want a little wiggle room to play around and make sure everything works. Using a psuedo-version is a good way to do this.
@latest caches, @master does not
Even after you’ve updated your project correctly, a go get
like this may still fail for a while due to caching.
go get -u github.com/example/project/v2@latest
If you go get
like this, the current master
will be force-checked and you’ll get the expected (or at least accurate) results.
go get -u github.com/example/project/v2@master
Additionally, if you find that @v2.0.0-pre.1
isn’t working for you, it may have been negatively cached. Bump to @v2.0.0-pre.2
and try again. Remember: You’ll never run out of psuedo-versions and you can always delete them once you publish the real, working v2.0.0
. :D
Debugging: Listing versions
Sometimes you just need to know what Go is seeing. Here’s how you can do that:
go list -m -json git.rootprojects.org/root/go-gitver/v2@master
{
"Path": "git.rootprojects.org/root/go-gitver/v2",
"Version": "v2.0.2-pre.1",
"Time": "2020-10-10T22:33:22Z"
}
Alternative Option: A ‘v2’ Directory
If you want to simultaneously maintain v1 and v2, or to build v2 as a wrapper around v1 (or vice-versa), you can use a v2
directory instead of bumping your go.mod to v2.
github.com/example/project
├── go.mod
├── sprocket.go
└── v2
└── sprocket.go
In this case you should keep your go.mod
plain, without the v2
suffix:
module github.com/example/project
go 1.12
In this case you can use both v1.x
and v2.x
tags on your repo and go get
will fetch the correct latest one based on the v2
suffix.
See an example
Take a look at go-gitver
, which has a v2
go.mod
.