Go
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions and Methods
-
Section 4: Concurrency
-
Section 5: Packages and Tooling
-
Section 6: More Standard Library
-
Section 7: Building Services
-
Section 8: Advanced Go
-
Section 9: Go in the Cloud-Native Ecosystem
-
Section 10: Data Structures and Algorithms in Go
-
Section 11: Testing and Deployment
-
Section 12: Practical Projects
-
Section 13: More Standard Library Practice
-
Section 14: More Practice Projects
-
Section 15: Design Patterns in Go
-
Section 16: Interview Practice
-
Section 17: Package fmt In Depth
-
Section 18: Package strings and strconv
-
Section 19: Package os and io
-
Section 20: Package time
-
Section 21: Package sort and container
-
Section 22: Package encoding
-
Section 23: Package net/http In Depth
-
Section 24: Package context
-
Section 25: Package regexp and bytes
-
Section 26: Package errors In Depth
-
Section 27: Package crypto and hash
-
Section 28: Package flag and log
-
Section 29: Package sync In Depth
-
Section 30: More Practice Exercises
-
Section 31: Go Modules and Workspaces In Depth
-
Section 32: Generics Deep Dive (Go 1.18+)
-
Section 33: Testing Package In Depth
-
Section 34: More Interview and Whiteboard Practice
-
Section 35: Package math and unicode
-
Section 36: Package path and filepath
-
Section 37: Package database/sql
-
Section 38: Package text/template and html/template
-
Section 39: Package archive and compress
-
Section 40: Lower-Level net Package
-
Section 41: Package runtime and reflect
-
Section 42: Package embed and io/fs
-
Section 43: Go Assembly and CGO Basics
-
Section 44: Building CLIs and TUIs
-
Section 45: Go Performance Tuning
-
Section 46: More Real-World Projects
-
Section 47: Go in Production
-
Section 48: Go Design Patterns
-
Section 49: Go Interfaces Deep Dive
-
Section 50: Final Practice and Review
43: Semantic Import Versioning
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.modfile to reflect the major version bump. - Update the import statement in
processor.goso that it correctly imports the internalwalletpackage 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()
}
There are no comments for now.