Hacker News new | ask | show | jobs
by mark242 568 days ago
The problem is not so much React as it is the JS ecosystem, but React is just very visible when you have these issues because there are so so so many packages being imported.

And the root of the problem is peer dependencies and the JS community's lack of backwards compatibility and maintenance.

Take any decently-sized JS application, whether React or whatever else. Put it in Github. Turn on dependabot. Watch your pull requests go up by 5-10 PRs per week, just to bump minor versions, and then watch how 1 of those PRs, every single time, fails because of a peer dependency on a lower version.

This has been a problem forever in the community, and there's no good solution. There's also just no feasible way to make a solution due to the nature of the language and the platform itself. You just have to absorb that problem when you decide to use eg Node for your backend code or React/etc for your frontend code.

15 comments

Reminds me of the Rich Hickey talk Speculation[1]. There is a special place in hell reserved for programmers that break back compat (for non-security impacting reasons) with widely used libraries, including google's guava developers. Linus Torvalds seems to be the only engineer with his head on straight on this topic and he has to constantly dive in and berate people that are trying to violate it in his project.

[1] https://www.youtube.com/watch?v=oyLBGkS5ICk

The culture around JS seems to believe that, between semver and package version pinning, there's some kind of mandate to do things like massive incompatible API refactoring, so long as you update the major version number.

The result is what TFA describes.

Can you provide an example where Google Guava broken backwards compatibility? I have used it for more than 10 years without any issues during upgrades. To be fair, it is a huge library, and I have probably barely used 20%.
Oh, this is common. Fun when you have two different dependencies which both use Guava, but different versions, and you can't upgrade them to a common shared one. The solution there usually means having to shade Guava for one of them, which sucks, but it at least gets things working.

It's not much of a problem when you use it yourself, but it is when your dependencies do.

The JS ecosystem definitely has a big problem from the culture developed in the IE6 era where people developed so many packages working around the limited language and runtime, but React does have part of the blame here: the way it’s designed forces everything into its proprietary model instead of web standards, so you end up with tons of components duplicating other projects but in React or providing shims for those projects, Facebook’s big devrel push prioritized getting started quickly on a proof of concept rather than maintaining a larger app so you had things like Create React App adding nearly 40k dependencies before you had written a single line of code, and the culture of focusing JavaScript over built-in browser functionality (which made some sense in the 2000s when you had users stuck with IE6) means that you’re going a lot of work in runtime JavaScript rather than the browsers’ heavily-optimized C++ – and it’s often hard to change that because it’s not a direct dependency but a nested chain.

This is also why it’s slow and memory hungry: it’s not just the inherent inefficiency of the virtual DOM but also that having such a deep tree makes it hard to simplify - and since interoperability makes it cheaper to switch away, framework developers have conflicting incentives about making it easier.

> inherent inefficiency of the virtual DOM

I was around when vDOM was being called optimized.

I was also around when they called it DHTML.

Get off my lawn!

Same (I started writing JavaScript when it was called LiveScript in the Netscape betas), and I remember how the vDOM hype was conspicuously short on rigorous benchmarks – people would compare it to heavyweight frameworks which did things like touch elements repeatedly or do innerHtml cycles and say it was fast.
I remember when all web pages were set in Times New Roman on a gray background. Unless you were browsing the web using a console application.
More specifically, they would use the default font, which IE in particular had set to Times New Roman, so that is what most people saw. To add insult to injury, there was no way to configure it for a very long time.

To this day I wonder if this particularly strange choice of a serif font that is very clearly intended primarily for printed documents rather than on-screen legibility is why this entire notion of using user-selected fonts for web pages has largely withered. What if they went with, say, Verdana instead?

Same, except my web backgrounds were in sepia. I had one of those old sepia monochrome monitors, so no grey for me. Or colors for that matter.

I even made my first website on that monitor (complete with animated gifs and <blink>, of course) - and seeing it finally on a color monitor was... interesting.

When was vDOM ever called DHTML?

I was around for DHTML days, and as I recall, it was just a generic term for the ability to manipulate the actual (not virtual) DOM programmatically from JS.

React has two dependencies at runtime [0] and react-dom adds one more [1] (two are shared)?

[0] https://npmgraph.js.org/?q=react

[1] https://npmgraph.js.org/?q=react-dom

Can’t help being sarcastic: I have seen a couple of “I ditched React” post-mortems that apparently start with “I decided to stop adding poorly vetted dependencies with poor package maintenance practices”, just worded differently.

