Live data from Hacker News

I finally escaped Node

acco.io

121–130 of 181 posts

Re: I finally escaped Node

#121
post #9

The article focuses on server-side Node, rather than its role as a CLI tool. Compared to V8, Ruby, Python, and PHP are not performant. Node’s libuv network library was highly concurrent and Node built single-core concurrency into the runtime and libraries; multi-core concurrency, however, is not best-of-class. IMO, Ryan Dahl made two fundamental mistakes in both Node and Deno that make them uncompetitive for simple s…

> The lack of a JDBC-like API/SPI hurts many languages included Node and Rust.

I always wondered what is a use case for Rust service that talks to a (non-local)database. First people pay borrow checker tax, get a chance to have an amazingly responsive system in return and then blow it on the round trips to a database. What am I missing?

Re: I finally escaped Node

#122

I'm still astonished that someone was writing Javascript on the browser and found the experience so great that he thought "I also want to do it on the backend!". More seriously, I'll admit I'm not a competent Node dev but the worst part for me is how convoluted you have to write large parts of code that don't need to be asynchronous or non-blocking in the first place. I also never figured why sometimes Node just sile…

"More seriously, I'll admit I'm not a competent Node dev but the worst part for me is how convoluted you have to write large parts of code that don't need to be asynchronous or non-blocking in the first place. I also never figured why sometimes Node just silently ends without pointing my syntax error (and more importantly the line number)."

This, so much. Even for moderately complex data workflows, 95% of the time what I needed to do was "execute a query or other IO operation, then do something with the result of that operation." But since js is async by default, I have to do extra work every time. Even if it's just a simple async/await declaration, there can be complications. Oh, your query failed with an exception so you want to do a rollback? Sorry, you lost scope to that db query when your promise was rejected.

And don't even get me started on the stack traces, or lack thereof. If an exception was thrown inside a promise there's a 75% chance you're not getting anything useful out of that.

Re: I finally escaped Node

#123
post #43
post #19

We went away from node as a backend technology for a bunch of reasons. Here's a list of the biggest pain points: - Lack of a good standard API; compared to environments like Java, C# or Go, node's standard library is significantly sparse. - The tendency for small libraries/frameworks leads to a very high number of third party code with all the problems attached; bigger attack surface, licensing challenges, it's econo…

How do you feel about the impact on developer productivity after migrating to Java + Spring Boot? I haven't used Java in a long while and every time that I try to come back to it, I get driven away by the difficulty and complexity to do simple things (thinking of annotations, dependency injection, complicated design patterns). It feels like an effort of one hour of Node or Python programming (or even Go) would take 1…

I maintain a Java Spring Boot service full time, and when something stops working or doesn't do what you expect, it can be a total nightmare to debug. There is so much misdirection, it can be incredibly difficult to figure out which code will be executed, in which order.

I try to make things as explicit as possible, which can help in testing and debugging.

Re: I finally escaped Node

#124
post #9

The article focuses on server-side Node, rather than its role as a CLI tool. Compared to V8, Ruby, Python, and PHP are not performant. Node’s libuv network library was highly concurrent and Node built single-core concurrency into the runtime and libraries; multi-core concurrency, however, is not best-of-class. IMO, Ryan Dahl made two fundamental mistakes in both Node and Deno that make them uncompetitive for simple s…

> The lack of a JDBC-like API/SPI hurts many languages included Node and Rust. I always wondered what is a use case for Rust service that talks to a (non-local)database. First people pay borrow checker tax, get a chance to have an amazingly responsive system in return and then blow it on the round trips to a database. What am I missing?

Users who use Rust in this way report that these services tend to be extremely robust and have orders of magnitude less resource usage, which in a world of cloud computing translates directly into an improvement on the bottom line.

Re: I finally escaped Node

#125

Earlier quoted context omitted.

I'm only just starting with Elixir, but my impression so far is that I will be refactoring a lot less, due to the way Elixir encourages simple and elegant approaches to many of the common problems a programmer faces. In node for example, I'm refactoring constantly (or, at least, should be...) because I keep backing myself into cognitive and developmental corners by using a kind of brick-by-brick approach that is perh…

Do you have any concrete examples you can link to show these differences? I'm unsure what you mean by architectural decisions made at the language level.

Maybe a good example could be the recursion pattern in Elixir. This is considered almost a base element of the language. Typically in Elixir recursion and "guards", are used instead of things like for-loops and if-else statements in other languages.

Take a basic factorial function in Elixir:

  defmodule Math do
      def factorial(0), do: 1
      def factorial(n), do: n * factorial(n - 1)
    end
There is almost nothing there that isn't directly representative of the base math equation itself (which, by convention, treats the result of factorial(0) as equal to 1). The function order is important in this case, the first is a "guard" that prevents the second from being executed when the firsts case is met. At this point the module exits out of the second function by multiplying the (silently) accumulated result by 1 and returning it.

Versus, in JS:

  function factorial(n) {
    if (n == 0) { 
      return 1;
    } else {
      return (n * factorial(n - 1));
    }
  }
It's not too bad, but the if-else, comparison, multiple return statements and nested brackets at the second return are all done away with in the Elixir version.

Further, recursion is not something many programmers reach for first when working in many other languages, perhaps out of habit, or maybe of the concern that extending such implementations later can become difficult. As such, most programmers might implement the above as more something like:

  function factorial(n) {
    if (n === 0 || n === 1) return 1;
    for (var i = (n - 1); i >= 1; i--) {
      n *= i;
    }
    return n;
  }
