panurus

Best Practices for Versioning Go Projects

Effective versioning is crucial for managing dependencies, ensuring stability, and communicating changes in your Go projects. With the introduction of Go Modules, Go has a canonical way to handle versions. This guide outlines the best practices to follow.

1. Embrace Go Modules

Go Modules are the official dependency management system for Go and are central to versioning. If you’re starting a new project or maintaining an existing one, ensure it’s using Go Modules.

2. Adhere to Semantic Versioning (SemVer)

Go modules use Semantic Versioning (SemVer) 2.0.0. Version numbers are typically in the format vMAJOR.MINOR.PATCH.

Key SemVer Points in Go:

3. Use Git Tags for Releases

When you release a new version of your module, you should create a Git tag with the semantic version number.

4. Managing Major Versions (v2 and Beyond)

This is a critical aspect of Go versioning due to the “import compatibility rule.” When you release a v2 or higher major version (which implies breaking changes):

Strategies for Structuring Major Versions in Your Repository:

  1. Major Version Subdirectory (Recommended by Go Team):
    • Create a new directory in your repository for the new major version (e.g., v2/, v3/).
    • Copy the v1 codebase into this new directory as a starting point.
    • The go.mod file within this subdirectory will declare the /v2 module path (e.g., module github.com/yourusername/yourproject/v2).
    • The v0/v1 code can remain in the root directory or its own v1/ directory.
    • Tags for v2 releases would be like v2.0.0, v2.0.1, etc. (If the v2 code is in a v2/ subdirectory, the Go toolchain understands this. Some projects tag like v2/v2.0.0 but this is less common now).
    • Pros: Compatible with older Go versions (pre-Go 1.11 GOPATH mode), allows concurrent development of multiple major versions.
  2. Major Version Branch:
    • Create a new Git branch for the new major version (e.g., v2-branch).
    • In this branch, update the go.mod file to include the major version suffix in the module path.
    • Tag releases from this branch (e.g., v2.0.0).
    • Pros: Clearer separation in version control, cleaner repository structure without duplicated directories in the main branch.
    • Cons: Users might need to be more aware of which branch to track for a specific major version if they are not just relying on go get.

Considerations for Major Version Updates:

5. Pseudo-Versions

When you go get a specific commit hash or a branch name that hasn’t been tagged with a semantic version, Go tools will generate a “pseudo-version” in your go.mod file.

6. Publishing Your Module

Go modules are typically published by pushing tags to a version control repository (e.g., GitHub, GitLab).

  1. Commit your changes.
  2. Tag the commit: git tag vX.Y.Z
  3. Push the tag: git push origin vX.Y.Z

The Go proxy (and other users) will then be able to discover and download this version.

7. Multi-Module Repositories

While the common practice is one module per repository, Go does support multiple modules within a single repository.

8. General Git Best Practices

While not specific to Go versioning, good Git hygiene supports a clean versioning strategy:

Conclusion

By following these best practices, particularly adhering to Semantic Versioning and understanding how Go Modules handle major versions, you can create a robust and predictable versioning scheme for your Go projects. This benefits both you as the maintainer and the users of your module.