New Go package versioning proposal promises compatibility without complexity
Go has always had something of a problem with incorporating version information with goinstall and go get. While the Go community has created a number of unofficial tools to deal with package versioning, a new, official proposal is now under consideration to provide backwards capability without increasing complexity.
How do you solve a problem like package versioning? From the very beginning, Go has had decentralized import paths without any official way to show version information. For years, this has been patched over with community-built add-on tools. But, much like creating a new standard, it just meant that there were too many solutions available.
A new standard had to be built; after GopherCon in 2015, the community coalesced around Dep, a package versioning approach with tagged semantic versions, a manifest, a lock file, and a SAT solver to decide which versions to use. Released in January 2017, Dep was a clear step above most other Go vendoring tools.
However, as the community moved towards Dep, there were concerns. Dep lacks support for gradual code upgrades in large programs. It’s also incredibly intricate, with little change to reduce the complexity.
Compatibility is key
Go 1’s big feature wasn’t actually any kind of language change or special function. Instead, it was a simple promise: Go’s insistence on backwards compatibility. This stability helped propel Golang into popularity. Even though there might be significant changes between version releases, Go developers could rely on it for production use.
“If an old package and a new package have the same import path, the new package must be backwards compatible with the old package.”
Semantic versioning is the de facto standard for software versions in Go, and later versions need to be backwards compatible with earlier versions within the same major release. This means that v1.2.3 needs to be compatible with v1.2.1 and v1.1.5, but v2.3.4 doesn’t have to at all.
However, this leads to problems if there is any gradual code repair or partial code upgrades. Most large packages for a particular dependency don’t upgrade from v1 to v2 at the same time. So, obviously, the program is running Franken-code, with some upgraded code and some of the original code. You can’t give them the same import path – that would violate the import uniqueness rule. So, if you want partial code upgrades, import uniqueness, and semantic versioning, you need semantic import versioning.
A new proposal from Russ Cox called vgo suggests that import compatibility and semantic versioning together require semantic import versioning. Version numbers are not a matter of taste, but of logical necessity. Building a system with semantic import versioning allows developers to create partial code upgrades or import uniqueness.
Import compatibility also simplifies version selection. Choosing the best option with Cargo or Dep requires solving Boolean satisfiability, an expensive solution. Yet, if a developer relies on import compatibility, Go can use a trivial linear algebra to choose the best possible configuration.
Russ Cox calls this algorithm minimal version selection, which should eliminate the need for separate lock and manifest files. Instead, the algorithm replaces them with a single configuration file that can be edited directly by developers and tools alike. Plus, it still supports reproducible builds. Using the import compatibility rule by introducing semantic import versioning eliminates the complexity of incompatible import paths, leading to a much simpler system.
Additionally, vgo also includes a number of significant changes to Go: a Go module, or a collection of packages versioned as a unit; verifiable and verified builds; and version-awareness throughout the go command, enabling work outside $GOPATH and the elimination of (most) vendor directories.
SEE MORE: Go is being used to create smart contracts in building blockchain — That might explain its recent demand surge
Russ Cox has put an awful lot of thought into this proposal. There’s a comprehensive seven-part series explaining the thought process that led him to vgo. Obviously, this is a choice for the Go community at large to see if they want to adopt vgo as an official function. This includes the necessity of adopting the import compatibility rule and semantic import versioning. Sure, this might mean more work during the initial development phase, but it should reduce complexity and issues downstream for Go users and devs.
If you’re interested in learning more about the vgo proposal, there is a healthy conversation going on GitHub as well as an in-depth look at what this feature would look like in action with the “Go & Versioning” series.