discussions icon indicating copy to clipboard operation
discussions copied to clipboard

EFI: Express LTS Strategy

Open wesleytodd opened this issue 2 years ago • 49 comments

Many decisions on which tools to use, coding patterns to follow, and generally run some technical decisions depend on how we decide to treat Node.js Version support and how to run major version releases. There is one requirement for an LTS strategy for this project:

  1. We must maintain strict Node.js version support within a single express major

There are some really nice to haves:

  1. Keeping our version support modern so we don't block node core from making progress
  2. Keeping our version support modern so we don't get old outdated bug reports or security issues
  3. Keeping our version support modern so we can adopt newer patterns faster

And then there are a lot of things to discuss around the cost and priorities of these things. I think we need to have this discussion, and I was waiting on things getting sorted more generally, but to keep the conversations focused I am opening this issue now so folks can centralize the conversation someplace without rehasing it in many different dependent issues like #184.

wesleytodd avatar Feb 24 '24 18:02 wesleytodd

Keeping our version support modern so we don't block node core from making progress

This merely requires continually testing on all supported versions of node, which both myself and pkgjs have actions to help you dynamically create a matrix for.

Keeping our version support modern so we don't get old outdated bug reports or security issues

I'm not sure what this means - as long as modern node is supported, a bug on an older supported version of node is still a bug.

Keeping our version support modern so we can adopt newer patterns faster

this really only applies to syntax, and that can be transpiled, so it needn't be a forcing function most of the time.


One of the inherent negative costs of any major bump is that people will be left behind on the old major, and backports for bug and security fixes become much more annoying and unlikely. When a major bump drops support for an engine, it makes upgrade the platform for that user much harder. I strongly suggest not arbitrarily adhering to someone else's support plan (LTS, eg) and instead, decide which node versions to support based on what minimal featureset is needed for express to support the user patterns it wants, and no fewer.

In the event that a dependency has a tighter contraint than express otherwise needs to impose, the tradeoffs should be ad-hoc weighed between letting that dependency make decisions for this project, vs the cost of finding an alternative dependency. (in most cases, i'll be happy to maintain an alternative dependency if that resolves this question)

ljharb avatar Feb 24 '24 19:02 ljharb

this really only applies to syntax, and that can be transpiled

Transpilation is not the best approach for a framework, as that can cause unpredictable runtime overhead in some cases, especially when transpiling to a much older version. And it complicates debugging significantly.

kibertoad avatar Feb 25 '24 02:02 kibertoad

@ljharb If the approach is to base supported version on desired libraries / syntax, it might make sense to start forming a wishlist for these. I'll start:

  1. Modern testing library. My personal recommendation would be vitest, which requires node 18+, which is roughly similar to native Node test runner
  2. Modern validation library. Typebox is Node 16+
  3. Async/await (Node 8+)
  4. Async iterators (Node 10+)
  5. Optional chaining and nullish coalescence (Node 14+).

That to me looks like 18 would be great, and Node 14 would be a bare minimum. What are the alternatives if Node 18 is too high of a bar? Minimalistic stuff like tap?

kibertoad avatar Feb 25 '24 02:02 kibertoad

You say “minimalistic” like it’s not one of the strongest arguments to consider a tool.

ljharb avatar Feb 25 '24 05:02 ljharb

@ljharb It's subjective, of course, but I would take DX over minimalism any day. Adjustable height table that you control by a handle mechanically is minimalistic, but pressing the button to adjust the height is so much faster and more convenient.

If I can get away with not doing something by hand in coding with no runtime drawbacks, I will. And experience of using TAP in IDEs is so poor, I sometimes want to scream when debugging failing tests for fastify libs. And uncomfortable tooling = less outside contributors.

What are the advantages of minimalistic tooling? It's not even about learning curve, because sometimes it's even steeper as a result of having less convenience features available.

kibertoad avatar Feb 25 '24 10:02 kibertoad

Those were the same arguments that supported using jest, and the reasons most people are trying to move away from jest now are the same reasons I’d give.

ljharb avatar Feb 25 '24 17:02 ljharb

@ljharb No, problem with Jest wasn't that it had poor DX. Actually, its API was so good, that both vitest and bun pretty much copied it verbatim, and it still hold up well. Problems with jest was that it a) monkey-patched a lot of stuff that it shouldn't have b) was abandoned by Meta

