Perfectly describes my 10+ years with the Node ecosystem. Not that it's a bad thing necessarily. I've made it my niche and the knowledge I've accumulated has made for a great career. But still, I understand the narrative.
Perfectly describes my 10+ years with the Node ecosystem. Not that it's a bad thing necessarily. I've made it my niche and the knowledge I've accumulated has made for a great career. But still, I understand the narrative.
The key is they're subtle. If a subtly-wrong design takes only 20% of the code of the truly-correct design to express and understand and it works for 99% of the cases, then there's actually a benefit to a lot of users of using that architecture. If you end up in the swamp outside the happy path you can get badly burned, but that's the thing... If most people never see the swamp, the language's simpler approach can win.
To take an example from the article: the author correctly notes that Go's filesystem library may not be as portable as one wants it to be across OS's, and that it doesn't handle paths as byte-strings. The former issue is one many people don't see because they get to operate on a Linux monoculture (Go's wheel-house was web servers, which are often running on Linux). And the handling of file paths as byte strings is irrelevant if other tools in the Linux ecosystem can't handle them correctly either, i.e. "If it doesn't `ls`, it's not a real filename" is a reasonable position to take on that problem.
It sounds like the author lands in the swamp a lot or has some significant anxiety about ending up in the swamp, and a developer in that situation may very well benefit from using a language and library ecosystem that is more technically correct more of the time.
Obviously all languages have their warts, and I can compensate. Using Typescript with it, I've actually been quite productive. It's higher level of expressiveness relative to Java is also a breath of fresh air.
But some languages definitely have more warts than others, and Javascript/Node is on the higher end of that distribution. All things being equal I would much rather work in a language with fewer warts.
I do Node / TypeScript basically all day these days, and I'm probably too close into the forest anymore; I'm wondering if other people are getting bitten by things that I just roll right past because I'm used to the pain.
The extremely weak (and frankly incorrect) Date handling that's built into JavaScript is the first thing that comes to mind.
- object methods cannot be passed as arguments to a function or another method like regular functions can because they lose their 'this' pointer.
- array.sort() is broken. It does the sort in-place and converts the elements to strings before sorting.
- const doesn't support separate declaration and assignment. This makes it harder to conditionally decide what to assign to a variable while still making it const.
- number types.
- the fact that you have to use '===' for the normal understanding of equality
- Date handling could be better
- NPM has a number of problems, including default behavior of updating your dependency versions.
Regarding `object methods cannot be passed as arguments to a function or another method like regular functions can because they lose their 'this' pointer.`: yeah, the fact that JS objects are actually kinda weird li'l things is frustrating. There are nowadays two ways to handle that:
- referencing the object in a closure binds the object instance. `(x) => foo.bar(x)` will do the right thing
- there is an explicit bind operator, so you can do `myCallbackTaker(instance.myFunction.bind(instance))`
... but I agree that in a better language, you'd be expected to do neither of those things.
Just to share an experience on the other end, I worked with Erlang several years back. While it was a harder language to learn, every new thing I learned about the language gave me the impression that the language designers did the right thing.
Don't get me wrong, I'm having fun with my current Typescript/NodeJS stack at work, but if I was building my own stuff it would absolutely be on the Erlang VM.
Still don't care because 80/20?
If we see a lot of companies succeeding using Go as critical infrastructure, that might be evidence that the swamp is rarely those things. If we don't... The swamp might be those things.
- Incorrect use of concurrency stuff like process.nextTick. This function probably doesn’t do what you think it does based on the function name - it won’t “yield to the event loop”.
- for years Buffer and friends was totally busted, these days Buffer has deprecated insecure APIs and uses an internal object pool to avoid the worst of performance issues.
- until Node 12, async stack traces didn’t work and needed to be copied manually for understandable errors.
- anecdotally, whenever I review a popular library I often find some issue where the library papers over incorrectness in order to expose a simpler, more appealing API. For example when I reviewed the popular NPM package execa for executing subprocesses, it put a NodeJS stream based on a Unix pipe into the subprocesses stderr with no way out, even when you request it to connect the parent process stderr directly. This will break with EPIPE if the subprocess writes too much data. But who cares, we get to see stderr in the exception thrown if the command fails, so that’s a nice choice for adoption.
For a taste of how easy and common it is to get this kind of thing wrong, look at this issue in execa from 2019 - stream error handling leads to unresolved promise that hangs forever. Very hard to blame a programmer for using this API wrong. It is so easy to use wrong, and so hard to use correctly. https://github.com/sindresorhus/execa/issues/350
This is really true. I ported a Node project to Go for this reason, and the streams in Go were miles easier to reason about.
You're right about streams, the way their API was designed just doesn't fit into my process of thought and I'm always heading back to read the spec. carefully + examples.
It definitely could've been made much more simple.