For young projects, where it's kind of expected (trade-off of being an early adopter), or for small projects (which can be easily maintained by a third party), it's acceptable.
But for bigger/core projects this is far more problematic.
Just for comparisons, a lot of Linux distributions will maintain a specific version for ~5 years if not more (Debian, RedHat), and if for smaller components, they can do things like backport patches, or even create the patches themselves, however, for larger ones, they are reliant on upstream providing stable and maintained versions for the life of a specific version of the distribution.
Also, I've seen quite a lot of large and complex projects taking several years to reach production. In these contexts, having to constantly rework components (and re-test them, both individually and as part of the overall system) is not really feasible.
There is no ideal solution, maintaining backward compatibility is costly, and can lead to messy code bases, and it's legitimate to want to avoid that by deprecating API/functionalities. However, downstream (distribution, users of a given library) needs stability and generally cannot afford to rework their solution every 3 months (keep in mind that downstream generally relies on many upstream components, if a lot of these components break at the same time, even in minor ways, this can result in a huge headaches).
IMHO, a more desirable compatibility period, at least for big/core components, would be around 5 years.
Soon I will be able to let go to Python 2.7, but not just yet. My industry is the precise opposite of "move fast, break things" because here the "things" happens to be what are called humans and their "break" scenario involves shrieking as their lungs are burned to leather, and no amount of Jolt soda poured into the floating holographic head of Mark Zuckerberg will suffice.
If you have a piddly language you and five people circulate on Usenet, go for it. Break it twice a day. But once your language gets serious, be ready for inertia. It's a property of mass.
I used to build things with frameworks and languages with yearly upkeep requirements.
> And those who prefer to live in rot,
What rot? If my application didn't change I'm not sure why my dependencies should need to.
Regarding golang, I hated go get and the go workspace idea when I first started with go.
Then I got over it, now I try to use modules and I just get annoyed, the whole system is confusing and flaky.
All this change is doing is pushing me further into rust. It has problems too but they tend not to do this kind of hand wringing.
Go is much more stable than Rust, you know how many Rust libs need nighlty to work?
Rust libs that use nightly do so because they're opting in to new features, not because released features are unstable.
Even then with the releases of const generics, proc macros, and a couple other big-ticket features, fewer and fewer Rust projects require nightly anyway.
(This is me being curious about your Rust comment, not implying anything about the relative stability of the two languages)
Anyway, thanks. Nightly does exist to be used, but our users largely report using stable, so when people say things like this, I am trying to see if there’s any blind spots.
I know with C, I can rely on it for 45 years. There is something to be said about this and the peace of mind that comes with it.
Obviously, this is a spectrum with extremities and many ways to go about it. We should not discount "Move slowly, and not break things".
Right, but that's at the expense of 50 years of progress. For the overwhelming majority of languages I strongly contend that's the wrong tradeoff.
I think the compiler should just ship with older versions of the language and different translation units can be compiled with older versions of the compiler. So long as the newer versions can interact with the older ABI (which ideally rarely changes), there's no problem.
* Fix all bugs in old version, or
* Ship known buggy code
Neither seems great. Better to just have archival versions with a "here be dragons" warning.
Thank god there's no known bugs in released software!
But more deeply, LTS ships buggy versions of the software. That's the point - only the truly dangerous bugs (e.g. compilation-triggered RCE or something) get fixed, everything else stays compatible even if it's otherwise undesirable or suboptimal. Code depends on those bugs.
Some organizations have a long path to production for runtimes and would prefer to skip releases. Requiring a code change to be tied to the runtime update makes things harder and encourages staying on the old release forever.
Upgrade cycle should be one major version per year (may be less, but not more).
N+1 version does not break compatibility with N version, but introduces warnings for things that's going to be removed. Every warning must have very easy and clear way to resolve it, ideally automatic way, if language and tools support it. Developer must not redesign his application to get rid of warning, but rather just make few obvious mechanical moves.
N+2 version removes previously deprecated things and breaks compatibility with N version.
So you're going to spend few hours once a year to migrate to newer dependency and resolve their warnings. If your software was rotting for 10 years, you're going to spend a week to gradually upgrade, resolve warnings, etc. That should be acceptable to everyone. And those who prefer to live in rot, should pay someone to maintain those outdated libraries (or just live with bugs and vulnerabilities, that's their choice).
With programming languages and foundational frameworks it might be worth to be a little bit more conservative and extend that deprecation period to a few years. Just don't keep deprecated things infinitely.