9 ms·
The question for compatibility is not whether but how. And it's not really backwards: you want your code to just work, from now on. Go 1.21 offers two essenti
by w10-1 3y ago
The question for compatibility is not whether but how. And it's not really backwards: you want your code to just work, from now on.
Go 1.21 offers two essential features not matched by any other language ecosystem:
1. A GODEBUG setting for each change, together with per-change opt-out and per-change metric for detecting use of the prior implementation.
2. A per-module toolchain version, together with automatic fetching of both older and newer go toolchains (deployed safely as modules)
As an amazing bonus, if you specify a given version of go (e.g., 1.21.2), then when running under a newer version of go, go will automatically apply the relevant opt-out configuration so you won't get the new behavior until you ask for it.
Finally, as ever you can declare things in code, in go.mod, and in environment.
That basically covers all the use-cases for compatibility, from developers through deployers.
Simple, and beautiful.
- shakow 3y ago> essential features not matched by any other language ecosystem > proceeds to list features abundantly common in other languages Least enthusiastic Go user.
- deleted 3y ago[deleted]
- mcbrit 3y agoApologies, but this actually sounds like a nightmare to me, if you have at least 1-10m+ loc over enough teams, enough teams being probably 5+ depending on org boundaries. Either you support the new version or you don't is more than enough complexity to cause arbitrarily ridiculous problems, because there is a deep valley of you /think/ you support the new version and the new version thinks that it supports you but you both miss. (who wants the t-shirt saying that they have been responsible for a 1m+ code base on top of 10m+ for at least 20 years?)
- jrockway 3y agoIf Team A requires compatibility with go 1.21, their go.mod will start with "go 1.21". Even if the code is compiled with go 1.22, their code will run unchanged, as the toolchain now treats the go version line as the toolchain to be compatible with. Similarly, if Team B requires compatibility with go 1.22 but their code is being compiled with go 1.21, go will download the 1.22 toolchain and run with that instead. It's sneaky and crazy, but so crazy it just might work. (As a user of CircleCI convenience images for a few tests suites, I appreciate this feature. When there is some security vulnerability that requires updating to go 1.21.1, I don't have to wait for Circle to build a new convenience image. I can just change go.mod and start using 1.21.1 immediately. This saves a day of telling people to ignore govulncheck.) The TL;DR is that the compiler version is now something you can declare in your go.mod file like any other dependency. If you share one go.mod file across all teams or have a One Version Policy, then there will always be work to do. No doubt there are several dedicated (in practice) employees to manage "there is a critical security release for github.com/whatever/frob@1.2.3 but the Team A's tests fail when updating to github.com/whatever/frob@1.2.4", which is inevitable at this scale.
- Groxx 3y agoIt's not downgrading (unless that changed recently), it's just new emulating old. And only partially at that. E.g. `go fmt` with a new Go will use new-Go's formatting, not the module's version's formatting (comment format thrashing is fun!). And then they special-case backwards compatibility stuff like the `//go:build` syntax change, and that behavior pays attention to module version. API accessibility and module file formatting follows module version, I don't believe `go vet` does (in general, nor do I think it necessarily should), compiled implementation of stdlib absolutely does not, etc. Rust (cargo) by contrast actually does version the tools, and automatically pulls the stated rustc, stdlib, docs, everything (set in rust-toolchain.toml, among others: https://rust-lang.github.io/rustup/overrides.html https://rust-lang.github.io/rustup/overrides.html). I'm not sure if cargo versions itself or not.
- jrockway 3y agoI believe it's emulated. There's a tradeoff; the old toolchain may actually build code that is vulnerable to some security vulnerability, but the new toolchain will produce (in theory) code that produces the same output from the same input, but without the vulnerability. So it's not clear that either direction is a clear win; old is "known", but old can be dangerous. But, you can only write so many tests to ensure that emulation is as good as the original. Which direction you are less paranoid about dictates the direction you'll go. Having used Go since ~1.3 and having been responsible for the version in use for my project since about ~1.9, I'd say that on average I upgrade on release day and it has never caused a regression. But, the article mentions a handful of bugs that have occurred due to fixing library bugs, so they aren't nonexistent. How much risk you want to take here is up to you.
- Groxx 3y agoYeah, mostly I think emulating is the best approach, and Go seems to strike a pretty good maintenance-load / compatibility tradeoff. Upgrading lints / optimizations / bugfixes is generally preferred. Rust's "true versioning" approach makes more sense for a language without a stable ABI and more backwards-incompatible changes in recent times. Besides, if you want true versioning, there's always gimme. Gimme's easy, and easy to use with folder-env managers (like direnv).
- ilyt 3y agoPerl does something similar; use v5.24 at start of the file will make it work as if the version of Perl was 5.24, and it works on per-file basics, not only per module
- sigzero 3y agoThat's exactly what I thought of when reading the description.
- holiveros 3y agoEven more fine-grained, https://perldoc.perl.org/functions/use#use-VERSION https://perldoc.perl.org/functions/use#use-VERSION > ... Later use of use VERSION will override all behavior of a previous use VERSION, > possibly removing the strict, warnings, and feature added by it ...
- anacrolix 3y agoThis is definitely present in other languages. Mainstream ones. Haskell takes it to the next level where individual files can turn on and off language features.