294 comments

[ 4.7 ms ] story [ 283 ms ] thread
I love Alex Kotliarskyi’s writing style.
> There’s left-pad, the legendary tiny package that broke all internet, collectively causing the amount of pain and drama comparable to the destruction of Alderaan.

Mind blown. Awesome.

It's like a stand-up comedy routine delivery.

It reminds me of the end of Neo’s “tears in the rain” monologue when he says “Now I know what a TV dinner feels like”
Neo?
The timelord.
On an unrelated note, should I be concerned if a British style police box seems to have appeared in a random location in my town?

...and it's red?

...and there is a line of tiny skulls on stakes near it?

...and until I checked Google Street View I could swear it was blue when I drove past?

Only if there’s VHS cases on top of it
I really don't know anything about Dr. Who.

All I know is allegedly, according to Google today, in this particular timeline, the Tardis is not supposed to be red.

And I remember this, from the VHS era: https://www.macintoshrepository.org/3913-daleks

What is this thread.

I am thankful for it.

Edit: I too remember the Daleks game. <3 A round of Member-Berries for all of us! <3

According to the lore it’s fine as long as you hit 88mph
JavaScript IS the gom jabbar
JavaScript is the gigantic man-eating worm that we all rely on for our spice.
To be fair, as already mentioned in other comments, it's the JS ecosystem. Writing plain vanilla JS in a text editor is still a pleasant experience. (However, this is deemed impossible, nowadays.) Having said that, I do get the comedy.
JavaScript is the little death that brings total obliteration.
If this is a reference to the Bene Gesserit test of humanity, then the gom jabbar is not the device that causes the pain. The gom jabbar is the needle with a poison tip held to the neck of the test taker. It’s just a poison weapon. Alia kills the baron with a gom jabbar.
>The type is set to module. This has something to do with the migration from requires to imports. Why do we have to care about this, again? The extensive pain you’ve experienced trying to importing ES5 modules from ESM modules and vice versa overwhelms you again.

Would anything truly have been lost if Node simply ignored ESM?

The half-measure seems, abstractly, worse than announcing that Node was all-in on ESM, deprecating CJS by a certain LTS release, as the end-user has no real notion of the ultimate destination of ESM in a Node project; what is the struggle for?.

Progress, the one thing JS has too much of, too quickly, without stabilizing for quality.

There are a number of benefits (see the link below). We could argue JavaScript shouldn’t be isomorphic, as it barely does its job well on either end. Node might in fact be better off without the ‘pollution’ of client-side packages, but well…

https://www.reddit.com/r/node/comments/rby64z/why_esm_what_d...

Thing is, Browser JavaScript projects tend to implicitly rely on CJS hacks, such as Jest, as the article points out, or, and correct if I'm wrong, most popular HRM implementations.
> Progress

It's lack of quality.

When you have a badly designed layer, you have a lot of problems to progress over. And each bit of progress creates a whole lot of new problems you can solve with further progress.

(comment deleted)
Jarred says it best: https://bun.sh/blog/commonjs-is-not-going-away

- 75% of NPM is CommonJS

- It’s better for lazy loading / cold start outside the browser than `await import(…)`

ESM has nice things too, like top-level-await and strict mode by default. Explicit imports/exports are better for autocomplete when TS is unavailable.

I think the ESM slowness can eventually be compensated for but it will need spec changes. Possibly the defer import proposal.

But yeah, CommonJS is basically never going away.

I think it has to do with Node.js using Chromes JavaScript engine called V8. So whatever V8 does, node.js must do too.
This is actually pretty funny. I coded javascript for a solid ~18 years (I stopped about 3 years ago) and the majority of the concerns in this list are from the past 10 years. What I find glaringly absent, which would have been present if this were written in 2013, is the absence of polyfills.

What other things that were JS nightmares in 2013 have largely ceased to exist? (Only to be replaced by this funny list.)

This is my favorite part: "You stop to count how many tools and parsers work on your codebase: TypeScript, esbuild, swc, babel, eslint, prettier, jest, webpack, rollup, terser. You are not sure if you missed any. You are not sure if you want to know. The level of pain is so high you forget about anything else."

Grunt, Gulp, CoffeeScript, IIFE, overridden undefined, with, closures to create private fields, modified prototypes, variables global by default, call-site-this, automatic conversions for comparisons...

Ah yes, over a decade of JS.

dealing with IE6 nightmares
The IE6 debugger, specifically, which Did Not. Especially when it was a very specific IE6 crash bug that did not occur in any other context. Drawing vml polygons on maps.
I kinda miss CoffeeScript, to be honest. I also miss ExtJs.
I have done things with Ext JS that now take me forever with React + MUI. I miss it too.
Aren't there commercial ExtJS-like frameworks built on top of React? Kendo UI commes to mind.
It really depends on what you're missing from ExtJS. Yes, there are some frameworks oriented towards more desktop-like UI components. Some of them even do layout, maybe even non-view stuff (Kendo seems to do at least the former).

But there are other degrees of ExtJS nostalgia:

- Being allowed to do everything in a desktop-like manner, and not having to bother too much with either mobile or more stylized web applications, even in enterprise contexts. No technical issue, no technical solution.

- Doing that in a simple manner. Even contemporary (> v4 or even v3) ExtJS might not be "ExtJS" in this regard, as the framework part got a lot more complex. First with their own weird tooling, nowadays with the worst of both worlds.

- Not having to touch HTML and CSS. Early ExtJS allowed you to do most things with heavily nested JS literal objects. With angular/react/etc. it's all separate syntaxes, most of them with angle brackets.

I'm also a bit pissed that in the past 10 (or more) years we still haven't gotten good established components in the browser that do even half of what ExtJS could do (and still does).

We may have some by 2050 thanks to https://open-ui.org

