How to make Go modules with version v2+ avoid the import path prefix requirement?

964 Views Asked by At

This is specific for: https://github.com/twitchtv/twirp

This library is currently on version v7, and we would like to migrate it into go modules. The library is currently imported from other libraries like this:

// go.mod
require github.com/twitchtv/twirp v7.2.0+incompatible

// .go code
import "github.com/twitchtv/twirp"

If we add a go.mod file in the library, now every other library is forced to change the import paths like this:

// go.mod
require github.com/twitchtv/twirp v7.2.0

// .go code
import "github.com/twitchtv/twirp/v7"

How can we migrate the Twirp library to use go modules without forcing other libraries to update their import path?

The main issue here is that a single service may import multiple Twirp clients that were generated on different versions, and make use of helper functions that depend on specific types. Forcing the import path update would force all those helper functions to require managing both the old and new types, and we can't make aliases for all types (some types are functions). This would create an upgrade lock. Does this mean that the Twirp library needs to stay in +incompatible mode forever?

1

There are 1 best solutions below

3
On

How can we migrate the Twirp library to use go modules without forcing other libraries to update their import path?

You can't.

If your go.mod declares the module as github.com/twitchtv/twirp/v7, then every client library (and your own packages) will have to update the import path. That's just how semantic import paths were intended to work in Go modules.

If you use v1 or v0 module path — which is written without the suffix — you are breaking the semver contract, since your project actually has already v7 tags.

The main issue here is that a single service may import multiple Twirp clients that were generated on different versions

Migrating to Go modules explicitly solves this in a clean way, i.e. clients that import multiple versions of your project just declare imports as github.com/twitchtv/twirp/vX etc, making it immediately apparent also in .go files what versions they are using.

Actually you have a very easy way to deal with this. Just migrate to Go modules declaring your module as with the next major version suffix github.com/twitchtv/twirp/v8, then tag it v8.x.y. This is correct from a semver perspective, since your change is indeed breaking — your clients must change the import paths.

This way, clients that do want to migrate to your Go modules version will know what to expect and deal with the implications of switching to a new major version, including rewriting the import paths. Whereas clients that do not want to migrate can keep importing a v7.x.y (or less) as +incompatible.