4 ms·
Apologies, 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 b
by mcbrit 3y ago
Apologies, 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).
- nextaccountic 3y agoThat's how Rust editions work too. You can have a Rust 2021 crate depend on a Rust 2015 crate which depends on a Rust 2018 crate, it will all be compiled by the latest compiler even though they are written in slightly different languages (different syntax and in some cases different desugaring) So that's how Rust can make language changes without splitting the ecosystem and without requiring everyone to migrate all at once To think about it, Java is like this too