Browsers did pour billions of dollars and hundreds of thousands of man-hours into a lot of bullshit though :(

CoffeScript is pretty much alive. It even has JSX support now. Although lacks types and tooling.
var that = this
I always favored `self = this`, personally
Careful not to overwrite global self!

Oh the foot guns of `this`

Overridden undefined?

Omg, that’s awesome. I think anyone feeling like a corporate mindless drone should sneak that into a code base.

And I thought overriding toString() was naughty.

In my defense, on my personal projects I have started using var instead of let/const, snake_case everything, don’t use semicolons, manage my objects with my own prototypical inheritance, and am fucking around with my own shitty Canvas2D widgets. Turns out ditching the DOM and using a Turing complete language still beats the crap out of wrestling with the DOM even if you have to reinvent things like word wrap and text areas.

Oh! And there’s not a framework in sight.

The funny thing this is, my productivity and error rate have way improved. Prob coincidence but I do actually enjoy it much more.

Take that ChatGPT!

edit: I actually write web assembly by hand too. Binary even. And have my own little shim in wasm to override its type safety, put code in my heap space pure von Neumann style, and have my own program counter so I can jump and goto. Can’t wait to start doing self-mutating code.

I know this sounds like I’m being sarcastic here- I often actually am- but I kid you not my error rate has plummeted and its become a blast to program again.

My internet actually slowed to dialup speeds lately so I’m only going to MDN or Stack Overflow if I really have to as well.

‘Modern javarscript’? Hahahaha.

undefined couldn't be minified. If you assigned it to an undefined variable early on, any minifier could easily convert it to yet another single-character variable name.

undefined became U (for example). An 8-byte or more reduction per reference. What can I say? Size used to matter. (Been doing JS since 1996.)

If you allow it I think most minifiers nowadays use `void 0` for undefined as it’s smaller.

Though I should try this, using a variable alias for undefined and null would maximize minification.

Callback hell, and a general lack of types.

I have has a misfortune of spending today writing a test assignment for a company that still uses vanilla JS, without promises or declaration files. Felt right back in 2012 when I first tried node. Worked around the requirement of using JS by utilising JSDoc, but still — it was much worse than I remembered.

For future reference if you ever need to do it again, @callback is roughly equivalent to @typedef but for defining function types, and @template is generic type params for both. It’s not a good TS replacement whatever anyone might tell you, but it’s a reasonable approximation of what you might need in a pinch.
Supporting ie6, ie7, and ie8 separately while developing on firefox just so we could use firebug.
I was on the "fuck ie6" train. I had to write some support for ie6 in like 2015, I almost quit the job.
I'm so glad Internet Explorer/Edge/Trident is Officially Over. If you told me in 2010 that in 10 years the worst browser I'd have to support was Safari/Webkit... I'd be overjoyed.
One thing (only thing?) I honestly miss about IE5.5-8 is how amenable the engine was to polyfilling. It wasn't fast, but you could do almost anything with the right polyfill technique.

No sessionStorage? Use window.name. No (then-) modern CSS? Use CSS3PIE [0]. IE doesn't support the transform CSS property? Use an *.htc behavior to convert the transform to a matrix filter.

It was madness, and it was beautiful in a Cthulhu kind of way.

[0] http://css3pie.com/

I second @golergka about callbacks.

The BIG pain point for me, though, looking at my 10-year-old JS, is that it's all made of "classes" built up by modifying SomeFunction.prototype, which then get wrapped in a self-invoking anonymous function and returned as an object, just so you can call `new SomeFunction()` ... `SomeFunction.actualFunction()` as if SomeFunction were a class. That and also the lack of arrow notation; modifying Function.prototype itself to use .apply() to bind events to scope (or else just an endless abyss of .bind(this) in event callbacks)...

(comment deleted)
Today I don't even write promises anymore. Using fp-ts and piping data through Task and TaskEither monads feels so much better and easier to write and maintain.
I know what most of those words mean, but I have no idea what they mean in that order. This feels symptomatic of the general state of Javascript, where I've not written any for a year or so and am now wildly out of date.
People often complain about how complex it is, but it’s really just a matter of passing endofunctions through transplainer pipelines so that you can asynchronously hydrate the islands.
Seriously, once you understand the ui as a superposition of entangled complex-valued eigenstates that only collapse into place once an observer visits your page, frontend development becomes so much easier.
Why would you ever even need to pass an endofunction through a transplainer pipeline? There are far more efficient ways to do async island hydration in Elm.
The thing is — I'm actually quite stupid. I don't even understand what a monad is. I just FP because it's a good tool. So, here's an example. I'm building an automatic storyteller on top of GPT-4 — it's a toy project to build the LLM-based functional library. Here's how the code looks, with comments: https://gist.github.com/golergka/3fdc6b9cf9717bd1c0b315925a8...

This is literally 20 lines of code, and it doesn't take that much to learn to read it easily. When I originally implemented LLM-based applications in Python, it would make about 10 times more, and all the components wouldn't be as modular. I can change the functionality of this code in a very drastic way with minimal code changes and with safety of completely statically type checked code.

Thing is: you don't read it easily. It's basically a custom DSL bolted on top of Javascript that hides actual functionality beneath an Everest of abstractions.

That is, it's a yet another library trying to force Haskell/Haskellisms into Javascript and claiming with no proof other than emotions that it's so much better and more readable than something.

Here's an actual readable version of that:

    try{
      return getTurnPromptMessages(state, action)
        .map(mkCCRequestFromMessages)
        .map(addSchemaToRequest)
        .map(handleChatCompletion)
        .map(extractFirstCompletionChoice)
        .map(extractChatCompletionMessage)
        .map(decodeSchema)
        .map(detectLocation);
    } catch (e) {
      mkMessageError(e);
    }
It can be further simplified. For example, you don't need two separate functions to extract the first chat completion message etc.

This version:

- uses existing language constructs

- can be immediately understood even by the most junior devs

- is likely to be 1000 times faster

- does not rely on an external dependency that currently has 143 issues and every two weeks releases a new version adding dozens of new methods to things

Note: one thing I do wish Javascript adopted is pipes: https://github.com/tc39/proposal-pipeline-operator

Your version doesn't work.

* all but the first results are not arrays, so the `.map` function doesn't apply

* doesn't handle async operations

* catches ALL exceptions here, whereas my code only handles statically typed and well-defined errors

* doesn't include the GPT-generated message and messages from the first steps in the error

* doesn't include retry functionality

I appreciate this argument and time you've taken to read my code and write this counter-argument. Please don't mistake my direct code for animosity, I clearly see that you're a smart person even though I think you're wrong about this.

Also, the fp-ts doesn't add any runtime performance overhead here. It's a couple of extra function calls for a call that has an async operation that likely takes 30 seconds (GPT-4 is slow). Obviously, if I was writing code that runs thousands of times per second, I would not use FP.

> Your version doesn't work.

It does.

It was also somewhat tongue-in-cheek, and a five-second copy-paste of your code.

> all but the first results are not arrays, so the `.map` function doesn't apply

Unless you return arrays from all functions.

Not that different from wrapping every result into seventeen layers of functional abstractions. What do you think flow or flatMapEither does?

> doesn't include the GPT-generated message and messages from the first steps in the error

Of course it does. That's what the catch is for.

Or you could use an additional map if you want.

> Also, the fp-ts doesn't add any runtime performance overhead here. It's a couple of extra function calls

Of course it adds runtime performance overhead. Any additional function call adds a performance overhead. And in your case it's dozens of functions wrapping trivial functionality.

> Obviously, if I was writing code that runs thousands of times per second, I would not use FP.

Then why use it here? If the alternative is more readable, more traceable, has better stack traces for errors, doesn't require an unstable ever changing dependency...

> Unless you return arrays from all functions.

But that's not... what they actually do?

> Not that different from wrapping every result into seventeen layers of functional abstractions. What do you think flow or flatMapEither does?

It changes what functions return without having to adapt them to particular use-case.

> Of course it does. That's what the catch is for.

To achieve that, the catch has to assume that the functions throw exceptions with particular data. This cannot be enforced with type system, and once again, tied implementation of these functions to this particular context where they're used.

> Of course it adds runtime performance overhead. Any additional function call adds a performance overhead.

The function call overhead is a couple of dozens nanoseconds at best. And we're talking about a remote call that takes 30 seconds to complete and costs $0,05 per OpenAI's API.

The overhead you're talking about is 0,0000001% in time and I think a few orders less in cost.

> If the alternative is more readable

It's not. An alternative that would actually have all the same functionality would be 4 times as long and more importantly, much more complex to change in a meaningful way.

> more traceable, has better stack traces for errors

But that's the point: we don't need stack traces. We don't need exceptions for errors that we know how to handle.

That's like complaining that a GC-based language doesn't have a tool like valgrind to handle error allocation and deallocation issues.

> doesn't require an unstable ever changing dependency...

I've never had a breaking change from fp-ts and don't expect it to ever happen because of the nature of the library.

Really? I feel like Javascript has been more or less the same since we got `class` keyword. People have been using “FP” words like monad or Either in Javascript for more than 10 years. They weren’t mainstream then and they aren’t mainstream now even if you see them written in an HN thread.
Prototype.js monkey patching every prototype it could get its mitts on
Replacing headers with a little Flash applet so that you can use whatever fonts you like, rather than being limited to whatever the user had installed.
I tell myself the only reason they grow into these monstrosities is because they were successful projects but I know this is just a coping mechanism.
So many of these points feel like superficial pains that are really quite optional. So many things don’t actually matter beyond offending our sensibilities.
They're painful because nobody is willing to argue the "business justification" for cleaning up tech debt, so your management asks you to hack away at the next feature instead. We've inherited a mess that slows us down in ways we can't produce metrics for, so we can't explain with pretty graphs how much more effective we would be if we cleaned up our config and tooling.
At a previous job where I had to deal with this stuff, I was able to make the case for an “update task” every six or twelve months, where we’d update all of the dependencies, change the config files that needed to be changed, etc.

This would require a full-regression test, but this employer was in the business of doing these fairly frequently since they were super risk averse, and so adding another full regression test that only happened every six months or so was no big deal.

We’d update a ton of stuff, inevitably break some obscure feature when used in IE11 (which at the time, they still supported), so we’d add a polyfill or chuck a dependency through babel and problem solved. Then nothing for 6–12 more months.

This was annoying, but not nearly as annoying as taking inventory at a grocery store or dealing with bad customers at a restaurant. It’s just work, and we were paid to do it.

Not everyone has a choice to not make these decisions. They inherit a project that is already mired in scripts, tools, and dependency hell.

If your CI fails because of eslint is it really something superficial and optional?

They become pain points when you didn't write them, which is implicit in the article. Picking up somebody else's code always sucks, but JavaScript has a way of making it so much worse. There are so many elements, many of which aren't libraries but instead transform your code. If you don't know any one of them you can be completely lost, especially if it is so outdated that you can't get it to work.

You could write a similar story for any language, but JavaScript embraces all of them at once.

TS and NodeJS is has become my fav for web.
Have you played around with the Lit library for defining UI components / custom web elements? It’s pretty slick.
> double-escaping JSON-formatted arguments

God how I hate anything to do with command line

Typescript is the JS life-saver.
TypeScript doesn't solve any of the issues mentioned in the article
It just solved the "untyped" problem, and it's more than enough.
Yes! TypeScript is fantastic, but you still do need the pain of the JavaScript ecosystem to do much with it.
And adds its own layer of weirdness.

Just one simple example: you must write `import { name } from "./file.js"`.

Not `"./file.ts"`. Why js? Because TypeScript developers are stubborn that their thing is just a layer on top of JS so they refuse to change ".ts" to ".js" in the generated output. This is just wrong approach. But here we are, importing things from "./file.js" which doesn't even exist.

if `import { name } from "./file"` doesn't work for you, something's up with your setup
I don't know the details, but I think there's some new-sh flag or something in TypeScript that requires the `.js` extension.

I don't know if they're pushing that to be the default or what.

If you use ESM modules, they won't resolve files without extensions.
Ah! That must be it. Yeah, super weird that you _have_ to put `.js` when that only exists at runtime
This COBOL will keep feeding us until proper GPT tooling arrives making the whole hands-on coding side of work obsolete.
[flagged]
Not just techbro/hipsters: programmers have huge egos, when in reality they are just mediocre tradesmen. Like your typical construction worker. There are 10,000 hammer swingers for every architect, but those 10,000 think they are all in the top-10. So we end up with egos and factions creating a tidal wave of mediocre tools, each with just enough fan base to grow large enough to be cancerous. Too few architects with experience at the helm (and trained by the old guard), unfortunately, is the problem.
I have to disagree. The ecosystem is what it is because the hipsters left it and it became the spiritual successor of Java’s enterprise-y mindset.

When I think of JS hipster I think of CoffeeScript, Elm, ClojureScript and all the other trendy stuff from a decade or so ago. JS borrowed enough stuff from them to be tolerable and now everything is React and Webpack plug-ins.

(comment deleted)
> Every time you modify dependency list, some of the dependencies print out screens-worth of messages to your console, asking for donations, warning about breaking changes.
I appreciate the playful tone, it's good to be reminded of these pain points after the first few layers of skin and nerve endings have burned away.

On the flip side, I can throw a hungry junior dev with a new macbook into the deep end, and they can be productive within a day. They don't have to know anything about containers, .net, nginx, or the top 100 most common command line tools. When they hit an issue they can just google it.

> When they hit an issue they can just google it.

Now they chatgpt it.

And get unreliable results from a stochastic parrot
I regularly find chatgpt to be better than googling for code related stuffs.
I think that speaks more to how much Google search result quality has plummeted over the years more than its suitability for the task.

> regularly

The trouble lies in the credible sounding results that are in fact subtly (or less subtly) nonsense.

It's a bit like old W3schools but instead of poor practices, its authors were half-in-the-bag

I’ve no problem distilling those! I find stackoverflow and co to be of worse signal:noise
Edit: this is getting a lot of downvotes but it is important information. ChatGPT with GPT 4 ($20/month) is an artificial intelligence that can save hours of your time and help you debug things you couldn’t without it. It really understands code.

While I will take a 4 point karma hit from downvoters who don’t know how to use ChatGPT, this information is very important to share with all programmers.

>stochastic parrot

This is incorrect. In the context of learning JavaScript, ChatGPT with GPT 4 ($20/month) can analyze any error if you give it the error and the code, or you can just describe what’s wrong and give it the code you wrote and it will analyze it and tell you how to correct it, even if your code is the first time anyone has done that. It might take it two tries but it’ll figure it out. It obviously understands what you tell it and follows your instructions pretty well.

Limitations: the code you give it has to be small; ChatGPT doesn’t scale to larger codebases.

Second limitation: if you happen to be a world expert in something, then it might have trouble understanding - haha, just kidding: if you are the best human expert in the world, ChatGPT is still smarter than you and can still fix errors you can’t fix.

Rather than a stochastic parrot, it’s the best debugger on the planet.

In fairness you are correct that ChatGPT is unreliable.

At which point they'll spend the next day or three smushing something together that they'll regret for the next ten years, and then write something like this. The cycle continues.
On the flip side, I can throw a hungry junior dev with a new macbook into the deep end, and they can be productive within a day.

Indeed. The irony is that the trade off you’ve made means that you can throw a senior dev with 20 years of experience building things that run in browsers, and he wont be up and running in a day.

He’ll be fighting all the unnecessary layers of complexity to modify this ship-in-a-bottle with the fiddly tools provided, knowing that he could have built the entire ship in a few days if he could just touch the thing with his hands.

At least that’s what I feel like dropping into Modern Frontend gigs after sprinting along on my own projects that use javascript, html and css.

Add to that any traditional server side template rendering like Jinja or Django templates, and you can implement probably 90% of today's SPAs in simpler terms and actually working browser staples like the back and forth buttons.
yet the devil lies in that last 10%
If your project doesn't actually need anything more complex than basic JS, html and css, you don't need those layers of complexity: you can create a new package.json based project and be productive in 5 minutes.

However, if you are thrown into an existing projects that does have all of these things, it's usually because they're actually necessary.

> However, if you are thrown into an existing projects that does have all of these things, it's usually because they're actually necessary.

That is almost never true.

Everyone's experience different. Of course, if you're building a static blog, you don't need React on the front-end. However, my experience was mostly with interactivity-heavy applications, where all of these tools are not only indispensable, but often arguably not heavyweight enough.
> Everyone's experience different.

This is such nonsense. Not everything is subjective or "an experience". There is objective truth is the world and the objective truth is your previous statement is almost never true. Unless you're operating at FAANG levels, that is, which almost nobody outside of FAANG is.

The juniors I know will go:

“Let me create a framework to handle this complexity”

Blissfully unaware that it would take them months to write that, and it would be obsolete the next day, because part of the problem is too many frameworks.

This is yet another case of people saying "JavaScript" when they mean "a bunch of Node ecosystem junk". Right out of the gate:

> It’s time for your test. You are sitting in front of a computer. The test is simple: you have to open a package.json file and read it.

And it continues in that vein. For all the stuff mentioned, there's actually almost nothing that has anything to do with JS per se.

Try writing any JS without a bundler that uses package.json. I mean really, try it. Until ECMA codifies a package manager or bundler in the official specs, package.json is de facto part of JS.
> opens dev tools > types `console.log("this is impossible without a package.json")`

To my astonishment, it worked, without a package.json file.

I definitely prefer using npm for any non-trivial front end, but let's be real: JS is an integral part of the web stack. You can even inline JS inside a <script> tag on an HTML file. It's not that hard.

I do sometimes wonder how many of the critics have only ever worked on Angular frontends and such. The frontend for my little site https://bongo.to doesn't use Node or NPM at all. Just a script tag, like you said. It's very simple and easy to use JS this way, and I wish people would learn to do this before learning how to write a SPA in the framework du jour.
`<!-- prettier-ignore -->`

You run prettier though? How do you download and run it - it seems to require Node.

you can run it with the `npx` command if you didn't want to install it. another way, which is the one I'm using here, is via the VSCode plugin. enabling the "format on save" option with prettier as the default formatter will format any file it can, which is very handy!
You know we used to write javascript before JSON existed, right? Let alone node and package managers.
> Try writing any JS without a bundler that uses package.json.

I have, and I continue to. It's not hard.

To give one example: Firefox, a serious desktop application by any measure, was written in JS from the very beginning. (Yes, really. A combination of JS and C++, at least—and, nowadays, Rust, too; not unlike the way that Emacs is written in Lisp, where that means a combination of Lisp around its C core.) That's hundreds of thousands of lines of JS we're talking about, and it predates Node and all of the bundler/package.json stuff that you are implicitly arguing is somehow unavoidable. Firefox is an existence proof that that's not true (and also happens to be a higher quality app than anything I've ever seen the Node community offer as example to serve as testaments to their methodology, including anything to do with Electron, which cribs the same Greenspun, Emacs-style architecture that's behind Firefox).

I worked on the JS docs on developer.mozilla.org in the early days of the site. (Also before Node ever existed.) I did this because I wanted people to understand JS. It's a much nicer—and more thoughtfully designed—language than it gets credit for. It pains me, therefore, when I see people refer things as JS when what they should be saying is "stuff associated with the kind of software of dubious quality that developers who decided to rally around NPM have been putting out into the world for the last decade—and more often than not actually goes against the natural grain and spirit of the language".

> I have, and I continue to. It's not hard.

I also write JavaScript code without NodeJS and package.json. It's not hard when the code is simple. But it doesn't scale. For a complex code, I want a linter, I want automated tests and coverage, I want to split the source code over many files... NodeJS is the standard requirement for all of these.

> Firefox, a serious desktop application by any measure, was written in JS from the very beginning. (Yes, really. A combination of JS and C++, at least

At the very beginning, this web browser started under the name "Netscape", and JavaScript didn't exist yet. Mozilla forked out of Netscape in 1998, and there was little JS in the source code at this time.

> it predates Node and all of the bundler/package.json stuff that you are implicitly arguing is somehow unavoidable.

Firefox doesn't use the standard bundlers of the Node ecosystem, but it's because it has its own build systems and bundlers. It's a beast that needs 30GB of disk to compile, with a custom build system written in shell, Perl and mostly Python, with many tests systems (including for "mochitest" JS code).

For instance, among the dozen of linters that Firefox uses, there is eslint, which a normal JS project would install through a package.json.

> JS. It's a much nicer—and more thoughtfully designed—language than it gets credit for.

JS was notoriously designed and implemented in a rush, because Netscape was under pressure from Microsoft. It also suffered from legal constraints that block the adoption of some Java concepts. Once LiveScript/JavaScript was born, its evolution was chaotic since the browsers war prevented any consistent progress of the language. After it settled, Ecmascript did enhance the language at a fast pace, but it cannot fix the many inconsistencies and design flaws.

> At the very beginning, this web browser started under the name "Netscape", and JavaScript didn't exist yet. Mozilla forked out of Netscape in 1998, and there was little JS in the source code at this time.

I have no idea what point you are trying to make here. Firefox—not Netscape—has been a JS-driven app since its very first day of existence. (Netscape Navigator 6.x, which has nothing to do with Netscape of the early-to-mid 90s, is the same way—but that's beside the point: I said Firefox, and that's what we're talking about.)

> Firefox doesn't use the standard bundlers of the Node ecosystem, but it's because it has its own build systems and bundlers. It's a beast that needs 30GB of disk to compile, with a custom build system written in shell, Perl and mostly Python, with many tests systems (including for "mochitest" JS code).

I don't know what Firefox's build system looks like now, but at the time that Node debuted (2009), Firefox 3 was the latest release, with Firefox 4 development underway. It did have (and always had) a pretty gnarly build system made of shell scripts, Perl, autotools, et cetera... the same as almost anything that involved reliably compiling a large project with C/C++ across many distros (not to mention other platforms), which was the entire reason behind it. The use of JavaScript, however, had nothing to do with that; there were no JS bundlers. You brought symbols in scope by adding a script element. You still can.

This is also how WebKit's inspector is written to this day, last I checked (~6 months ago or so).

> JS was notoriously designed and implemented in a rush

Again, no idea what point you're trying to make here. JavaScript was done in a rush. It's also thoughtfully designed—certainly more than it gets credit for—and more than other languages that had more time (and less of an excuse). And it remains pretty nice, popular misconceptions notwithstanding (and not helped by self-loathing/self-sabotaging developers from the GitHub era like the author of the submitted article).

> But it doesn't scale. For a complex code, I want a linter, I want automated tests and coverage, I want to split the source code over many files... NodeJS is the standard requirement for all of these.

Hard to cover this succinctly given how much weird stuff is packed in here.

Hundreds of thousands of lines of code of higher quality (and subject to harsher requirements) than the Node fanclub has ever produced is proof that JS scales.

Testing is a basic engineering concern and common across the industry and across all languages. It's a completely orthogonal issue.

Lint as much as you like if that's what you want. Neither Node nor the Node way of development are in any sense a requirement.

For having source code "split [...] over many files", whether you use Node or not (or follow their fads) is neither here nor there...

I’ve done it since the mid-1990s. It really isn’t that hard.
I’ve been writing JS my entire career and never wrote a package.json file. I’m not sure what tool would even use it.

But then again, I’m a happy, sane person who only writes client-side JS.

> I’m a happy, sane person who only writes client-side JS.

I've never written backend javascript. I've used and fought all the tools mentioned

how do you manage large projects?

what happens if your project has tens or even hundreds files, how do you get that to client without any sort of bundler?

Here's what the person you're responding to said:

> I’ve been writing JS my entire career and never wrote a package.json

And you replied:

> how do you get that to client without any sort of bundler?

Please connect the dots for me.

Why would the project have hundreds of JS files? Usually I might have something like main.js which handles universal page elements like the search bar in a site-wide navbar, then, if necessary, a few this-specific-page.js files which handles elements which are specific to this page or site section. If any libraries are needed, try first to just link them in using CDNJS or the like, so no need to store them locally or bundle them in our own scripts - I know there are downsides to relying on external CDNs for such things but in practice it just works. Easy peasy.

But first, I try to avoid using JavaScript in the first place when not strictly necessary. This means server-side rendering as much as possible rather than doing the glacial "load an almost entirely blank page, load giant JS blobs, then load the page content via AJAX and hope that nothing goes wrong in that process, sorry slow connection users, non-standard browser users, and search engine bots" approach which thankfully is finally starting to see some pushback.

You can use modules directly in the browser and it will load them in fine. You don't need to use a package manager do to that: https://byexample.xyz/javascript/ECMAScript2015/modules/#dir...

I have never used a package.json file in my 10+ year writing javascript & typescript. We don't have nodejs installed on our workstations or servers. A lot of work has gone into that build chain, and I have no idea what it is doing (apart from something something bazel & monorepo) but it works.

At home I use Deno and it is a joy.

Right, the ecosystem is not a valid critique of the language, got it.
(comment deleted)
If that fries your pickle, whenever anybody says JavaScript, I think they really mean ECMAscript.
[flagged]
None of these semantics actually exist. Javascript is the language, and it was released as an implementation first, then a standards body worked on a specification for it. For various reasons (bickering, trademark), the specification was called ECMAScript. ECMAScript is the specification for Javascript. That's it. There's nothing clever to be had here, it's just odd historical trivia.
(comment deleted)
I always thought, inaccurately, that Javascript was simply the 'navigator' object and a few other objects, glued into the namespace.

I did not know that it was standardized, though .. duh .. it would have to be I now realize.

TY for the interesting comment @Freedom2, it's informative for me.

They absolutely do, and I honestly get annoyed when ECMAScript gets misnamed. If you're putting a virtual machine with pseudocode into my browser, I want your applet gone.
Is that distinction really significant? I know JavaScript is a trademark owned by Oracle, but in practice it is used interchangeably with ECMASCript.
Um yes.

It's a critique of the JavaScript ecosystem.

> the JavaScript ecosystem

We just covered this.

What tools/package managers/linters do you consider part of the JavaScript ecosystem?
There's nothing mentioned in the submitted article that isn't part of "the JavaScript ecosystem"—because everything it mentions is the product of the outgrowth of Node, NPM... and that subtree exists within the JS ecosystem. But to narrowly focus on that subtree and say it's "a critique of the JavaScript ecosystem" is like visiting McDonald's and passing off your experience as a meaningful critique of the ritual of ordering food and eating it, or writing an angry blog post about the chicanery of some nincompoop your cousin knows while implying that this is what you should expect when you interact with anyone else named "Steve". It's a shameless generalization that's both stupid and unnecessary.

If you have a grievance against The Node Way of software development and you want to air it publicly, then have enough of a sense of awareness that that's what you're doing—and say as much when you do it.

> because everything it mentions is the product of the outgrowth of Node, NPM... and that subtree exists within the JS ecosystem.

Where subtree constitutes ~90% or more of the ecosystem.

Yeah? Have you measured it?

Aside from that, do you have anything meaningful to say about the points actually raised? Let's take your claim as a given. If it were 2003 instead of 2023 and people kept bringing up, say, problems with Windows but refused to speak about them in those terms—instead insisting on framing with a generalization about "computers"—would that have been acceptable on the basis that 90+% of the world is using Windows? It plainly wouldn't have, and that's true for a case where we actually know that we were dealing with numbers of 90% or more, and not just lazy attempts at retorts that are fired from the hip and by a reasonable guess probably won't actually hold up under scrutiny, anyway.

> Aside from that, do you have anything meaningful to say about the points actually raised?

You didn't raise points. You went off on a rant by completely dismissing the tongue-in-cheek article about tooling issues in the frontend space as something about "node-only".

All the points the author raised are:

- about frontend aka client-side

- about javascript ecosystem

And this ecosystem is ubiquitous. Much more ubiquitous than "no, this is not frontend, I do client-side and I don't use it".

> If it were 2003 instead of 2023 and people kept bringing up, say, problems with Windows but refused to speak about them in those terms—instead insisting on framing with a generalization about "computers"

See? This is not a point. This is argument by analogy. You're pretending that this analogy is valid (it isn't) and you're expecting me to argue with you.

> this ecosystem is ubiquitous

I wrote a perfectly lucid response which you ignored completely. Your choosing to ignore it, or to read it as if it means something other than what it does, does not mean that I don't have a point (wat).

> Much more ubiquitous than "no, this is not frontend, I do client-side and I don't use it".

Who are you quoting (and what is it even supposed to mean, besides)?

> you're expecting me to argue with you

Nope.

> I wrote a perfectly lucid response which you ignored completely.

I wrote why, and you definitely literally ignored what I wrote.

> Who are you quoting (and what is it even supposed to mean, besides)?

I'm very shortly representing what you wrote. So it's for you to answer what this means.

> Nope

Yup. Otherwise you wouldn't resort to bad analogies.

Anyway. This is going nowhere.

Adieu.

> I'm very shortly representing what you wrote.

No, you aren't—and the point at which you are attempting to explain what it is that I really meant or was thinking (explaining me... to me) is the point at which you have stumbled upon a strong indicator that you have lost the plot. Viz:

> Yup [you are expecting me to argue with you]

About what percentage of JavaScript developers don't use NodeJS?

You are right that JavaScript !== NodeJS, but the majority of people would understand what the author meant.

It wouldn't hurt the author to be a bit more precise, but they aren't really wrong either.

> About what percentage of JavaScript developers don't use NodeJS?

I'm always surprised by developers who prefer to use Node on the backend.

I used Node for a small project. Never again.

No typing, requiring a couple of dependencies just to get poor, half-assed typing that doesn't really work anyway.

Poor dependency management, although this is probably not the fault of the tool, it's the fault of a poor ecosystem dominated by junk.

A primitive execution model, requiring hacks around hacks to get around the fact that the execution model is based on single-thread async execution.

Speaking of execution, slow execution too.

Limited IDE help (due to lack of typing). Difficult debugging tooling. No standardisation of anything.

The people using Node know that there's alternatives like Go, and yet they still patch on band-aid after band-aid and engage in drama.

Using Node for the backend is a special kind of hell.

I don't even like Go all that much, but it's a world of difference having strong static typing, nice dependency management, good tooling, simple debugging, no setup costs.

Someone upthread made the point about how they might burn a day just setting up a new Node project. A new Go project might take a few minutes from inception to "hello world".

Poor typing isn't a Node issue, it's a JavaScript issue. Use TypeScript, install @types/node, and you're ready to go. The rest of what you say is true. Although I actually do enjoy using Node for quick one-off scripts and small dev tools
My complaumtnis that typing shouldn't be optional.

With Node, not only is it optional, it's a third party dependency.

That's like complaining that Linux kernel doesn't have a GUI because GUI shouldn't be optional for an operating system.
> That's like complaining that Linux kernel doesn't have a GUI because GUI shouldn't be optional for an operating system.

Really? That's your defense of both not having type-checking AND not having optional type-checking builtin?

All common languages implementations, other than Node with JS, either have type-checking or have optional type-checking builtin.

Node and JS stand alone by having no type-checking, nor having optional type-checking builtin.

Node is uniquely low-quality in this respect.

All other operating systems implementations have GUI, and Linux doesn't.

Seriously though: modern JS ecosystem is not a "language" or a "framework". It's a kernel. You can choose your own language, whether it's Typescript, Coffescript, Purescript, Clojurescript, Reason or one of it's sublings, Elm, or pure JS. Of course, like with Linux, this can be a good or a bad thing for different users and circumstances: personally, I like Linux, but I don't want to handle all of this complexity on my own desktop.

And just like with Linux, you have to choose your own implementations for everything: testing library, strings, dates, templating library, state management, web server, etc.

If you're comparing the barebones Node and vanilla JS to other languages and frameworks, you're not making a valid comparison. You can't pretend that something is inferior just because it's modular.

> All other operating systems implementations have GUI, and Linux doesn't.

You "two other", right? I've used many that didn't have a GUI.

> Seriously though: modern JS ecosystem is not a "language" or a "framework". It's a kernel.

That's not how it's pitched.

> You "two other", right? I've used many that didn't have a GUI.

Good point, however, I don't think it makes the analogy invalid. You can replace "GUI" with "init system" or any other thing that separates OS kernel from a full-fledged OS.

> That's not how it's pitched.

There's truth in it. However, I think that the problem here is that we might have different expectations of what a "language" is. For some, it should be a complete package with all libraries and tools built-in — like go or Python. Racket even comes with it's own standard IDE. For other languages, like C, not even standard library is something expected, it's very basic and it's implementations are platform-specific.

Yes, if you take the basic Node, it will not have Typescript included. And for a developer that comes from a different background modifying your "language/framework" is something so weird and out of the ordinary that you don't really do that often. So you might inclined to judge Node ecosystem by your habits, but that's a mistake.

Just as with leftpad, developers from other ecosystems were in shock to find out how many small dependencies does a typical Node project have — because including a dependency is an expensive thing in their experience! But with Node, it's not. I can build and run a typical Node project with hundreds dependencies ~10 minutes after I clone a repo, but it takes me a few hours to configure the right version of Python, requirements and native C/C++ libraries for any significantly complex Python project that has a lot less.

So, instead of comparing the "type checking" out of the box, compare two reasonably sized projects after a year of active development by 5-10 person teams. And from my personal experience, a modern Typescript project would eliminate a lot more failure conditions at compile time than any other mainstream language.

Again, that's not Node. It's JavaScript. You experience the same typing issues in any environment which runs JS (browser, embedded system, etc.).

If you want a good type system then you need to change languages and treat JavaScript as a compilation target. That's what most "JavaScript" professionals have been doing for years. Install the TypeScript compiler and a single dependency to give you Node-specific types and you're set

That's ironic, because as a fullstack developer, I don't want to touch Go exactly because of it's lacking type system and how it manages dependencies.

Typescript has the best type system among modern mainstream languages, and although you're right that Node is not well suited for CPU intensive applications, the most part of backend work is IO bound, where async/await works perfectly — waiting for reply from the bd looks and works much more natural than with Go's primitives.

Typescript type system is both powerful and lacking, and with a syntax that is horrendous to do anything complex. It gets even worse than regex in the "write-only" direction. And you can do black magic but at the same time very basic things are still not possible. I would argue that the Python type system is actually better because you can build on it with regular Python instead of typescript weirdness and because you can use it at runtime. The fact that typescript does not support any kind of reflection makes it very limited for a script language. Rust type system on the other hand is also limited in refection but it is not a scripting language so it's more acceptable, and at least it integrates very nicely with features such as pattern matching and control float which you don't have in TS. TS also has a way too complex option list which is becoming nearly as annoying as ESLint to configure, with basic safety disabled by default and rules that are just annoying and add no safety whatsoever in the "strictest" config.
Can you please provide examples of what do you consider "write-only" types? Because so far what you've described is the exact opposite of my experience as a Typescript developer.

As far as reflection goes, I see how it could be useful with other languages with less powerful type systems, for example, to build a deserialiser. But with Typescript's type inference I just never felt the need to reach for a crutch when I can use a proper solution.

> Typescript has the best type system among modern mainstream languages,

I wouldn't call something the best when it's disabled by default, even after considering the fact that it's neither part of the language nor the platform, and needs multiple dependencies in order to run, at which point it has worse type-checking than every other language.

TS's type system is nice but to call it "the best type system among modern mainstream languages" is a bit of an overstatement.
I would say TypeScript is second only to Rust
Check out Scala's type system. I don't think you'll miss anything that TS can offer there.
> No typing, requiring a couple of dependencies just to get poor, half-assed typing that doesn't really work anyway.

I'm not sure when this experience was. I've used TypeScript for years. About 5 years back it was okay, but it was very hit-or-miss for a given library to have types.

Today, I haven't had that problem. Every library I use has _great_ types, and many libraries are even written in TypeScript.

Anyway, all I'm saying is that you might want to give it a second change. TypeScript and the ecosystem has greatly improved. I hate working without static typing (Python, JS, and Ruby are pain), but TypeScript is really quite great.

Hello there - that's what I've been doing lately.

Applications by and large do, but when you're writing a library you want to at least start without the usual bs.

JSDoc for type information works great as a crutch in this initial period.

EDIT: ok, now I recalled that I use Node's built-in test runner for tests. But I still don't have a package.json as well as any dependencies.

(comment deleted)
And here I was thinking the first thing about version was going to mention that v0.x.x (major 0) semver packages are considered "unstable" and every update to the version string, even minor or patch numbers, is considered a major update, by the logic that determines whether or not to update the package/which compatible highest version to install
What would be the minimal but modern setup to develop web apps that use javascript to good effect in conjunction with an agnostic server stack.

I am intrigued by "less js is more fun" approaches like htmx and alpine.js but that still leaves a range of options on how to organize things and some may bring you dangerously close to what OP describes.

Just a .JS file. Oh, modern. Sorry, nothing but unnecessary complexity and suffering is allowed in modern dev environments.
Was meant to be a serious question :-)

There must be a sweetspot of tools and designs that helps organize a modest code base to get the benefits of reusability and readability without too heavy a penalty.

Imho frontend people gladly walked into the trap of "excess complexity" as this elevated this niche into a "serious" business. Whenever some aspect of tech doesnt make sense, follow the money and this usually clears things up.

Native DOM APIs are a lot nicer than they used to be. Just JS will work just fine as long as you're comfortable building a whole UI yourself. You can sprinkle on Typescript for static types (which I wouldn't do without for any serious project, myself), esbuild for bundling, then serve it how you like.

