7 ms·
I agree with some points author is making, but overall I wouldn't put them either on Node.js runtime, or Node.js ecosystem. a) Erratic max latency IME Node.js
by watermelon0 6y ago
I agree with some points author is making, but overall I wouldn't put them either on Node.js runtime, or Node.js ecosystem.
a) Erratic max latency
IME Node.js performs well if you are not pushing its limits. If you have a service receiving 25k+ requests per second, you should better benchmark it, regardless of the language. Horizontal scaling should be configured based on the results of the benchmark.
b) Cognitive overhead
Yes, there is a bit of an additional overhead when mixing async and non async functions, but same goes for every other language that supports promises, C#, Python, Rust, Scala, etc.
Typescript should fail if you try to access a property/method of a promise, without resolving it, so I'd say it's not such a big deal.
Personally, I rarely work on Node.js, but when I do, I'm really grateful for await/async and typescript.
You know what's an even worse cognitive overhead? Languages without strong typing, like Elixir.
c) `pg` connect & query
I would say the issue lies with how the library is implemented. I'd definitely expect `query` to fail if it's not connected to the database.
- dnautics 6y ago> Languages without strong typing, like Elixir. Have you ever actually worked with elixir? In practice the (strong) dynamic typing of elixir is not a problem. I have not written a type error that has made it to prod in tens of thousands of lines of code. The last type error I made only showed up extremely rarely (once a week or so) and it didn't matter because the supervisory tree restarted the process that threw it. Guess what? I didn't fix it. No one will die and $0 will be lost. There's very little ambiguity about falsely values and you can't arbitrarily coerce from one value to the next. That in and of itself cuts down on the type confusion, and also the fact that there are only about ten types. Even struct field names are compile-time checked for you, and I get little squiggly lines warning me about statically checked type errors in elixir-ls. Sure it's not perfect, but saying it's a cognitive burden is just... Silly.
- marshmellman 6y agoHow is refactoring? I was considering moving from Node to Elixir, but now that I’m using TypeScript it does feel like I would be giving something up. It’s a breeze to make broad changes knowing the compiler will prevent me from missing a code path, etc.
- dnautics 6y agoWrite your tests, gradually make red go to green. Super rewarding. Code coverage reports are a thing. You should be writing your tests async (including tests that leave the VM) in elixir, that will give you a chance to catch and raise asserts on race conditions which will stress ouct timings in your vm, etc. https://youtu.be/hvgdWQuriB4 https://youtu.be/hvgdWQuriB4
- mcintyre1994 6y agoThanks for sharing this, looks like a really useful series of videos :)
- thdxr 6y agoHaving spent a lot of time in both it's hard to argue it's as easy in Elixir. You can do it reasonably with a lot of tests but it's nowhere near the same experience as TS
- argvargc 6y agoI'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 perhaps encouraged not only by the language but by the eco-system. I'm not finding this with Elixir, possibly because many of the more important architectural decisions have already been made for me at the language level, and apparently been made very well. The hurdle becomes more understanding how to best do something, rather than figuring out how to do something at all. A big part of this also may be Elixir's REPL. It has help for everything built-in. Whereas in node for example I find I'm constantly searching online and piecing together things from many different parts, in Elixir I almost never am, it's all just right there.
- ducharmdev 6y agoDo 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.
- hnedeotes 6y ago> 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 in the BEAM - that part specially is like comparing a stone wheel car moved by oxen with a space shuttle that has the same costs (or less), to launch and operate. It might sound snobbish, but sincerely I don't know what to say when I read that. Although not compile time, pattern matching allows you to define as strict, and in some cases, stricter conditions for functions than many typed languages, without any additional cognitive overhead or complex type definitions that you need a PhD to understand. def do_it(<<a::binary-size(2), ":", b::binary-size(4)>>), do: IO.puts("the argument is a binary string, of the form xx:xxxx") Even conditions where it depends on more than the types, like with complex types such as maps, structs, tuples, lists, where parts of those conform to a certain pattern, in any combination needed.
- thdxr 6y agoI've been using Elixir for five years and I think it's sorely missing a better type system. I've done fine without it and have gotten entirely used to it but that doesn't mean things wouldn't be better with one. I only really started noticing as I started to build more complex Typescript systems how much it helps. My teammate shipped a bug in Elixir yesterday that would have been caught by a static type checker
- hnedeotes 6y agoI know there's plenty of people working with Elixir (and Erlang) that feel that way. I can also imagine that in very large codebases dialyser might not be enough and it would be great to have it. Also that obviously, outside web and telecom there's definitively software which I wouldn't want written in them, but not in TS either, and probably it would have more tests than code, property and fuzzy and all the remaining things. I also won't say that it doesn't happen but I do believe if the same effort that goes into writing typed typescript and tests, aka, using dialyser and writing tests, that there shouldn't really be any difference? And when you include things like guards, explicit pattern matching, casting your data at the entry points of your system that it provides a much more robust experience? But that's all optional - what I've seen is that theoretically there's nothing you can't express with the base syntax (+ecto) that you could with most (as in the common ones) type systems, perhaps the difference is that since in a typed language it won't compile you are forced to do those things, define all enums, all their variants, exhaustively match, etc?
- martinald 6y ago(This may have changed since I used it in anger last). NodeJS does not handle multicore servers well, and I imagine many people don't realise this and have a huge amount of unused CPU capacity. You need to run multiple nodejs processes (one for each core really) to get the most of it. But this then requires load balancing even on a single machine, so a lot of people don't do it. There are other solutions but often require json strings passed between the workers, instead of objects, which can end up stalling the whole thing (more time spend with json (de)seralization than actually doing work on complex models). .NET (core) handles this extremely well. It's trivial to do a parallel async foreach, and it's easy to scope services (eg database access) to avoid threading problems. It also automatically uses as many cores as you have without any extra problems for handling multiple requests at once. This is even more of a problem on mobile apps in React Native, which I've used a lot of. It is virtually impossible to use multiple CPU cores efficently on a RN application, which is pretty crazy considering how many cores mobile phones have. For 99% of projects it's not a huge issue, but on some it becomes a real killer, especially as the workarounds involve sending json strings between processes, which can end up being the blocker itself with complex state you want to pass between threads.
- hajile 6y agoNode has worker threads and shared memory buffers to share data between them.
- martinald 6y agoDo you have a link to the shared memory buffers? I can't find much about them. I've found some stuff about worker threads but can only pass strings.
- hajile 6y agoHere's one overview that's a pretty easy overview. https://dev.to/feezyhendrix/worker-threads-in-node-js-2ikh https://dev.to/feezyhendrix/worker-threads-in-node-js-2ikh You can read more about atomics and shared array buffers in JS on MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe... https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...