Skip to Content
Course content

43: Semantic Import Versioning

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Why do I have to change the import path for version 2? Can't I just update my go.mod file?

I get this question a lot because it feels counter-intuitive. In most languages, you just bump the version number in a config file and you're done. But Go does something different called Semantic Import Versioning (SMIV). In Go, a major version bump (v2.0.0 and above) is treated as a completely different package.

Here is the "why": imagine you're using a library for processing payments, let's call it paylib. You're using v1. Now, you want to use a new feature in v2. But you also depend on a third-party logging library that *also* uses paylib, but it's still stuck on v1. If Go just "updated" the version, you'd have a collision. By adding /v2 to the import path, Go allows both versions to coexist in the same binary without fighting. It's a bit of a chore for you as the developer, but it saves you from a massive dependency nightmare later.

How exactly do I implement this in my module?

When you're ready to move from v1 to v2, you don't just tag the commit; you have to change the identity of the module itself. You do this by appending the version suffix to your module path in the go.mod file.

// Old go.mod
module github.com/username/paylib

// New go.mod for v2
module github.com/username/paylib/v2

Now, here is the part people usually forget: every single file in your project that imports other packages within the same module must also be updated. If you have a internal package called client, you can't just import "github.com/username/paylib/client" anymore. You must use:

import "github.com/username/paylib/v2/client"

I usually recommend doing a global find-and-replace across the whole project to handle this. It's the fastest way and ensures you don't miss a single file.

What about v0 and v1? Do I need to add /v0 or /v1 to my paths?

Nope. Go makes a special exception for the "initial" stages of a project. Any version before v2.0.0—meaning v0.x.x and v1.x.x—does not use a version suffix in the import path. You just use the base URL.

So, if you're starting a new project today, your go.mod looks like this:

module github.com/username/paylib

And everyone imports it as "github.com/username/paylib". The /v2, /v3, etc., only kick in once you've hit that first major breaking change. I've seen some developers try to force /v1 into their paths because they want to be "explicit," but don't do that. It's not the Go way and it will actually confuse the toolchain.




📋 Practical Task

Migrating the PayLib Payment Processor to v2

You are maintaining a library called github.com/dev/paylib. It is currently at v1. You have introduced breaking changes to the API and need to migrate the project to v2 according to Semantic Import Versioning rules.

Your Task:

  • Modify the go.mod file to reflect the major version bump.
  • Update the import statement in processor.go so that it correctly imports the internal wallet package from the same module.
  • Ensure the package name itself remains the same, but the import path is updated.

Initial go.mod:

module github.com/dev/paylib

go 1.21

Initial processor.go:

package paylib

import "github.com/dev/paylib/wallet"

func ProcessPayment() {
    wallet.Charge()
}
Rating
0 0

There are no comments for now.

to be the first to leave a comment.