The issue you might run into as you scale up is managing the state and synchronization of a large, complex UI. This is where having a reactive, declarative interface can help out a lot - which is why you arrive at React and tooling around that, or something much like it. That's where most large UI projects end up, frontend or backend.

Don't know if it's still a good approach but a few years ago I used Preact with htm without any build tools for a project. No compilers, bundling or package managers, on my part. (Preact and htm were, of course, pre-compiled and bundled so that might be a turn-off with this approach.) The Preact devs provided CDN links to add to your app but I decided to just download the two JS files and host them with the app's files. My only gripe back then was that the bundle for the Preact hooks library somehow had a CDN location hard-coded so I had to decide between using the CDN but having hooks or using class-based components but having it all local.
> Native DOM APIs are a lot nicer than they used to be.

You must be joking, Mr. jrajav :)

Native DOM APIs are just as bad as they always were. Because they are stuck in the 90s era of heavy imperative APIs.

There are a handful new additions that are somewhat nice, but all of them either underdesigned, or with warts you need to be aware of (querySelector, querySelectorAll and native fetch come to mind).

Building a UI from scratch with them is just as cumbersome and awkward as it was in the early 2000s, and you'll end up re-implementing half of jQuery at least in the process.

(comment deleted)
I’m going to be learning more about HTMX & friends myself.
Almost all of the painful stuff in that post is due to migrating from tool to tool. (“Both main and browser fields are present”, “This has something to do with the migration from requires to imports”, “Subcommands calling npm even through you switched to yarn and then pnpm”, “Some scripts still use watchman … There’s also this gulp-based script that nobody has the guts to replace”, “There’s also moment.js … you have to migrate to date-fns”, “these packages are way behind [current] … you should upgrade them … [you] have tried that before and you know how much suffering it brings”, “resolutions … a suffering you choose to avoid dealing with package upgrades”, “jest … depends on some babel packages while the rest of your app is transpiled by a mix of esbuild and swc”.)