Compared to the Elixir code, many steps are required to read and understand this. When this type of laboured patterning is expanded out into a larger project, with many interlocking parts, it may quickly become difficult to work with, and can become necessary, and necessarily difficult, to refactor into something simpler, which then may require rethinking the entire process.

Re: I finally escaped Node

#126
post #84

Earlier quoted context omitted.

A JS library that wants to interop with something outside of its runtime (which is not necessary V8 - it isn't in WinRT, for example) can do so through FFI facilities. So long as said FFI supports all the same things that C does, it supports callbacks via function pointers. And if you can pass a function pointer + data pointer to some API, you have a stateful callback - i.e. a promise/future/task. And you can map any…

Here I'm a bit out of my depth. I think with Erlang you don't need to know >that particular implementation of green threads, but you need to implement the required specifics of the VM FFI (which in practical terms might be what you meant) - but this has a reason, in that, for the VM to provide its guarantees it needs to be able to count "cycles", refs, etc in order to preempt the execution of any function/process at…

That's the thing - it works for Erlang/Elixir, because it tends to live in its own ecosystem with its own libraries etc. If you are working on something where there's an existing large ecosystem in, say, C++, you'll be reinventing the wheel. Or jumping through hoops with a multiprocess implementation (pipes, sockets etc), and having fun synchronizing those.

Now, not all projects are like that - but reusing libraries from other languages is common enough in large projects. Thus, languages that can't accommodate that use case, don't become truly mainstream. Within their niche, they can be much more pleasant to work with, though. So I don't think there's anything wrong with Elixir per se, and for some tasks, it makes perfect sense - but it's not a very general-purpose tool.

Note, by the way, that I'm not talking just (or even mostly!) about JS, but rather about async/await in general - e.g. also in C#, where that syntax originated, or these days in C++20. On Windows, if you write "modern" (UWP) apps, regardless of the language used, they make a lot of async API calls for stuff like UI - and the implementation is all in native code, running in the same process as your app.

Re: I finally escaped Node

#127
post #52

Earlier quoted context omitted.

Green threads are great, right up until the moment you have to interoperate with something written in another language / using another VM. Then it's a mess (see also: Go). The nice thing about async/await is that, because it's all just a bunch of syntactic sugar over callbacks, any language that supports some kind of callbacks with state, can be mapped to async/await - even C! On WinRT, for example, such interop work…

I don't know why green thread based languages do not export a pooling interface on their FFI. Pooling much nicer interface to implement than callbacks, and a quite easy concept to plug one interface into another.

I'm not sure I follow. Can you explain how two runtimes, each with its own green thread implementation, would interoperate in this manner to make a call across the boundary? Say, X (written in A) wants to asynchronously call Y (written in B) and wait on it, passing it Z (also written in A), which Y needs to call several times to perform its task.

Re: I finally escaped Node

#128
post #114
post #94

It’s really hard to read such posts. The tool is only as good as the contributor, that’s all there is. Great things were built in all languages and frameworks. For god sakes, stop blaming the tool by doing naive comparisons with other languages/framework. Nothing’s perfect, that’s a given, but these kinds of post smells so much like entitlement. You don’t need to trash your previous framework/language and discourage…

I get where you're coming from - the post meanders around some topics every programmer has to learn by hard experience, such as the value of being able to reason about code / systems and the importance of data structures. That said my feeling is Node gives you multiple pump action shotguns you can easily shoot yourself in the foot with. For example https://nodejs.org/en/docs/guides/dont-block-the-event-loop/ > One co…

I understand all the things you're talking about, and I agree, you can block the main thread (and elixir does not use the OS processes so it's awesome...). I do disagree with the take on await/async but that's a different discussion.

Is the article insightful? Mostly, yes.

Did Node and JS have to be painted this way? probably not.

Re: I finally escaped Node

#129
post #52

Earlier quoted context omitted.

> You know what's an even worse cognitive overhead? Languages without strong typing, like Elixir. I see this mentioned many times, but when I do my impression (which might be wrong) is that you haven't written Elixir or Erlang for any thing more serious than tutorials or docs examples. Besides, elixir is technically (?) strongly typed - but not statically typed. Regarding async/await, compared to the things you get i…

Green threads are great, right up until the moment you have to interoperate with something written in another language / using another VM. Then it's a mess (see also: Go). The nice thing about async/await is that, because it's all just a bunch of syntactic sugar over callbacks, any language that supports some kind of callbacks with state, can be mapped to async/await - even C! On WinRT, for example, such interop work…

> Green threads are great, right up until the moment you have to interoperate with something written in another language / using another VM

You mean like this? It's not a problem in elixir.

https://www.youtube.com/watch?v=l848TOmI6LI

Re: I finally escaped Node

#130
post #20

From the title, I was expecting the article on something along the lines of how, where and why developers can move from Node.js. However the arguments are not so solid and there is no migration path. We all know Elixir is great but the adoption and maturity is low as compared to JS. Yes, Node is not perfect but (with TypeScript added) tell me a development platform that can run under 100 MB of memory, handle most req…

"And the same devs can also use the same skills to build a frontend".

This is such an overstatement. Knowing the syntax is just 10% of the story.

There is literally 0 common things in for example React + Styling + SEO + browser + knowing good standards for the web vs. backend expressjs api talking to some DB and knowing good practices for having scalable backend based on for example 12 factor practices.

Post reply on HN