It is unsurprising to me if the router library is the first accused. When I was starting with a new project where I am using React, I went through a bunch of router libraries. There are tons, it seems like a low-hanging fruit with many implementations and many people trying to make a living off theirs (can’t blame them for it, unless they make changes for the sake of making changes and to incentivise people to pay for support). Ultimately, I found something off in every one, so I… just decided to not use any!

That is the thing, React is a small rendering library[0] and you are free to build whatever you want around it with as many or as few dependencies as you want. If the ecosystem is popular enough, there will be dependency tree monsters (simply because the ecosystem is extensive and using many dependencies allows package authors to make something impressive with less effort); switching to a less popular ecosystem as a way of dealing with that seems like a solution but a bit of a heavy-handed one.

[0] Though under Vercel it does seem to suffer from a bit of feature creep, RSC and all that, it is still pretty lean and as pointed out has two packages total in its dependency tree (some might say it’s two too many, but it is a far cry from dependency hell).

> React is a small rendering library

Sorry, can't agree. React is a state management library that also implements efficient rendering on top of the DOM diff it computes as it propagates the state changes.

This allows React apps to remain so simple (one mostly linear function per component) and so composable without turning into an unmanageable dish of callback / future spaghetti.

There is a number of other VDOM libraries, but what sets React apart is the data / state flow strictly in one direction. This allows to reap many of the benefits of functional programming along the way, like everything the developer sees being immutable; not a coincidence.

Regarding the size, preact [1] is mostly API-compatible, but also absurdly small (3-4 kB minified), actually smaller than HTMX (10 kB). But with preact you likely also want preact-iso, so the size grows a little bit.

[1]: https://preactjs.com/

> implements efficient rendering on top of the DOM diff

Here your definition of React diverges from reality, I believe.

React does implement state management—it sort of has to to be of any use. It is a flavour of immediate mode GUI, and some way of managing what changes and what does not change between renders is necessary.

However, React famously does not know about DOM or even Web. (Unless you meant DOM in some more general sense?) People use React to make command-line interfaces, output to embedded LCD screens, etc.

Yes, coupling it to Web and DOM and abandoning the separation of concerns that makes core React so general-purpose could probably make React a bit smaller, but I think projects like Preact are welcome to do it instead.

Makes sense: React itself is independent from a particular rendering method. There's react-dom that does the DOM rendering.

I was trying to point at the fact that rendering occurs on top of state management, as opposed to saying that "React is a rendering library".

React notoriously doesn't come with enough state management to properly manage state in anything larger than trivial.
I have yet to see a single case of any state management library actually improve state handling.

Lately I have started removing these libraries were possible and the maintainability has improved a lot.

Thankfully at my latest place there was no one when I came and I haven't seen a more productive team doing frontend.

React comes with useState, useMemo, and useCallback, which is actually enough, but it may be too low-level when you think e.g. in terms of a huge interactive form. It's easy to write your own useWhatever based on these which would factor out your common boilerplate.

I suspect HTMX also does not come with every possible battery included, judging by the proliferation of libraries for HTMX-based projects. Modularity is a strength.

In reality though, making a case to use only the React library in a minimalist setup is just as hard to convince a team filled with people who came up in the past 10 years of front-end development as it is to convince them of using HTMX or web components. Nowadays, when people use React they use the whole kit-and-caboodle, and when they say React they mean all of it.

Personally, I avoid React because I don't want a compile step. I do everything I can to avoid one. And if I do need to use a framework like React, I prefer to isolate it to exactly where I need it instead of using it to build a whole site.

For the first part: yes, but that is why I think it is important to stress dependency vetting.

For the second part, a couple of times when I had to add a bit of purely client-side reactivity to something pre-existing but did not want to introduce any build step I simply aliased createElement() to el(). That said, personally I prefer TypeScript for a project of any size, so build step is implied and I can simply not think about converting JSX. Webpack triggers bad memories but esbuild is reasonable.

FYI I use jsdoc for typing in my IDE so I get the benefit of types without the build step.
Culturally, who writes React apps with only those dependencies? I’ve done it for quick benchmarks and the like, but actual sites almost always have a huge amount of code to load. It’s like saying that you can have a Java app using only builtin libraries: true in theory but rarely in practice.

I found this really noticeable while traveling over the summer with limited bandwidth: the sites which took 5 minutes to fail to load completely all used React or Angular along with many, many other things posturing at being an SPA but the fast sites were the classic server-side rendered PHP with a couple orders of magnitude less JavaScript. It really made me wonder about how we’ve gotten to the point where the “modern” web is basically unusable without a late-model iPhone and fast Wi-Fi or LTE even when you’re talking about a form with a dozen controls.

Most of the problem there are people implementing their own timeouts in javascript instead of relying on the browser. The browser knows the difference between something taking 5m and making no progress vs. something taking 5m and making slow progress. Your application does not.
In this case, it’s simply putting a mountain of code into the critical path. If you have to load 30MB before the page works, it’s just not going to be a good experience. You can try to handle and retry errors but it’s better not to get into that situation in the first place.
That's what I mean. I've seen async loaders that wait 5s and don't see the file, then request it again. Before you know it, you're downloading 50 files of the same file or making 100 api requests to the same api endpoint.
There is a good solution. It's actually a great solution: Write everything in plain JavaScript. You get a great language to develop in. All your problems will go away. No dependency hell. Excellent load times and performance. Superb compatibility.
> You get a great language to develop in

Every single footgun from PHP but wrapped in a nice syntax so people don't understand they are in danger.

I'm deeply impressed by the Javascript community. What they have managed to create using a that language is amazing.

But please people, do yourself and everyone who ever has to touch your code base a favor and use TypeScript.

…and all of a sudden you have a build step. This is how it starts.
you can use fully featured frontend frameworks without a build step. while that may sound ingenious because you are effectively using a prebuilt version of the framework (so there is a build step somewhere) you are not suffering the problems that come with building it yourself. instead your code is used and stored in a way that it will work directly in the browser as long as browsers support javascript.
I was responding to the suggestion to use TypeScript. If you use TS, you have a build step.
Why are you dragging PHP into this?
Because people know they are supposed to dislike PHP.

So I am just taking advantage of that to help spread awareness about the JS problems.

The people who disliked PHP are the ones who created the gargantuan JS mess. Meanwhile, PHP became the only sane development environment.
We're supposed to dislike PHP? Have you tried it in the last 5-6 years?
lol, the php footguns exist in every language. I'm not sure what you mean by that.
This.

In one of my previous job, the main product was 100% pure Javascript (using AngularJS), with a few (vendored) third-party scripts, and it was very nice to work on it.

No package.json, no dependency issues, and above else, the workload we had was always related to business, and almost never related to external technical constraint such as a depreciated dependency.

I... don't think that AngularJS + third-party scripts was what OP meant by plain Javascript.
And I wouldn't say AngularJS v1 is nice to work with either
In general I would agree, but this project managed to avoid the main gotcha of AngularJS (the performance issues & the possibly messy data-flow), so it was holding out suprisingly strong, even pas the maintenance date of AngularJS.

The lack of pre-processing steps, combined with good CSS and a well-formed DOM made it one of the rare project in my work history that didn't create any rewrite-envy.

AngularJS or not, the main point is that avoiding piling layers of tooling that might force you to an upgrade for purely technical reasons was a nice experience.

Maintaining a large typescript codebase with many developers is already a nightmare, I can't imagine what it would be like if intellisense was clueless about the parameters a given function takes.
> Write everything in plain JavaScript.

If you are one programmer it may work, but many of us work in teams. JS is horrible for that as you need a lot of discipline (which often does not carry over well from team to team) in order to write "good JS".

We use Elm now. Elm translates well to JS (quick to compile and Elm is designed to map well to JS). We use Elm libs, but not nearly as much as in the unholy React+jQuery (yes, that's a bad idea) code it replaces.

All is compiled into one bundle. For the browsers the result is much less to download. For us devs it is a very different development flow: once the compile errors (shown in the IDE) are gone, it just works.

Compared to the loads runtime bugs in JS, we are confident this is a huge step forward and a good foundation to build on top of.

For node and other technologies like react, I would prefer less, but fatter libraries that could be optimized at compile time. All those micro packages coming from nowhere and getting updated every day is a big pain.
Or better yet, no dependencies! The built-in's for both JavaScript and the browser APIs are getting better and better every day, but people still reach for things like Lodash and Date libraries when the equivalent functions are built into the language and runtime itself.
Some things are easy now, but some things still require multiple lines of code: https://youmightnotneed.com/lodash. If you only need one or two, sure, just write your own, but if you're maintaining a big project... why waste time debugging these common utility functions? You'd basically just be reinventing lodash, but with fewer community eyes and tests on it. Whoever inherits that is gonna need to debug all your util functions when something inevitably goes wrong. Like for _.pickBy(), none of these are very readable (https://stackoverflow.com/questions/54743996/converting-loda...), another implements it wrong and leaves out the predicate (https://github.com/you-dont-need/You-Dont-Need-Lodash-Unders...), etc. Why do this to your project and fellow devs when it can easily be a single tree-shaken function imported from a popular, well-maintained lib?

If anything, ECMA should just absorb more lodash functions into the standard lib, like they've gradually done with some of the array functions. But common things like that shouldn't be up to each individual programmer & team to reinvent all the time. It just needlessly expands the maintenance surface and causes subtle bugs across teams & projects.

JS Date is in an even worse place. If you ever need to work across time zones on both the server and the client/browser, native JS date is totally unusable because it "loses" the original time zone string and just coerces everything into (basically) utc milliseconds. The Temporal API is supposed to fix that, but I've been waiting for that for nearly a decade: https://tc39.es/proposal-temporal/docs/. That proposal links to https://maggiepint.com/2017/04/09/fixing-javascript-date-get..., which explains some of the weaknesses of the current JS date system.

I agree with you about timezones and I think a library like date-fns (and date-fns-tz) strike a nice balance by not using a bespoke intermediary object type.

As you say, some of them are built in and those should just be used instead in most cases. The problem is that when you leave the choice of library to use, then the choice isn't always obvious, especially for niche use cases. A library that's well-maintained today may not be so tomorrow when the maintainer falls ill, gets burnt out on work + open source work or simply gets bored of the project.

Deno has the right approach in this regard where they are creating standard libraries to go with their runtime which are expected to be maintained in the long term, but even then I'd still prefer built-in APIs in most cases.

Angular was kinda like that, batteries-included, but it lost to React. It wasn't until Next that we got a similar batteries-included big framework for the React world.
But React was not batteries included, which is where the dependency hell came from; Angular comes with routing, API management, the works, but with React people quickly needed additional dependencies like state management (redux & co), routing, API calling, etc.

While Angular feels big and heavy because it's a batteries included framework, React feels simple and quick on the surface, but as the article and the discussion show, it comes with its own price.

Angular and React are not directly comparable.

I think we're saying the same thing? :)
Isn't that Svelte?
As a recent convert, svelte has felt wonderful.
But here's the thing.

It doesn't matter!

If I'm building a personal project, I don't have the same time to curate a full ecosystem stack and nobody in the react system is maintaining those for applications that are put to the side for weeks or months at a time.

As for me, I just restarted a personal project on rails because of its batteries included mentality - it means I can limit the number of dependencies, and they have gotten very good at migration paths and deprecations.

> I just restarted a personal project on rails because of its batteries included mentality - it means I can limit the number of dependencies

https://github.com/rails/rails/blob/main/Gemfile.lock

Just a playful comment - not challenging your experience

Sure, but the framework cares about that for me. I don’t use rails personally but that’s the whole point — someone upstream of me is paying attention and making everything work together.

In contrast, I have work apps made in React that need regular piecemeal updating — routers, form libraries, query managers, CSS — because we’ve chosen to cobble that together ourselves. That’s fine, that’s the path we chose knowingly when we picked the tech we picked, but the point isn’t that frameworks don’t have dependencies — it’s that they take on more of the burden of managing them for you.

Well, Next is kinda like that then. It takes care of the sub-dependencies for you and when you upgrade, you just upgrade to the next major Next version (which isn't necessarily easy, but more so than upgrading 100 individual packages). They provide codemods for some stuff too.
Next is like rails 2. Eventually they will get to rails-like abilities.

I'd say Clojure and java ecosystem is miles ahead of rails as well, but for this project I don't want to play in that garden.

I suspect that most rails, or next, projects add additional dependencies than just the framework. Generally the framework isn't the issue in my experience.
Sure, but it's not an either/or situation. Every big project adds dependencies, but using Next means you have some basic, common functionality included out of the box by default/by convention (like TypeScript, linting, testing, routing, caching, SSR, static builds, serverless definitions, etc.) all done in a predefined way. Maybe your project has 200 deps, but Next would replace like 50 of the big ones that you'd otherwise have to separately install and maintain. Just having a basic page/app router and minimal state system (via contexts and RSC and props and such) reduces a lot of the headaches of the bad old React Router days.

It replaces "React soup of the day" with a more standard "recipe" shared by most Next projects – like "Grandma Vercel's secret React minestrone", I guess. But yes, projects would typically still add their own "spices" on top of those basics.

The main question should always be: why update?

Should a library become compromised with a vulnerability, fine (if said vulnerability is relevant to your usage). If you need a feature only available in a newer version, fine (I’m counting better performance as a feature).

What I’m seeing far too much of is upgrading for the sake of it. It feels like such a waste of dev time. Pinning dependencies should be absolutely fine.

There are two reasons to do frequent updates:

1. Process. As a guiding principle, it is easier to make frequent small steps rather than one big step. There are many reasons for this, and the benefit of frequent small chunks of work apply beyond updates. 2. Security. Frequent updates can improve security posture, for different reasons: you apply undisclosed security fixes without knowing it (not everything is a CVE), prevent unnoticed vulnerabilities (this can be fixed by automated monitoring) and when there is a time-critical upgrade, the work is faster and less risky (see previous reason).

Pinning and updating reactively would be fine and sometimes is, however: there will be security issues, you will have to update. Given that the task is hard to avoid, for any product that is actively maintained and developed I think the better choice is to do it regular updates regardless of security issues. Maybe with good monitoring and for products that are really not developed any further just reacting to security issues is the better choice - its often also a pain though.

You either waste your time updating daily or you rewrite from scratch every 3 years. JS is what happens when you let the inmates run the asylum.
In my experience (~10 years of front end stuffs), the rewrite will happen either way. Code rot, sweeping redesigns, obsolescense, or over-eager consultants will trigger a full front-end rewrite / re-architecture every ~5-10 years.
Yes, but that's 2 to 3 times slower than what happens if you let the code rot.
The issue is that feature or vulnerability might not be patched on older versions. If you are using a 2 year old version and a non-backported vuln or needed feature comes along that means you have to absorb 2 years of breaking changes to move to that version.

Frequent updates allow you to address the breaks gradually rather than all at once.

JS is just awful, though, because of the sprawling dep tree. I get why devs would prefer pinning as any one of the 1000 deps that get brought in could need an update and code changes on any given day. A sticky static version requires less daily maintenance.

It's vastly, vastly easier to upgrade small version bumps constantly via automated tools like renovate than it is to try upgrade several major versions every few years. It's shite being stuck with dependencies the dev team has now put in the "too hard basket" in terms of upgrading because the delta is too scary or difficult and too much code has ossified around the now ancient version. Don't willingly do that to yourself if you can avoid.
I get that, and it’s a good point. But at some point that easy patch/minor version bump becomes a major version with a breaking change, and does take time to upgrade regardless, scary delta and such. My point is that, without an actual feature need or an actual vulnerability (none of these guaranteed to spring up in future), any time spent upgrading is potentially wasted. I know some projects are unlikely to last beyond a few years —- in those cases I think the risk is calculated enough to not matter too much.
It's down to engineering culture at that point. We have a weekly process where we merge those PRs and including any that are failing. It doesn't suck up much time at all, but our stuff is always well maintained with few surprises lurking. The side effect of this type of culture is high quality test suites and pipelines that you have very high confidence in and are executed frequently and quickly. It's overall been a far better experience than just letting stuff rot.
Any security work always involves a calculated risk. The risk here is that you will be forced to do a painful and error prone upgrade at the time of the vulnerability - under pressure. You haven't done that too often, thus the process is unlikely to go smooth. So there may be bitrot, lots of debt, and time pressure to put out a patch: perfect storm for a lot of things to go wrong even if you don't get exploited. It also throws a wrench into your current schedule. This should be part of the risk calculation.

As I web developer I see so many CVEs in mature stacks, and every so often they really do apply to our work. It is hard to avoid updating, unless you kind of pretend those vulnerabilities don't exist or apply (honestly, the vast majority of devs and small orgs do just that). Even monitoring and deciding what vulnerability applies is a recurrent 'waste' of time, sometimes you might as well just do regular updates instead.

One issue I often see is that if you do your job well, any time sunk into security can by definition be seen as wasted. Until that rare moment comes when it is not so, and then it suddenly transforms from wasted time into a business critical or even business ending death crunch.

> My point is that, without an actual feature need or an actual vulnerability (none of these guaranteed to spring up in future), any time spent upgrading is potentially wasted. I know some projects are unlikely to last beyond a few years —- in those cases I think the risk is calculated enough to not matter too much.

You could make the same argument for any kind of code quality efforts. Frankly I think this site probably leans too far into a high-quality mindset, but apart from anything else good programmers won't want to work on a codebase that isn't seen as valuable and treated as such.

I have been building a platform https://github.com/claceio/clace for teams to develop Hypermedia based internal tools. One of the main criteria for the technology stack and the feature set has been making sure apps can be maintained easily, after six months and after six years.

Settled on using Go HTML templates, Starlark and HTMX. Go has a great track record of not breaking backward compatibility. Go templates are widely used by ops teams, any breaking changes there will cause ops teams to revolt. Starlark is somewhat widely used by build systems (like Bazel), any breaking changes there will cause build engineers to rise up in arms. The HTMX 1.9 to 2.0 upgrade was also painless, no changes required in my test apps. Only change required was to update the way the websocket extension is resolved.

Isn't it still a React problem? They could have chosen to not rely on dependencies and you don't have to add more to it.
React itself doesn't have many dependencies at all, but nobody uses React in isolation because it's only a rendering library / data flow; React was never pushed as a full framework.
It’s not a JS problem, it’s a fundamental constraint of working within the browser runtime: https://bower.sh/my-love-letter-to-front-end-web-development
I wouldn't dismiss the responsibility of culture in the JS dependency problem. I agree with a few points in the article but much of these dependencies aren't needed, they are preferred and importing is normalized.

Left-pad wasn't a problem because of browser constraints, it was a problem because of culture and to some extent discipline.

This doesn't excuse some authors who enjoy constantly rewriting their libraries just for the hell of it, consistently introducing breaking API changes.

react-router might be one of the best examples (or the worst, depends on how you look at it), and it's unfortunately very popular, even though sane and stable alternatives exist (like wouter).

OP's article complains specifically about wouter's rate of change!
There's a few solutions that the community won't do for you though. Reduce your dependencies; the author proposes htmx, which is an extension of "just use the platform". For a lot of websites / applications, you don't need these tools; you don't even need a build step, because plain JS is fast and compact, browser support for most features is fine, and intermediates like CDNs or whatnot can handle the "last-mile" optimization of assets if needs be, with HTTP 2/3 being the other newer factor that makes asset optimization / a build step less necessary.

Reduce your update frequency; a lot of the updates of these libraries are trivial, which is both good (fast updates and releases is good, many open source contributors are good) but leads to a high update frequency. But it's fine to run a month behind, the amount of actually critical issues are few and far between. If these projects have their semantic versioning correct, you should be able to see whether updating them once a month requires a lot of work.

The fear, which is justified, is that waiting too long with updates means these compatibility problems add up. Especially when the ecosystem was still figuring itself out and did major backwards-incompatible rewrites (remember Angular 2?) this was a major issue, but it seems to have eased off a bit. Last big one I've run into was when eslint decided to change its config format, and given ESLint's old config could get pretty convoluted already (especially in a monorepo with partially shared configuration and many plugins), changing that was effectively rebuilding the configuration from scratch.

Anyway. I frequently look to the Go ecosystem and attitude for things like this. And it's had an impact on the JS ecosystem too, it was only after Go came out and said "use gofmt, fuck your opinion on formatting and fuck spending time on trivial shit like that" did the JS and other ecosystems follow suit with e.g. Prettier and Biome. I unfondly remember peppering code reviews with dozens of "this single should be a double quote" and "there should be a newline there". Such a waste. Anyway, the Go ecosystem mindset is a healthy one. Go the language gets a lot of justified criticism and it's not for everyone / everything, but Go the mindset does a lot of things right or better, for less developer frustration, better future proofing, and more maintainable software.

Well, designing a module to be a peer dependency and then not strongly favoring backwards compatibility is a choice. When you make that choice you're probably screwing your users, in the long run.

As a user of modules, if you can detect such module, you can choose not to use it, and save yourself all that future trouble.

Now. Let's see. How many times has react's major version number changed?...

Yes, it's not only react, but boy are they an enthusiastic leader of this approach.

We also have a generation of designers who "think in React" who don't approach the web like the web, but like a less form of mobile.

A designer who has a solid understanding of hypermedia and puts its principles first would be worth their weight in gold to a team who wanted to move away from the React ecosystem.

Now to make it even more fun, add something like Synk forcing updates due to security issues.
Yeah React itself is actually a very small part of the ecosystem that has evolved alongside React. And it's a mature ecosystem, therefore there's a lot of libraries to use that come at the cost of keeping them patched.

It's easy to think that a new tech stack is somehow more complete because there are fewer add-ons and no vulnerabilities have been discovered yet.

Yeah even when use sveltekit, its still apparent

but its still pleasant imo