Ironically enough, the last paragraph of the article? About hating node while refusing to switch to bun or deno? That kind of decision might have prevented some of the pain mentioned in the previous paragraphs.

I don’t have an object-level answer for your question, and I’m not trying to mislead you into thinking it’s possible to actually have a minimal setup for JavaScript web apps - it’s always going to be painful. But with a commitment to maintaining minimalism you can pick a stack to start with and mostly keep it that way. The grass might be greener on the other side, but the fences are made of razorwire!

I would also love some sincere answers to this question. I'm mostly a back-end person, but would love to start building some front ends that don't look quite so 1997-ish. But each time I think, "Gosh, I should learn whatever the current thing is," a) it's different than the last current thing, b) it seems to be pretty volatile in terms of frequent changes, and c) smart people have some really good critiques of things that are fundamentally wrong with it. And I feel like I've been having that experience for a decade.
Just find a basic CSS library and use server side templating in whatever your preferred language is.

As systems evolve, they always get more complex. Web front ends are evolving to keep up with users, app archetypes, and business needs. People choose tribes to identify with, angular vs react vs vue. People are ambitious and pushing their own frameworks while tearing down others. Its just programmer politics, no different from windows vs linux vs osx.

I learned react, stuck with it, and it has worked out.

Same situation.

Had to choose a test for an interview. Some UI required.

Ended up using Blazor Server, got it done..

I think React via Vite.js is pretty great. Takes a few seconds to get started with very few direct dependencies. It is fast and easy in my experience. For an API server, use whatever you like. If you want SSR, check out Remix or Nextjs... but that is going to be a little bit more complicated.
There are projects attempting to do more things. I've really enjoyed Parcel (https://parceljs.org). But it won't handle things like linting or unit testing, which you may or may not want. Vite is also pretty popular (https://vitejs.dev/), and it has a test runner.

Thing is, most of the problems described in the post aren't related to low-JS front-end libraries like HTMX or alpine. You can write React without a linter, bundler, build tool, unit testing, or linting (or dependency manager, for that matter!). But with any of these projects at scale, you start wanting more:

- If you want to write unit tests in JS, you need to choose a test runner (probably Jest or Vitest -- until the built-in node testing module becomes more common).

- If you want linting, you need a linter (probably Eslint). If you want type safety, you need a type checker (probably Typescript).

- If you want to create smaller JS files to ship to production and to automatically handle assets, you need a bundler.

- If you want to use new language features while supporting old browsers, you need polyfills.

- If you want to use all these things together, you need something to bring it together (like Webpack).

So it really depends what you need! You may not need any. But as you can imagine, in many professional projects with multiple developers it's very nice to have unit tests, linting, and type checking :) (And you start caring about end-user performance a lot more, in which case optimizing the shipped bundle is important.)

Take all that, and then compare to a language like Rust, which has most of the "ecosystem stuff" built-in. In Rust, you get the test runner, the linter, dependency manager, type checker, and documentation tool all included. Easy! Thankfully, Rust doesn't have to care about whether users support modern language features (because it compiles down to lower-level code ahead of time), or whether the binary shipped to the client is optimally organized for downloading immediately over the internet.

It's a problem in JS because A) you have to care about more problems than many other languages since JS needs to load instantly over the wire in a web browser, and B) there is a huge amount of choice and not a lot of standardization in web tools. (And what standardization there is (Node, npm), there are still competitors trying to even further reduce the pain points.)

I think that in ten more years, we'll be in a better place, because there is push back (like this post!) against these problems, which will encourage more tools trying to solve the explosion of tools. Which seems counterintuitive, but these tools were created to solve very real problems. So I see it as a pendulum which has swung too far, but will likely swing back to a more balanced place. And you see that with tools like Vite gaining popularity.

The best experience with frontend JS I've ever had is the following setup:

1. Use npm. Well, I didn't try yarn, probably it doesn't matter.

2. Use vite. It sets things up good enough and pulls only 15MB dependencies. Tiny!

3. Use TypeScript. I can't imagine writing untyped code.

4. Use eslint to complement typescript checks. Takes some time to set it up first time. Use default rules, they're good.

5. Use prettier to format code. I use vscode and with corresponding plugins, it just gets out of the way. It's good thing to have and works with minimal setup.

6. Use react. Well, it's pretty much standard nowadays and things are mostly fleshed out. But it's pretty complex so beware of steep learning curve. But once you learn it, it's not going anywhere. At this point I consider react a fundamental knowledge, worth spending time to learn.

7. For state management use react-query (yes, that sounds strange).

8. Use built-in to vite methods to access CSS (I think it's called module CSS).

9. Don't add any more dependencies unless you absolutely feel like it. Don't add CSS frameworks or things like that, just write your CSS by hand.

This was the least pain setup I ever had and I consider it to be minimally viable.

> 1. Use npm.

Better use pnpm, faster for those that don't have 1 Gbps uplink and live outside the usa.

> 3. Use Typescript

Better use jsDoc, d.ts files are scary and are really complex, you need a typescript wizard la matt pollock just to tell you are mistakenly trying to concatenate a string and int.

> 5. Use prettier

yes use prettier but don't forget to configure eslint to work with prettier otherwise you are going to have a bad time.

> 6. Use react

Use whatever fits your problem but if you are going to really use rect then do it with Next.js, no point in still using CRA/CRACO

> 7. state management

Avoid using state, try to solve every problem with pure functions, if you must use state then consider redux/react-query

> 8 & 9.

You should still recommend stylelint, specially if you are going to write css by hand otherwise don't just use tailwind, tailwind is bootstrap with extra batteries (change my mind).

> yes use prettier but don't forget to configure eslint to work with prettier otherwise you are going to have a bad time.

Can you elaborate? I'm using eslint and prettier with pretty much default settings and never had it conflict with each other. My impression was that they work on different aspects. May be there're some overlapping eslint checks, but they're not enabled by default (or I didn't stumble upon them yet).

> Better use jsDoc, d.ts files are scary and are really complex

Why would you use d.ts files and not just... .ts files?

When you want to integrate a javascript module that isn't annotated with types... types in a dynamic language like javascript are complex once you start doing really abstract things like generics the fun of typescript ends, also there is a recent push to abandon typescript mostly fueled by svelte fan base but nevertheless typescript is really complex, easy to shotgun yourself.
The whole point of explicit complexity of Typescript is to prevent you from shotgunning yourself from implicit complexity of Javascript.
And thats why it isn't worth, you are adding complexity to a problem that doesn't need to be complex, jsDoc is the best of both worlds, type safety, zero complexity and it works with typescript (if you still really want to use it).

Personally i think typescript isn't going to be around much time on the frontend side, maybe on the backend by some people that still believe in adding another compiler layer to their build time.

Angular is "batteries included". Many applications I have worked on have just angular as the single dependency, everything else is business logic written just for that app, so you don't need package.json since there are no other dependencies to manage.
I'd suggest taking a look at Deno it has most of what you need, batteries included.
> Oh, dear jest. […] Properly configuring it with ESM and TypeScript was a PhD science project.

No kidding, I had to do this recently. It was a trek and a half.

Same here. Once I thought the nightmare was over another team's dependent project's jest started failing for a different reason.
I'm a polyglot developer who likes to think they're comfortable in a wide range of programming paradigms and languages across both frontend and backend.

This is pretty much exactly my reaction any time I see a package.json longer than ~20 lines.

Same. But commenting to make an off-topic remark: the link in your profile is blocked by uBlock Origin and leads down a rabbit hole of strange websites. Since I never used hackernewsers I can't tell if it's working as expected, tbh
Huh. Thanks for the heads up. Used to be a useful service but apparently the domain expired and was replaced by spam.
stairs glaringly at the new Angular front end we're developing that has four seperate front end components, each with package.json files of 200+ lines

Speaks out loud, hoping to keep the demons at bay.

“I must not fear. Fear is the mind-killer. Fear is the little-death that brings total obliteration. I will face my fear. I will permit it to pass over me and through me. And when it has gone past I will turn the inner eye to see its path. Where the fear has gone there will be nothing. Only I will remain.”

cries

Very in tune with the reference of the articles title.
The article was fine until this part near the end:

> engines prominently lists node. And while you hate it with the depth of your soul, you are not going to Bun or Deno because you know this will not stop the pain. This will only make the pain worse.

Deno will make the pain worse?

Nearly every complaint in the article could have been solved by using Deno.

- Package.json, no such thing for Deno (not unless you go out of your way to infect Deno with it)

- main, browser, and fs don't matter in Deno since Deno's APIs are based on browser APIs.

- type, module, requires, and imports doesn't matter in Deno since it shares the same import API as browsers.

- scripts, doesn't matter since all Deno projects have the same tools built-in (benchmarker, bundler, compiler, formatted, linter, task runner, test runner, etc...)

- watchman doesn't matter since deno has watch support built in

- dependencies doesn't matter anywhere near as much because Deno comes with a standard library

- left-pad doesn't matter because it's part of the language

- moment.js doesn't matter since you can just use the browsers built-in Temporal api

- resolutions, dev-dependencies don't matter for the same reason that dependencies doesn't matter

- eslint doesn't matter since deno has a linter

- postcss matters less because modern css is great

- jest doesn't matter because deno includes a test runner

- no need to work out typescript installation with your dependencies because deno includes typescript and has less dependencies

The only things not solved by using deno is version and name, which are universal concerns amongst all projects, not just the js ecosystem.

There's a lot of 'just' in your argument.

As programmers we fall into this trap where we think that 'just' doing something else will solve all our problems. But I can guarantee that it's never that simple, and for the people who consume your library, it won't be either.

There's a lot of 'just' in your argument.

I used the word "just" once in my comment in the point about using Temporal.

The Temporal spec was created in part specifically because of the issues with moment.js.

----------------------------------------------------------------------------------------

> But I can guarantee that it's never that simple, and for the people who consume your library, it won't be either.

Unfortunately, Node has historically not used the same standards as browsers over a whole swath of functionality, which lead to differences in the language based on whether it was running client-side or server-side.

Simple libraries intended to run anywhere would sometimes need to release a client version and a server version.

Even when node started adding support for the standard APIs instead of creating new proprietary APIs, they ended up with a fractured ecosystem where some things would only work with the proprietary APIs, and some would only work with the standard APIs.

There were so many missteps with node that the creator of node essentially said screw this, I'm starting again from scratch, hence: Deno.

Coming back to your point:

> But I can guarantee that it's never that simple, and for the people who consume your library, it won't be either.

But it *IS* that simple.

How?

Because Deno uses the standard APIs for things. If it already works in the browser, it will continue to work in the server. No more fractured language.

> Because Deno uses the standard APIs for things. If it already works in the browser, it will continue to work in the server. No more fractured language.

For greenfield projects? Maybe. For established projects? Not for a very long time.

There's a reason Deno added support for npm modules and CommonJS. Because no amount of wide-eyed naïve fanboyism can replace reality.

This feel pretty biased pro-Deno:

- "main, browser, and fs don't matter in Deno since Deno's APIs are based on browser APIs."

  - fs implies we are reading files, how do you use a browser API to read files in the local filesystem? You cannot, you'd use Deno.readTextFile, which is not browser-compatible, so in Deno you have to either make it for Deno or for the browser, not both. The fields main and browser allow you to make a single package compatible with both NodeJS and front-end JS, something not possible with Deno.
- "type, module, requires, and imports doesn't matter in Deno since it shares the same import API as browsers."

  - Seems like Deno just doesn't support CommonJS, e.g. the vast majority of npm modules, which is a big con and not an advantage: "Current Node.js supports both CommonJS and ES Modules, while Deno only supports ES Modules"
etc.

If you interpret the article as intended, yes Deno will make the pain worse because virtually no one is writing libraries that only supports Deno, so now you'd have all the above issues + Deno-specific ones.

I can see where you've gone wrong, let me briefly go though your points.

> fs implies we are reading files, how do you use a browser API to read files in the local filesystem? You cannot, you'd use Deno.readTextFile, which is not browser-compatible, so in Deno you have to either make it for Deno or for the browser, not both. The fields main and browser allow you to make a single package compatible with both NodeJS and front-end JS, something not possible with Deno.

Both Deno and browsers have a bunch of APIs facilitating File/FileSystem/Stream, etc...

- In Deno, you can use things like Deno.readTextFile which is written on top of those APIs to read/write files on the server's file system which the server will do if it asks for permission and is granted by the developer.

- In browser, you can still use those APIs to read/write files on a private sandboxed file system, and the client can request permission to read/write between the private sandboxed file system and the client's real file system through things like the file picker API's, drag and drop APIs, etc...

- The only real difference between the two is that the developer can explicitly grant the server access to files/folders up-front before the server asks for permission in the first place.

> Seems like Deno just doesn't support CommonJS, e.g. the vast majority of npm modules, which is a big con and not an advantage: "Current Node.js supports both CommonJS and ES Modules, while Deno only supports ES Modules"

Deno *Does* support CommonJS (see the --compat flag). But you probably want to use ESM over CommonJS anyway.

> [...] Deno will make the pain worse because virtually no one is writing libraries that only supports Deno, so now you'd have all the above issues + Deno-specific ones.

If you write your library to the JS standards, then you have code that supports both browsers and Deno (and possibly Node depending on if Node supports those particular standards and you're web standard code isn't being used by your non-web standard code).

- That's not the point, the point is that by having a browser and a main script you can selectively pick whether you are loading one or the other. The browser loads the browser-only code, the main loads the fs-compat code. Browser support for FS is a joke compared to NodeJS support.

- That was a quote from Deno's official page, if it supports commonjs better ofc. "But you probably want to use ESM over CommonJS anyway" -> no way, if you want to use most of the npm packages you need support for commonjs. Ofc you'd be writing ESM for your modern app though.

- There's no "JS standards" for backend stuff like FS, running commands, launching a server, etc. so very often you need backend-specific code. You already showed two extra pain points of using Deno vs Node in this convo, adding the `--compat` flag and granting permissions.

> Package.json, no such thing for Deno (not unless you go out of your way to infect Deno with it)

Yeah...this always felt like a bug in Deno. ¯\_(ツ)_/¯

> scripts, doesn't matter since all Deno projects have the same tools built-in (benchmarker, bundler, compiler, formatted, linter, task runner, test runner, etc...)

AND...you therefore can't do anything outside of the box in a well-defined way. Oops.

Example: How do I run just the main server and not a worker? How do I seed the database?

> dependencies doesn't matter anywhere near as much because Deno comes with a standard library

Sigh. Yeah. Really. You said that.

OK, using the standard library, please:

- Parse Excel files, modify them, and rewrite them to disk.

- Load and modify a PNG and write it out as a JPG. Be sure you scale it down using a Lanczos kernel.

> eslint doesn't matter since deno has a linter

One of the most powerful eslint rules for TypeScript is the detection of dangling promises. A quick glance at the deno lint rules doesn't see anything that will handle this critical lint rule.

And OOPS, adding eslint to the project in a standardized way isn't possible because there's no package.json!

... I could go on.

Sorry, but Deno fanboying is so last year.

> > scripts, doesn't matter since all Deno projects have the same tools built-in (benchmarker, bundler, compiler, formatted, linter, task runner, test runner, etc...)

>

> AND...you therefore can't do anything outside of the box in a well-defined way. Oops.

>

> Example: How do I run just the main server and not a worker? How do I seed the database?

That doesn't really make sense. If you're trying to do anything in a well-defined way, you're going to need a box. The box is the definition.

------------------------------------------------------------------------------------------------

> > dependencies doesn't matter anywhere near as much because Deno comes with a standard library

>

> Sigh. Yeah. Really. You said that.

>

> OK, using the standard library, please: [...]

I'm not going to be able to do these in a comment here, but I can briefly tell you how I would go about doing them.

> - Parse Excel files, modify them, and rewrite them to disk.

Use streams API to create a pipeline (input -> modify -> output).

If by Excel files you mean CSVs, then I can stream the whole thing, making modifications and saving while I'm still streaming in the file.

If by Excel files you mean an xlsx file, then I tweak the pipeline to (input -> unzip -> modify -> rezip -> output).

> - Load and modify a PNG and write it out as a JPG. Be sure you scale it down using a Lanczos kernel.

First I'd make sure I have the encoder and decoder settled. If you have a specific encoder in mind (e.g. ImageMagick + specific settings) then you can box that up by compiling it to web assembly. If you don't have a specific encoder in mind then it doesn't really matter what you use. If you have a specific encoder in mind, then same thing. If you don't, you can always use the JS standard Canvas API which you can load an image into then pull out raw pixel info.

If you want to do the whole thing from scratch, you can do it like the Excel way and create a pipeline using the streams API and TypedArrays.

------------------------------------------------------------------------------------------------

> > eslint doesn't matter since deno has a linter

> One of the most powerful eslint rules for TypeScript is the detection of dangling promises. A quick glance at the deno lint rules doesn't see anything that will handle this critical lint rule.

>

> And OOPS, adding eslint to the project in a standardized way isn't possible because there's no package.json!

Linting for floating promises is tricky to accomplish (ESLint doesn't get it quite right).

Depending on the code, you can get stuck in the halting problem. To solve it correctly, you need to write a compiler/solver that has access to the relevant type information.

This is something that the typescript project itself has been investigating and once solved would be added as another strict rule (e.g. noImplicitOverride, etc...).

If you don't care about correctness and want any heuristic solution, then use any heuristic solution.

ESLint does an amazing job in detecting floating promises. I've not had it miss one, ever. When adding this to a project, I've discovered multiple accidental bugs due to a missing "await" keyword--bugs that were extremely subtle and intermittent in many cases.

The only thing it can't do is determine that you actually did handle the promise later. Which is fine. It's a LINTING RULE, and false positives are the name of the game.

What's BAD is when you accidentally miss handling a promise at all. It's an invisible error without the linting rule.

Your other comments...don't even make sense. You're going to build a Lanczos filter by hand? Or you're only going to ... compile ImageMagick to WebAssembly?!, ... an implementation which is tremendously slower (nearly unusably so for large images) than that of Sharp:

https://www.npmjs.com/package/sharp

... which is simply an import away?

No, what you're doing is called "motivated reasoning." You've concluded that Deno is the best, and you're reinterpreting all of my complaints in convoluted ways to support your predetermined conclusion.

Standard fanboy behavior. Or troll behavior. I cite Poe's Law as why it's impossible to tell the difference.

Not a fanboy. But I'll go through my reasoning anyway.

----------------------------------------------------------------

## Eslint

We're on the same page that:

- heuristics work until they don't

- we'd all rather have false positives than missed positives

- Having something is better than nothing

- ESLint fits it's roll well for now

I've been using node since the 0.x days and used iojs while it was ahead, I've gone from jslint -> jshint -> eslint -> tslint -> eslint (because it supported ts) + sonarlint -> now (which is rome tools in editor, and sonarlint to catch what rome tools doesn't support yet).

The one problem I've had transitioning projects through each linter is just the amount of crufty exception comments to disable rules that ends up through my code.

I've found that I can get rid of a lot of those by setting up my configs and turning on most of the strict modes.

As for promises specifically, I've ended up needing to fire off a lot of floating promises in the last 18 months. There's a lot of firing things off to set values in things that may not exist anymore. (I've noticed it happens a lot a lot around using Promise.race to make code that gracefully recovers in chaotic environments)

The false positives got too much so I disabled the rule (and at a later point moved away from eslint altogether).

There's also some tricky things with catching floating promises when working with frameworks that can deal with super hard to debug side-effects.

So now I have:

- floating promises where for specific situations I don't want to catch their errors and have them throw up to the next layer

- floating promises that are conditionally caught

- floating promises that are actually a wrapped promise that fails as an uncaught promise should, but also conditionally triggers the debugger or logs out a trace.

I use different approaches depending on the situation.

Maybe for you, the only situation you need to deal with is catching all promises (in which case you should continue using ESLint then turn off that rule when TS gets the correct promise handling detection), but for me it causes more trouble than it fixes.

----------------------------------------------------------------

## Images

If I had to do what you suggested, I would have actually just used sharp, but after reading your comment about dependencies, I wasn't sure if you would accept that answer.

I suggested two alternatives:

1. Compiling an existing solution into web assembly to box up that functionality and call into it. It could be ImageMagick, libpng, libspng, libvips, etc... I suggested ImageMagick because that tends to be the go-to for when someone has a specific setting in mind (usually due to a legacy system).

2. Setting up streams and using TypedArrays (which is what sharp uses anyway)

> Nearly every complaint in the article could have been solved by using Deno

Alternatively, don't go out of your way to install Node or Deno—instead, just use the runtime you already have on your computer; browser makers are much better at following the standards (and consequently not encouraging/inducing you to write against standards-incompatible APIs to begin) than the people peddling Node and Node-influenced stuff.

Yeah, these are pain points for engineer, but not for any product, so there is no priority and space to improvement.
It feels as if JavaScript (and its ecosystem) has left us all with PTSD that makes us unable to appreciate the comedy on this post. So much “serious” discussion around a funny piece. Feel free to dissect it as you may, but keep in mind its true intent: humour.
Just because it’s humorous doesn’t mean it’s not serious.