kibertoad avatar Feb 25 '24 17:02 kibertoad

Yes, I’m saying that the very good DX actually hid a lot of the complexity that turned out to be the root of the unavoidable problem. By avoiding that complexity, even at the cost of some DX, you save years of dev time later. My tape tests have never once needed any significant migration on 500 projects, over a decade ¯\_(ツ)_/¯

ljharb avatar Feb 25 '24 17:02 ljharb

By avoiding that complexity, even at the cost of some DX, you save years of dev time later

Not necessarily. We've migrated our tests from Jest to Vitest without any changes, other than renaming jest. to vitest. and changing the config file. That's a one time migration effort, and then I'm saving time and energy every day.

It's somewhat strange to say that walking is superior to a car because you never need gasoline for it. That's technically true, but tradeoffs involved are way deeper than that.

kibertoad avatar Feb 25 '24 17:02 kibertoad

My tape tests have never once needed any significant migration on 500 projects, over a decade

YMMV on that. I definitely had to do more changes to node-tap tests when bumping major versions in fastify codebase than I had to do to migrate from jest to vitest, or when bumping vitest semver major. That's a benefit of a stable, mature and well-designed API - it doesn't need to change.

kibertoad avatar Feb 25 '24 17:02 kibertoad

Changing a major version support (removing one) would be for me a breaking changed, and I agree to being tied to another project schedule release can be an issue. But we should also work on lowering the need to support a large matrix of versions.

As posted there https://github.com/expressjs/discussions/issues/172 it seems that a lot of other framework (and not all) adhere to something close to supporting the major version for the past 3 years which for us would make it between v14 and v16 if we were releasing a new major version today.

As this discussion is only for Express@5 (we won't change or deprecate versions supported by Express@4) we should discuss about that.
From what @kibertoad was also mentioning this would be quite close - and allow some "new" tooling

sheplu avatar Feb 25 '24 22:02 sheplu

If there is concrete difficulty in supporting a particular version ("having to run CI on it" doesn't count), or a concrete feature needed (the "exports" field, native ESM for some reason, async/await syntax, etc), then it makes perfect sense to constrain the supported versions accordingly. "it's old" is not a sensible reason, nor is "people who aren't us don't support it", is all.

ljharb avatar Feb 26 '24 04:02 ljharb

I think this conversation is a great example of where some agree upon technical priorities would help. I think my ideal is that we find a good happy medium. Yes that means we might drop node versions for sometimes "arbitrary" reasons but IMO we should value "predictable schedule" over "maximal version support" (phrasing might not be great, but the I hope that gets the point across). That doesn't mean we don't value version support, just that we want predictable timelines more than we want to support older versions.

wesleytodd avatar Feb 26 '24 16:02 wesleytodd

Why is that beneficial?

ljharb avatar Feb 26 '24 16:02 ljharb

Because it would help frame discussions which are tradeoff's against a common set of ideals. I think it is clear from the above discussion that there are good reasons and strong arguments for a few different approaches we could take. I would like it not to be left up to persistence of individuals in the discussion and then a TC decision if we can help it. I think a more healthy way to have that tradeoff discussion is to first get on the page for what our goals are.

wesleytodd avatar Feb 26 '24 16:02 wesleytodd

Oh sure - i mean, what's the value in "predictable timelines"?

ljharb avatar Feb 26 '24 16:02 ljharb

@ljharb People who use the framework can plan their upgrades.

kibertoad avatar Feb 26 '24 16:02 kibertoad

@kibertoad right, but no planning is needed unless there's a breaking change. what value would there be in forcing one?

ljharb avatar Feb 26 '24 16:02 ljharb

Not only do we have planned breaking changes (hopefully low impact, but still breaking), I think everyone is better off if we release those breaking changes in a predictable way.

wesleytodd avatar Feb 26 '24 17:02 wesleytodd

Maintaining consistent breadth of supported surface. Any sufficiently complex library eventually reaches state where certain problems and behaviours only affect certain versions of Node. Providing predictable pace of obsoletion is the most healthy approach for the ecosystem. It encourages keeping up with up-to-date versions, but also provides non-surprising cadence one can bring to their management for planning. Encouraging users to update is healthy. Maintainers win, users win. I wonder what inspired your "radical compatibility" philosophy. Are Node upgrades banned at your workplace?

kibertoad avatar Feb 26 '24 17:02 kibertoad

I think a lot of the disagreements in these kinds of conversations are based on some "theory". Lets avoid too much theory crafting and keep the conversation grounded.

Also, to be clear about the value of having some guidance is to avoid the heat which seems to be entering this discussion. I know everyone here wants whats best for users and the project, but we should be careful not to get to deep into the "what inspired" type of comments. @kibertoad ideally we can keep to the topic and less on the motivations of the folks taking those positions.

wesleytodd avatar Feb 26 '24 17:02 wesleytodd

@kibertoad i've had over a decade of workplaces where breaking changes in deps blocked node upgrades for months or years. The way to get people to upgrade node is to make it as easy as possible for them to do so, and having to upgrade multiple things at once increases risk.

@wesleytodd sure, well-planned and pre-announced breaking changes are the best way to do them! but if those breaking changes are avoidable, they should be; and once those breaking changes are made, what would be the benefit in planning for another set prior to the need existing?

ljharb avatar Feb 26 '24 17:02 ljharb

what would be the benefit in planning for another set prior to the need existing?

Not saying we should do that. Again, this is theory crafting. If we go a whole year without landing a single breaking change, GREAT! If we don't, we have a documented support window where we can decide what to do. Maybe we get to that point and decide to skip the major and extend support for the old major. No one is going to complain about that lol. What they would complain about is a series of quick majors in an unpredictable way.

wesleytodd avatar Feb 26 '24 17:02 wesleytodd

Maybe we need to call that out in the LTS strategy? This is a plan and we expect the plan to evolve to meet our needs. If we had technical priorities they might include (aka I think they should include) avoiding breaking changes.

wesleytodd avatar Feb 26 '24 17:02 wesleytodd

@wesleytodd Sorry if it sounded heated, I am genuinely trying to understand the rationale behind a particular line of reasoning. It is not my intention to attack it. @ljharb is a distinguished contributor to OSS space and I have utmost respect for him.

@ljharb Not sure if I follow. How is making library XYZ incompatible with Node 10 making upgrades to Node 12 any harder?

kibertoad avatar Feb 26 '24 17:02 kibertoad

@kibertoad imagine v1 of a dep requires node 10, and v2 requires node 12. Often (altho thankfully less often in node than in other ecosystems), v1 won't work with 12. This means you have to upgrade both the dep, and node, at the same time, which increases risk. Certainly if you can upgrade all your deps either before, or after, a node upgrade, then it's fine - as long as all of the transitively used versions of that dep upgrade at the same time as you, which never happens.

ljharb avatar Feb 26 '24 17:02 ljharb

@ljharb Yeah, but as you've said, this is realistically very rare in Node ecosystem. If v6 works with Node 10-20, and v7 works with Node 12-20, I don't see a notable detriment to dependant team experience.

kibertoad avatar Feb 26 '24 17:02 kibertoad

Sorry if it sounded heated

@kibertoad No worries! Thanks for the clarification, as I have only interacted with you on twitter I am still learning about you, sorry if I assumed something there.


I still stand by my statement that we are theory crafting here. We currently have a 10 year old major, that's not good. We also don't want to move right to running breaking changes every 6 months. My proposal is that we find a middleground which is:

  • Ergonomic for the maintainers (us)
  • Makes keeping updated as easy as possible for our users
  • Doesn't block breaking changes which are good for users for another 10 years

wesleytodd avatar Feb 26 '24 17:02 wesleytodd

Sounds great! Should we start drafting the LTS strategy text then?

kibertoad avatar Feb 26 '24 17:02 kibertoad

If you would like to start a draft PR I think that would be great. I was thinking, we might want to move alot of these docs into this repo to start (not the main express repo).

wesleytodd avatar Feb 26 '24 18:02 wesleytodd