Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Lane Wagner
Lane Wagner

Posted on • Originally published atqvault.io on

     

Go’s Major Versioning Sucks – From a Fanboy

Go's Major Versioning Sucks

The postGo’s Major Versioning Sucks – From a Fanboy first appeared onQvault.

I’m normally a fan of the rigidity within the Go toolchain. In fact, we use Go on thefront and backend at Qvault. It’s wonderful to have standardized formatting, vetting, and testing across the entire language. The first real criticism I’ve had is with the way Go modules handle major versions. It’s over-the-top opinionated and slows down development in a significant number of scenarios.

Refresher on “Go Mod”

Go modules, and the associated commandsgo mod andgo get can be thought of as Go’s equivalents to NPM and Yarn. The Go toolchain provides a way to manage dependencies and lock the versions that a collection of code depends on.

One of the most common operations is to update a dependency in an existing module. For example:

#updatealldependenciesgoget-u./...#addmissingandremoveunuseddependenciesgomodtidy#savealldependencycodeintheproject's"vendor"foldergomodvendor
Enter fullscreen modeExit fullscreen mode

Semantic Versioning

Go modules use git tags and semantic versioning to keep track of the versions of dependencies that are compatible with the module in question. Semantic versioning is a way to format version numbers and it looks like this:v{MAJOR}.{MINOR}.{PATCH}. For example,v1.2.3.

Each number is to be incremented according to the following standards:

MAJOR version when you make incompatible API changes,MINOR version when you add functionality in a backwards compatible manner, andPATCH version when you make backwards compatible bug fixes.
Enter fullscreen modeExit fullscreen mode

Package-Side Problems

Go has decided that all versions beyondv0 andv1 are required to use the major version in the module path. There are two ways to accomplish this.

The first and recommended way is laid out in an example on theGo Blog:

To start development onv2 ofgithub.com/googleapis/gax-go, we’ll create a newv2/ directory and copy our package into it.

In other words, for every major version, we are encouraged to maintain a new copy of the entire codebase. This is also the only way to do it if you want pre-modules users to be able to use your package.

The second way is to just change the name of your module ingo.mod. Fore example,module github.com/lane-c-wagner/go-tinydate would becomemodule github.com/lane-c-wagner/go-tinydate/v2. Besides this not working for older versions of Go, I also find it problematic because it breaks (in my mind) one of the most useful things about module names – they reflect the file path.

Package-Side Solutions

Allow package maintainers to specify the major version simply by updating git tags, no module name changes required. There is no need for two sources of truth.

We can enforce safe updating by adding warnings or prompts to thego get CLI. We don’t have to add unnecessary time-consuming policies.

Client-Side Problems

When new versions of dependencies are released we have a simple command to get the newest stuff:go get -u. The problem is that this command has no way to automatically update to a new major version. It will only download new minor changes and patches. There isn’t even a console message to inform you that a new major version exists!

That said, the reason for not auto-updating is clear, and to be fair, well-founded:

If an old package and a new package have the same import path, the new package must be backwards compatible with the old package.

Import compatibility rule

In other words, we should only increment major versions when making breaking changes, and if breaking changes are made they can’t have the same import path. While this makes sense, I think a simple console warning would have been a better solution than forcing a cumbersome updating strategy on the community.

Another problem on the client-side is that we don’t only need to updatego.mod, but we actually need togrep through our codebase and change each import statement to point to the new major version:

Users who wanted to usev2 had to change their package imports and module requirements togithub.com/googleapis/gax-go/v2.

Instead of a few simple CLI commands to get the latest dependencies, we are making changes to the code itself.

Client-Side Solution

go get -u should have an additional command line flag to update major versions, and should default to showing a warning that there is a newer major version you don’t have yet.

Import paths should not change between major versions.

Why This Sucks For Me

It is often the case that I want to build a package that has domain-specific logic and will only be used only in services at the small company I work for. For example, we have a repo that holds thestruct{} definitions for common entities used across our system.

Occasionally we need to make backward-incompatible changes to those struct definitions. If it were an open-source library we wouldn’t make changes so often, but because it’s internal and we are aware of all the dependencies, we change the names of exported fieldsregularly. We aren’t changing names because we chose bad ones to begin with, we are usually changing names because requirements from the business change rapidly in a startup.

This means major version changes are a fairly regular occurrence. Some say that we should just stay onv0, and that’s a reasonable solution. The problem is these ARE production packages that are being used by a wide number of services. We WANT to Semver.

Go makes updating major versions so cumbersome that in the majority of cases, we have opted to just increment minor versions when we should increment major versions. We want to follow the proper versioning scheme, we just don’t want to add the unnecessary steps to our dev process.

Hey – I Get It

I understand why these decisions were made – and I even think in a lot of cases they were great decisions. For any open-source or public facing module this makes great sense. The Go toolchain is enforcing strict rules that encourage good API design.

In their effort to make public APIs great, they made it unnecessarily hard to have good “local” package design.

There is anopen issue on Github that would make new major versions more discoverable from the CLI. Take a look at that if you are interested.

Go still has the best toolchain and ecosystem. NPM and PIP can suck it.

If you disagree, @ me on Twitter.

Thanks For Reading!

Follow us on Twitter@q_vault if you have any questions or comments

Take somecoding courses on our new platform

Subscribe to our Newsletter for more programming articles

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Golang and javascript dev interested in distributed systems and cryptography
  • Location
    Utah
  • Education
    Dixie State University
  • Work
    Lead Software Engineer at Nuvi
  • Joined

More fromLane Wagner

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp