Disclaimer: I worked with Node.js full-time for about 14 months, ending about 9 months ago. I'm willing to admit that the following complaints may or may not be out of date, especially with ES6 and newer Node versions. Proceed with grains of salt at the ready.
There are a lot of things to like about Node.js, but the primary thing that bothers me about it is the obsession with async. It's a language/framework designed to make pretty much everything async.
In the real world, almost everything is synchronous, and only occasionally do you really want async behavior. By that I mean, you almost always want A, B, C, D, E, F, G in that order, and only occasionally would you say that you want async(H,I). But with Node, it's the other way around. You assume things are async, and then specify synchronous behavior.
Instead of assuming A; B; C; D; E; F; G;, you wind up with a ton of code like A.then(B).then(C).then(D).then(E).then(F).then(G);
...I know that's a contrived example, and I know you don't really need to do it that way, but so many people do, and it really illustrates the point. In Node.js, you are explicitly synchronous / implicitly async. Most other coding paradigms (including Go) better match what I consider reality, which is that everything is implicitly synchronous, and you specify async behavior when you need it.
Basically, I think it's backward. But perhaps like the OP, I just can't wrap my head around it.
The NPM stuff... well, I think all ecosystems have their pros and cons. I'm not a huge fan of NPM, but it does the job for the most part, and I'm curious as to how people would actually improve it, rather than just complain about it all the time. I don't really have any good ideas (knowing nothing about how package management actually works under the hood).
Well.... no, not really. Processors really are fundamentally asynchronous. They are state machines which react when external signals impinge upon them. Synchronous behavior is a useful illusion, a convenient reasoning tool for some kinds of tasks, and our traditional notations for describing computational processes are all based on this model of how computing might be done - but it hasn't actually been done that way since the batch-processing era.
I learned this as I dove into low-level hardware work - drivers and embedded systems. When you're dealing with hardware, life is fundamentally driven by interrupts. Processors spend most of their time asleep. When something happens, the signal level of an input pin changes, and the processor reacts: it wakes up, it starts executing the interrupt handler for that signal, it does things, it sends messages, and then - its job done - it goes back to sleep.
So what, we're talking about normal computers here, you say, surely they're different, right? But no, they really aren't; it's the same process of signal, interrupt, handler, return, all the time. Every context switch, every packet receipt or transmit completion, every timer tick, all of it, it's all driven asynchronously; the system is a hierarchy of event handlers waiting for their opportunity to respond to some external signal.
If you're trying to build an IO system, then, the synchronous model is an expensive illusion. You can force it, but it isn't reality. You spend time waiting, you spend time blocking, you build complex APIs that pretend to poll arrays of connections in parallel; and all you're doing is forcing that illusion of synchrony onto a fundamentally asynchronous world.
It's backwards, all right, but it's backwards because our traditional way of describing computation is backwards, especially the languages which follow the imperative tradition. Functional languages have gained popularity in part because the mental model works better for asynchrony; instead of describing a series of steps to follow, and treating the interrupt as the exception, you describe the processes that should be undertaken in certain circumstances; you effectively construct a state-machine instead of describing a process, and then you leave that state-machine to be poked and prodded by whatever external signals come along to drive it.
Mainstream processors are synchronous. For this reason there is a global clock needed to synchronise all (most) the chip parts.
I remember there was some research on asynchronous CPUs but I don't think it ever yielded anything commercial.
What you are saying is that they react to interrupts asynchronously, but this has nothing to do with the synchronous nature of a CPU.
Moreover I think that if you have a CPU that doesn't respect the barriers and reorders the instructions to execute them in parallel, then it's a bugged CPU.
The whole point of abstractions is for us to not have to worry about the underlying complexity. If I want to program in a way that is close to how the hardware works, I'll pick a language like C, however when I choose a high level language, I do not want to care if everything my processor performs is async.
What difference does it make if from the processor's perspective I have:
```js
somepoeration().then(function() {
// do stuff
});
```
or
```js
somepoeration();
// do stuff
```
In both cases my program will just wait for 'somepoeration' to complete before doing anything useful.
You're right, and you're also being chronically misunderstood. It's not about "asynchronous" in the sense of "unclocked"; it's more about how incredibly fast processors are if and only if they aren't waiting for something else.
The programmer would like to think of the series of operations involved on a per-connection basis. Whereas from the point of view of the processor a "connection" is not a thing; there is only a stream of incoming message fragments at occasional intervals from the network and I/O subsystem. It can request data from persistent storage by what is effectively snail mail: send a message and wait a few million cycles for it to come back.
So the software must consist of a set of state machines. We can push those up into the operating system and call them threads, or down into the user's code and call them callbacks and continuations. Each approach has advantages and disadvantages, and it's important to understand what they are.
(Possibly the language which does this best is Erlang, although it's not as easy to get started with as node.js. Theorists really overlook the vital metric of "time to hello world" in language learning.)
> the mental model works better for asynchrony; instead of describing a series of steps to follow, and treating the interrupt as the exception, you describe the processes that should be undertaken in certain circumstances
I could never understand how this works better as a mental model. Say you ask somebody to buy you a gadget in a store they don't know. What do you do tell them:
a) "drive in your car on this street, turn left on Prune street, turn right on Elm street, the store will be after the second light. Go there, find "Gadgets" isle, on the second shelf in the middle there would be a green gadget saying "Magnificent Gadget", buy it and bring it home"
or:
b) when you find yourself at home, go to car. When you find yourself in the car, if you have a gadget, drive home, otherwise if you're on Elm street, drive in direction of Prune Street. If you're in the crossing of Elm street and Prune street, turn to Prune street if you have a gadget but to Elm street if you don't. When you are on Prune street, count the lights. When the light count reaches two, if you're on Prune street, then stop and exit the vehicle. If you're outside the vehicle and on Prune street and have no gadget, locate store and enter it, otherwise enter the vehicle. If you're in the store and have no gadget then start counting shelves, otherwise proceed to checkout. Etc. etc. - I can't even finish it!
I don't see how "steps to follow" is not the most natural mental model for humans to achieve things - we're using it every day! We sometimes do go event-driven - like, if you're driving and somebody calls, you may perform event-driven routine "answer the phone and talk to your wife" or "ignore the call and remember to call back when you arrive", etc. But again, most of these routines will be series of steps, only triggered by an event.
I think a lot of the issues with JavaScript are around the movement of individuals from synchronous programming languages to the asynchronous JavaScript way.
For some time it resulted in callback hell (http://callbackhell.com/). This was addressed through Promises and more recently Observables, Generators, and async/await.
With transpiling, adoption of these asynchronous concepts has been possible quicker than would have been achieved otherwise.
I would suggest revisiting some of the modern JS constructs to support better use of the underlying cpu architecture.
I will however admit, getting my head around Promises took about a month before it really clicked how powerful this approach can be to support non-blocking IO.
It's not about how the processor works. A developer which is using a language of JavaScript's abstraction level does not think about exactly how a processor works.
It's about how a human can easier think up a program and put it into code. And that is mostly synchronous indeed.
How many JS-targeted VMs / container things run just one thread of this async-ness when more are available to that container / VM / context? I'd rather run mod_php under preform MPM and take advantage of all the cores. Is there something as easy and well-supported for running Node apps in things like Docker?
> In the real world, almost everything is synchronous, and only occasionally do you really want async behavior.
From an Ops perspective, I disagree. At a systems level, everything already is async, whether you like it or not. So then the options are either to embrace the admittedly hectic nature of async, or pretent that things are synchronous and impose those constraints to maintain a logical purity from a programmer's standpoint.
For example, in a synchronous setup, it's perfectly logical to service an incoming http request with a few database queries followed by some more logic and some more DB queries or API calls; wrap all the result and send it back. The same in node.js forces the code author to think about those initial DB queries (which may run simultaneously), about how to roll up the result when they are all done and pass it to callbacks.
But while thinking about those callback interactions, there's a motivation to reduce the number of steps, or to factor them out into their own module or microservice. This promotes thinking at a systems level instead of just at the level of "1 request serviced by 1 handler". Perhaps that big request can be broken up into several API calls and the web page can load those asynchronously. Perhaps the smaller microservices end up building up less memory usage per request (or spread it out in a way that makes clustering more efficient).
One may see async as a hurdle in the way of logical abstraction, or as a way to align thinking with the realities of network programming.
>>So then the options are either to embrace the admittedly hectic nature of async, or pretent that things are synchronous and impose those constraints to maintain a logical purity from a programmer's standpoint.
Logical purity is important. It helps people reason about their code better, which in turn makes them more productive.
The reason we have frameworks and high-level languages is so that we can take advantage of abstractions and not worry about the underlying complexities of the system. If a framework is making you "embrace the hectic nature" of those complexities, why use that framework in the first place? Might as well use a language like C, which actually is a low-level language designed for developing hyper-optimized software. Node.js, on the other hand, is supposed to be a Web (read: high-level) framework. That's why implicit-async doesn't fit its nature, and in large codebases you see nested callbacks all the way down.
The advantage you're describing is concurrency, which doesn't necessarily require an asynchronous/event-driven programming model. Synchronous concurrency is built around threads to achieve concurrent execution (i.e. instead of waiting on events for several requests to finish in parallel, you fork off a thread for each to process each request synchronously).
Node came of age about a decade after epoll was introduced, when not having access to nonblocking IO was considered a big liability for a couple of dominant web programming languages, and they built their concurrency model around the semantics of epoll.
However, there are languages like Haskell, Erlang, and Go that IMO did the right thing by building a synchronous programming model for concurrency and offering preemptable lightweight processes to avoid the overhead associated with OS thread per connection concurrency models. These languages offer concurrent semantics to programmers, yet still are able to use nonblocking IO underneath the covers by parking processes waiting on I/O events. It's not the right tradeoff for every language, particularly I think lower level languages like Rust are better off not inheriting all the extra baggage of a runtime like this, but for higher level languages I think its probably the most convenient model to programmers.
I think what this really gets at is that callbacks are a shitty concurrency primitive.
They certainly make sense from an implementation perspective (I've got this interpreter and I want to do concurrency... I know, I'll just use it to trampoline callbacks).
But callbacks are too low level to reason about easily to some extent, it's kind of like writing everything as a series of goto statements. Sure you can do it, but good luck following it.
The advantages of asynchronous execution are lost. This isn't limited to nodejs, I've experienced similar problems with Python twisted...
Channels (such as in clojure/core.async) or lightweight processes and messaging (such as in erlang/beam), are much more enjoyable to work with.
This shouldn't really be a shock, however, they're higher level abstractions based on real computer science research by people who really thought about the problem.
Callbacks are ugly when you have to deal with them directly. But a little bit of syntactic sugar to capture the current continuation as a callback - such as C# await, or the now-repurposed yield in Python - makes it very easy and straightforward to write callback-based async code.
Fortunately JavaScript is a flexible enough language that you don't only have to use the primitives it provides.
Promises (which were pure JS before they were ever added to the language) are a more-composable structure for reasoning about concurrency. I don't think I've put more than one line of callback-style code into production per month.
On the subject of nice higher-level concurrency abstractions, I'm very taken with the current fad for Promises/Futures (a la ES6 or Scala), which are basically a translation of the Haskell IO monad to plain English.
I was a node developer back when it hadn't been packaged yet (yes, I'm a hipster). I left a few years ago for many reasons, including the fact that I think the community sucks.
I agree with everything you say, and I want to add to it. People compare node to things like Go, PHP, Python, etc, which is a mistake. As someone who had to write a fairly complex native module for node that a lot of people still use (5000 downloads this month, according to npm), nodeJS is a framework not a language.
Node is not in control of the underlying code that implements the ES spec. Node is a bunch of C/C++ middleware wrapping V8 and implementing various server and process utilities. That is all it is.
Why is this important? Because it biases the community towards piling on feature after feature and moving at an insane pace, sometimes in ways that are outside of the community's control. Node is biased against stability, because they feel the need to keep up with V8. Counter this with Go, which has been frozen as a language for 8 releases now, with each release fine tuning the stability of the compiler/language, runtime, and libs.
That native module that I mentioned earlier, I have a bunch of open requests to make it work with nodeJS 6 (no idea what this is), which is the fourth breaking V8 interface change I've had to deal with. The native module clocks in at >2000 lines of C++. I implemented the same library in Go and it clocks in at 500 lines. I've only had to modify the Go version of this library once, and it was to take advantage of a new feature (i.e. I didn't have to update it).
Node's continual quest for features is going to keep it in the niche web development space it has come to dominate. There is simply no way I would architect a system with such an unstable platform.
Doing synchronious is much clearer and easier, most of the async patterns (like yield/await and promises) really just try to simulate a sync paradigm.
But there is one great reason why node's async first mentality is superior- when you want to do async in node, you don't have to worry that some library you are using is going to lock up your thread.
In any other language, you have to painstaikingly make sure everything you use isn't doing sync, or try and monkeypatch all io operations (python has something like that).
Frankly, for me, when I'm dealing with webservers I find myself needing to use async quite a lot.
No; for example, in Go, every time the code does IO, the scheduler will re-assign another goroutine, and they asynchronously get back to the other when the IO finishes. It doesn't need any special support by the library.
> Doing synchronious is much clearer and easier, most of the async patterns (like yield/await and promises) really just try to simulate a sync paradigm.
I'm currently working on some lighting (DMX-based theatre lighting) software and the ability to have my 'effects' written in a sequential manner by using 'yield' is actually incredible. It's simplified my code a huge amount and made it a lot easier to reason about.
There is a function called every frame, which then calls each effect. Since each effect is a Python generator function, it can be written sequentially and just has to yield whenever the frame is computed.
> when you want to do async in node, you don't have to worry that some library you are using is going to lock up your thread.
> In any other language, you have to painstaikingly make sure everything you use isn't doing sync
I haven't found that to be the case TBH. In most other languages i've used i don't have to worry about foo() being syncronous or not. I can assume that. In other words, after the control returns from the foo() call, i can assume that what foo() is supposed to do has been completed. In the case that foo() needs to do some IO operation, i can usually rely on it doing a syscall that will block the thread and put it to sleep, instead of doing something nefarious like busy waiting.
And that's it. The current thread will sleep while the IO operation in foo() completes, and the OS (or VM if we're talking green threads) will switch the CPU to do something else in the main time. And if it's a web server we're talking about, that something else may be another web server thread, serving a different request. Yay, preemptive multitasking!
In the case of node, you do very much have to worry about a function call being synchronous or asynchronous, as it defines the way it has to be called. In the former case, it can be called "normally":
let a = foo()
// use `a`
In the async case, some code gymnastics need to be involved:
// Either callbacks:
foo(a => {
// use `a`
})
// Or promises:
foo().then(a => {
// use `a`
})
// Or promises + await syntax:
let a = await foo()
It may not seem a big problem at first, especially if using the `await` syntax is an option. But to me the biggest problem of this technique is that the "async-ness" of a function cannot be abstracted away. If a function `foo` that has always been synchronous, and that is used in several places, needs to be changed, and this change implies `foo` calling an async function `bar`, then `foo` will need to become async too, and all the places where `foo` is called will need to be changed. Even though this internal `bar` async call was an implementation detail of `foo` that shouldn't have concerned `foo` callers. And this propagates all the way up: if any of the places where `foo` was called was, in turn, a synchronous function too, then that function will also need to be refactored into an async one, and so on and so forth.
And that's basically why i find node's distinction between sync and async functions so frustrating :(
PS: Cooperative multitasking does not imply this weird syntax complexity of having two different kinds of function calls that cannot be freely intermixed. For instance, Eralng "processes" use cooperative multitasking, but the "yields" are managed implicitly by the VM, so you don't need to explicitly yield control of the current thread to let other stuff run concurrently.
Maybe it's because I learned to write code in javascript (browser and node) -- does that make me an async native? -- that I feel the reverse of you. Synchronous just doesn't feel right, and I'm happy with the way node is assumes async. I'm one of those who finds the new Await syntax sort of unsettling at a gut level.
Async isnt about ordered operations its about waiting. When you make a database call, you thread can sit there for 50 ms or do something useful. You can outsource rendering work to a child process. You dont have to let infinite loops block events.
That 50ms isn't necessarily wasted. When I get the results of that database call I want the results returned to the user ASAP, with async operations the thread may be too busy to do that.
We do this daily in real life. It's more efficient for me to have 10 minutes down time at work while I'm waiting for someone else than it is for me to start something else then either get interrupted or finish another task before I respond to the person I'm waiting on.
This seems absurd to me. What exactly are we going to sit there and do while waiting on the data we need to fulfill a query?
I work on services where any given endpoint may handle many thousands of requests per second. We don't care about a slight penalty to query the database, because these are very short, and our services return responses on the order of 100 ms.
Maybe these are things you care about in a language like Javascript, but in something statically typed, they just don't seem like a problem.
This isn't intrinsically bad. It just makes Node.js biased towards use cases that benefit from asynchronous I/O, which is a perfectly valid design choice. Sensible programmers already know that computationally intensive tasks that benefit from actual parallelism are best served by a different tool.
What's absolutely horrible about Node.js is how they make everything asynchronous: explicit continuation passing everywhere. In other words, the user is responsible for manually multiplexing multiple logical threads through a single-OS-threaded event loop. If it sounds low level, it's because it is low-level. A high-level programming language ought to to better than this.
Other languages get this right: Erlang, Go, Clojure, Scala, Haskell, Rust, etc.
2) All I/O (if you're doing it right) is asynchronous.
Node does things the right way around; it's just a shame that Linux (unlike Windows) doesn't have async I/O baked into the heart of the OS and well supported with useful kernel primitives.
Synchronous I/O by default means you're wasting your CPU cores. When running at Web scale, you ALWAYS want to leverage async I/O in order to not have the CPU idle blocking on some I/O operation, and you don't necessarily want the synchronization and memory overhead of multiple threads. That's why Node is designed the way it is.
By using the 'co' library and generators you can write async code almost as if it were sync. It works like the inline-callbacks feature of Twisted.
Synchronous I/O by default means you're wasting your CPU cores.
But that's the thing: synchronous code doesn't mean synchronous execution. The language/platform should be able to make it async for you, without forcing you to re-shape your code. And it's not a rare system: even plain old threads work like that - in fact, they're often a good solution: https://www.mailinator.com/tymaPaulMultithreaded.pdf
> it's just a shame that Linux (unlike Windows) doesn't have async I/O baked into the heart of the OS and well supported with useful kernel primitives.
What about epoll()?
> Synchronous I/O by default means you're wasting your CPU cores.
You can implement synchronous IO on top of an async backend using the above mentioned epoll. I believe that is how read() and similar syscalls are implemented. They block the programs thread, but the core is free to run other threads while it waits for IO.
> Synchronous I/O by default means you're wasting your CPU cores
When a process becomes blocked (like due to synchronously waiting for IO to complete), the kernel will context switch away to another runnable process. The process that becomes blocked essentially goes to sleep, and then "wakes up" and becomes runnable again once the operation completes. The CPU is free during this time to run other processes.
If you need to do multiple different of these things concurrently, then you can run multiple processes. Writing a single process with async code won't make that process faster. To do more things at the same time you can run multiple processes. Context switching between different processes is what the kernel scheduler is designed to do, and it does so very efficiently. There isn't much overhead per thread. If I recall correctly, Linux kernel stacks per thread are 8 kilobytes (with efforts under way to reduce that further [4] - also discussed in [1]), and the user stack space is something the application can control and tune. The memory use per thread needn't be much.
Using all available cores to perform useful work is the most important thing to achieve in high-throughput code, and both async and sync can achieve it. Async doesn't become necessary for high performance unless you're considering very high performance which is beyond the reach of NodeJS anyway [2]. Asynchronous techniques win on top performance benchmarks, but typical multithreaded blocking synchronous Java can still handily beat async NodeJS, since Java will use all available CPU cores while Node's performance is blocked on one (unless you use special limited techniques). There's some good discussion about this in the article and thread about "Zero-cost futures in Rust" [1]. The article includes a benchmark which compares Java, Go, and NodeJS performance. These benchmarks suggest that the other tested platforms provide 10-20x better throughput than Node (they're also asynchronous, so this benchmark isn't about sync/async).
Folks might also be interested in the TechEmpower web framework benchmark [2]. The top Java entry ("rapidoid") is #2 on the benchmark and achieves 99.9% of the performance of the #1 entry (ulib in C++). These frameworks both achieve about 6.9 million requests per second. The top Java Netty server (widely deployed async/NIO server) is about 50% of that, while the Java Jetty server, which is regular synchronous socket code, clocks in at 10% of the best or 710,000 R/s. NodeJS manages 320,000 R/s which is 4.6% of the best. In other words, performance-focused async Java achieves 20x, regular asynchronous Java achieves 10x, and boring old synchronous Jetty is still 2x better than NodeJS throughput. NodeJS does a pretty good job given that it's interpreted while Java is compiled, though Lua with an Nginx frontend can manage about 4x more.
I agree that asynchronous execution can provide an advantage, but it's not the only factor to consider while evaluating performance. If throughput is someone's goal, then NodeJS is not the best platform due to its concurrency and interpreter handicap. If you value performance then you'll chose another platform that offers magnitude better requests-per-second throughput, such as C++, Java, Rust, or Go according to [1] and [2]. Asynchronous execution also does not necessarily require asynchronous programming. Other languages have good or better async support -- for example, see C#'s `await` keyword. [3] explores async in JavaScript, await in C#, as well as Go, and makes the case that Go handles async most elegantly of those options. Java has Quasar, which allows you to write regular code that runs as fibers [5]. The code is completely normal blocking code, but the Quasar runtime handles running it asynchronously with high concurrency and M:N threading. Plus these fibers can interoperate with regular threads. Pretty gnarly stuff (but requires bytecode tampering). If async is your preference over Quasar's sync, then Akka might be up your alley instead [6].
> By using the 'co' library and generators you can write async code almost as if it were sync.
For an interesting and humorous take on the difficulties of NodeJS's approach to async, and where that breaks down in the author's opinion, see "What color is your function?" [3].
> 2) All I/O (if you're doing it right) is asynchronous.
Not true. Synchronous I/O is higher-throughput. If you need to do a large amount of high-latency I/O (e.g. web requests) then asynchronous can end up being lower-overhead overall, but making everything async is just as bad as making everything sync.
In the past, I was involved with building a Scheme interpreter which was automatically asynchronous, without need for manually handling it with callbacks.
Basically, it was lightweight threads which would yield execution to other threads whenever waiting for I/O, or explicitly yielding. It allowed for a very straightforward linear programming style, no callback hell.
Coupled with a functional programming style, there was rarely a need for mutexes or other synchronization between threads.
When a thread yielded, the current continuation was captured and resumed when the thread was ready to continue. At the bottom of it all was a simple event loop where the Scheme interpreter dispatched I/O and timeouts. Scheme happens to have support for continuations built in to the language, so the implementation was actually quite simple.
I've built something similar but a completely new programming language with backtracking as the major feature. The intended use is dynamic OS configuration especially network interface configuration.
The interpreter is single-threaded but there is simple concurrency between the "processes", that is a process yields when it has nothing more to immediately do.
> Basically, it was lightweight threads which would yield execution to other threads whenever waiting for I/O, or explicitly yielding. It allowed for a very straightforward linear programming style, no callback hell.
How is this different from a normal multithreaded C program on a POSIX kernel?
When I call read(), my thread is suspended until the data comes back, and another thread can run.
This is exactly what I was about to write. Also, the amount of "callbacks, that's how it's supposed to be" and "hardware is like that" and "programming is hard, deal with it" kind of replies for a readily solved problem are staggering.
It's a solution for a made-up problem. Sure, async/await makes it easier. But not as good as the normal sync code would be. And for what reason?
The concurrency problems are already a solved issue in many many frameworks. Sure, if you do fully manual threads in something like C++ and code up everything yourself - you're in for the trouble down the road unless you are a good professional. But there are heaps of concurrency frameworks in all major languages that make this very easy.
And what about performance, how does JavaScript justify putting virtually every single return value of every function into some Promise object? Talk about wasted resources.
> The NPM stuff... well, I think all ecosystems have their pros and cons. I'm not a huge fan of NPM, but it does the job for the most part, and I'm curious as to how people would actually improve it, rather than just complain about it all the time. I don't really have any good ideas (knowing nothing about how package management actually works under the hood).
- Remove the Couchbase dependency
- Make login authentication a plugin or API-based
- Integration with Github/Gitlab for automated publishes
Yes, and on top of that, it is asynchronicity without real concurrency. Imagine one part of the code doing a long CPU-bound computation; the rest has to wait!
With due caution about @marssaxman's point that software is fundemantally interrupt driven, I will agree with this.
I work an a real-time system that is controlled by RPC from a desktop. We claim we want to keep the real-time code small and simple.
Yet I find people pushing complexity down onto the RT system even when the real-time guarantees aren't needed because the PC code responds to it using a callback driven async model. It's easier to put a non-real time for loop into the RT thread than to emulate a for loop on the PC.
I think the idea of having a powerful scripting language based on JS is a godsend in many ways. But like you, I also feel that async is a bad default for a language that has such widespread use.
I love being able to use one syntax to write 95% of my code(instead of having to switch between JS / python / php etc.) but some of NodeJS's programming and ecosystem realities are annoying to say the least.
No. That's nonsense. Everything is async in node because node is based on the V8 Javascript engine which is not thread safe. V8 is not thread safe because it was designed for web browsers, and browser rendering engines are also not thread safe.
People have tried to retcon node's choices as being based on some sort of deep technical justification, but that's not the case: its limits are the limits of the material it's made of, not a specific design choice.
If you look at more advanced VMs like the CLR or JVM then they support all modes of operation: synchronous/blocking (which is often what you want), synchronous/blocking with many threads, async with callbacks, async with continuations ... node only supports one. How is that better?
The universe is inherently concurrent. It only makes sense for programming languages (which are used to model problems in this universe) to follow along.
Your comments about the annoyingness of async are real.
BUT
Just because you want to do ABCDE does not mean it's feasible.
You can access a file, read it, compute something, depending on that access another file, read it, write something via Http Post somewhere else.
BUT the nature of i/o is such that the problem is 'intrinsic' - it's not a 'node.js' problem so much.
Of course you know that you can do everything syncrhonously if you want, but then you get hangups and what not waiting for underlying resources, networking calls etc..
The nature of a distributed system is inherently async, and so we have to live with it.
That said - maybe you have some examples of where async definitely should not be used?
The other huge problem with Node.js is the vast, crazy expansion of tiny libraries, supported by ghosts, that change all the time. A simple lib can depend on a dozen others, with varying versions, everything changes almost daily. A small change somewhere can really jam things up.
This is by far my #1 concern, although it can kind of be avoided if you're careful.
> Just because you want to do ABCDE does not mean it's feasible.
Every single language is able to do ABCDE, except for JavaScript. Yes, I guess that means it's impossible.
> The nature of a distributed system is inherently async, and so we have to live with it.
The nature of a distributed system is the one you impose onto it by design. Computer systems are not something that appear at nature and we just harvest.
There are plenty of synchronous architectures for distributed systems, from distributed logical clock ones, for clock domain ones. People tend to avoid async ones anyway.
I usually ask people to explain why they picked or like (or dislike) a particular technology and that surprisingly tells quite a bit about their proficiency level.
At least in interviews I found it tells a lot more about their proficiency than say knowing how to invert binary tree in under 20min or solve a digital circuit diagram in with object oriented principles.
Node.js is a technology that raises red flags when someone advocates it. I've heard stuff like "it's async so faster", "it makes things non-blocking so you get more performance not like with threads", "you just have to learn one language and you're done", "...isomorphic something..." When digging in to discover if they knew how event dispatching works or how these callbacks end up called data comes in on a TCP socket, and there is usually nothing.
The other red flag is the community. Somehow Node.js community managed to accumulate the most immature and childish people. I don't know what it is / was about it. But there it was.
Also maybe I am not the only one, but I've seen vocal advocates of Node.js steam-roll and sell their technology, often convincing managers to adopt, with later disastrous consequences. As article mentions -- callback hell, immature libraries, somehow the promised fast performance guarantees vanish when faced with larger amount of concurrent connections and so on. I've seen that hype happen with Go recently as well. Not as bad, but there is some element.
Now you'd think I am 100% hater and irrational. But one can still convince me that picking Node.js was a good choice. One good thing about Node.js is it is Javascript. If there is a team of developers that just know Javascript and nothing else. Then perhaps it makes sense to have a Node.js project. Keep it small and internal. Also npm does have a lot of packages and they are easy to install. A lot of them are un-maintained and crap but many are fine. Python packaging for example used to be worse, so convincing someone with an "npm install <blah>" wasn't hard.
I've been developing public and internal APIs with node.js full-time for the past 3 years. I can see where you are coming from, but nothing you've said explains why the platform itself is not useful. Most of what you complained about is the caliber of developer and the ecosystem. That reminds me a lot of the complaints about PHP.
The truth is that there are good developers using node.js, there is good code in the ecosystem, and someone that's worked with for a while has learned lessons.
I agree with your performance complaints. On my last project we had to spend considerable time reworking components of our application due to those components blocking the event loop with CPU intensive tasks.
I would say that node.js is probably selected more than anything else for speed of getting a project up and running. It's easy to find JavaScript devs. JavaScript doesn't require a compilation step so iterating and debugging small changes is much faster. There's a ton of pre-build frameworks for serving up APIs even with very little code for CRUD apps.
It's not that there's something you can do with node.js you can't do with other languages. There's just less of a barrier to entry.
There's _way_ more bad Perl in the world than good Perl (Matt's Script Archives anyone?), but these day's it's easy to find well written Perl and appropriate and useful Best Practices for Perl projects of any scale.
There's a _lot_ of bad PHP out there - but Facebook and HipHip clearly show that there are sensible, scalable, and well understood ways to write good PHP code.
Nodejs seems to me to be like the Perl world was in '95 or the PHP world in 2000 or the Python world in 2002 or the Java world pretty much forever ;-) There's not enough examples of "Good Nodejs" yet, and all the Google searches show you are "typical" Nodejs code - which as Sturgeon's law dictates "90% of everything is crap" - so most of the Nodejs code that gets seen or discussed is crap. That will _probably_ change as we "forget" all the worst written Node, and upvote, links too, copy, and deploy well written Node.
There's more similarity to PHP that other languages in my opinion too, in that Node _is_ Javascript, and like PHP, it's a fairly easy route into "development" for an html savvy web designer, which means there's a _much_ larger pool of novice Javascript/Node devs with little or no formal training. You don't need 3yrs of CS degree to dive in and start "scratching your own itches" in Javascript - and in "that part of the industry" it's much easier to leverage "a great looking portfolio but no CS degree" into a job offer than in, say, an enterprise Java or DotNet shop (or a GooFaceAmaUber "don't even respond it they don't have a PhD in another field as well as their CS one" reject-a-thon...)
I was going to say the same thing about PHP. I was at a PHP shop who thought all their problems would be solved with NodeJS. They basically created the same problems in Node. The next place I went to was heavily biased to Erlang and OCaml, but the front-end is done in Node. But the code and architectural quality are like comparing night and day.
>I would say that node.js is probably selected more than anything else for speed of getting a project up and running. It's easy to find JavaScript devs.
Writing JavaScript for the browser and writing it for Node.js are different beasts. It's "easy" to find JavaScript devs because most people have tinkered with jQuery and think that qualifies them. Furthermore, since the Node.js explosion in 2012, lots of posers have been trying to get into this scene.
>JavaScript doesn't require a compilation step so iterating and debugging small changes is much faster.
As another commenter pointed out, this is true, but most projects use a Grunt pipeline or something similar at this point, because they'd feel left out if they didn't. Just more groupthink from the Node.js camp.
This isn't a unique property to Node.js. Pretty much everything that isn't Java, C#, or mega-crusty pre-Perl CGI has it. However, compilation is actually pretty useful; it's not something you necessarily want to throw away. Java and C# devs seem plenty capable of training their Alt+Tab / F5 fingers to get tweaked code running fast.
>There's a ton of pre-build frameworks for serving up APIs even with very little code for CRUD apps.
There may be, but the JavaScript ecosystem changes so quickly and integrates so many esoteric, not-supported-anymore-after-next-Tuesday things that it's offputting. My experience with the code quality of a lot of common libraries has not been great either.
I personally detest the Node.js fad and can't wait for it to die out. I have never been able to find someone who can actually give me a good reason for it to exist. At least when RoR was the fad there was a sensible reason behind it: PHP sucked. I really don't know why Node.js even exists except to push buzzwords and execute a really terrifyingly bad idea of making everything JavaScript.
Even Eich admits that JavaScript was a one-week, publish-or-die project that did many things wrong. It's frankly embarrassing that we still use it as the primary language in our browsers 20 years later. I don't know who looks at that and says "Let's take this to the backend, baby!"
Personal ambition is basically the only reason I can think of for Node.js to exist, both in general and in any specific organization. People see it as a tool to colonize parts of their company. That's the only explanation, because they assuredly don't see any technical benefits in it.
> If there is a team of developers that just know Javascript and nothing else. Then perhaps it makes sense to have a Node.js project. Keep it small and internal.
If you have a team of developers that know just JavaScript and nothing else, you are probably dealing with 100% front-end developers that have little to no understanding of systems programming and what you need to do to be performant and secure outside of a browser.
This leads to a lot of issues with security in the Node.js ecosystem, and is one of the reasons I have a job.
Any developer that only knows one language is carrying around a horrible cognitive bias of how software should be written and they don't even realize it. They are dangerous, keep and eye on them, and for the love of god teach them something new.
I've never really understood the "one langauge" argument. I'm a good (not great) front-end JavaScript programmer, but I don't think that helps writing node.js. Sure, the syntax is technically the same, but the bulk of a node.js program looks and is written quite differently from front-end js (even js library writing). Surely having similar syntax (which is basically C-like anyway) isn't that big a deal?
It's supposed to mean you can share object declarations. But it's javascript so that doesn't mean much. And it's not like the data format in the database and the gui is ever the same by the end of a project anyway, so two formats end up being needed anyway, so it's a bit of a pipe dream.
The one case I've found where node really makes a lot of sense is if you're trying to build a webapp with a native version using the same code. Then node-webkit is the only game in town that doesn't involve a transpiler. Then again, the component parts are flaky enough that maybe the transpiler isn't so bad afterall.
The one language argument, as far as I can tell began when people tried to share code across the server and the front end, it sort of worked for one persons use case but nobody else really had much success with the endevor (at least that I'm aware of.)
That argument is a pretty bad one and is the one most people like to assume people are talking about so they can make fun of it.
The real argument, at least in my mind is that you can specialise and pretty much run your entire stack completely in js with only a bit of json and some html. Build tools, database access, deploy tools, dependency management, server side code, client side code, its all JS.
For me, I don't agree that it matters, I don't even think having a diverse stack takes much longer to learn. But I do understand the fanfare for it.
I'm still surprised no one has created a JavaScript front-end for Erlang/BEAM, like Elixir or LFE. JavaScript is a decent language with a huge community and Erlang/BEAM could solve Node's deficiencies like async-everything APIs and single-threaded execution.
That's because Erlang developers have good taste ;-)
But Elm is actually moving in that direction, the last release added a process abstraction inspired by Elixir, and I've read they're even considering BEAM as a compile target, in addition to the current JS target and plans for a WebAssembly target.
> JavaScript is a decent language with a huge community and Erlang/BEAM could solve Node's deficiencies like async-everything APIs and single-threaded execution.
While BEAM of course has facilities at the VM level that support that, you'd have to extend javascript to take advantage of them on top of building a JS->BEAM compiler.
At which point, the benefit of using JS rather than a language designed to deal better with concurrency and parallelism is dubious.
Maybe because JS is not really a decent language, just the pile of awfulness that we've had to grin and bear it with to do web front-ends for too long, and Erlang or Elixir is nicer to work with to start with.
I've been a software dev for 10 years. When I started I saw the transition from Perl to PHP and a lot of snobbishness from the former towards the latter. Seeing the changing of the guard in web languages was pretty instructional and it's something I see again and again.
I think basic CompSci courses should really have a course or two on managing software projects and handling the problems of what framework do I use to build my new software app? Because fundamental language or framework decisions have both a technical and a business component and even as a front line programmer it helps to be aware of both.
Node.js is a great environment for getting a server side app going fast and it has very good tooling thanks to the rest of the JS community with additions like npm, gulp, bower, express etc. There's obvious benefit in having shareable libraries between client and server side and most importantly (to software companies) hiring coders who can work with it is far, far easier than say finding that rarest of unicorns - an experienced Haskell developer.
If (and it's a damn big if) you outgrow Node.js you're doing well. Then (and only then) look at the alternatives like Play Framework, Spring Boot, Vert.x or whatever else floats your boat.
Rants can be useful in giving a kick up the asses of the relevant community to go address certain bug bears. This rant though is so damn generic it reminds me of those Perl developers at college pouring cold water over the idea of using PHP because they felt threatened by it.
I think basic CompSci courses should really have a
course or two on managing software projects and handling
the problems of what framework do I use to build my new
software app
The problem (if it's a problem, depends on who's asking) is that undergrad CS courses mainly train you to be a CS graduate student (which in turn train you to be a CS academic), but most students choose to major in CS because they want to become professional programmers (aka Software Engineers).
I've done both bachelor and master level CS studies and job-preparation-wise would probably have gotten as much (or more) from a 1-1.5 years (2-3 semesters) vocational training than I did from 8 years of university.
Probably the most apparent perk my studies have gotten me career-wise was being invited to interviews at major tech companies like google and amazon.
I think this actually varies from school to school. My undergraduate university, where the CS program was part of the engineering school, focused on software engineering. My graduate university where the CS program was a part of the college of arts and sciences focused a lot more on theory. But my view here may be skewed, because I only remember one professor who taught most of our theory courses -- I took most of his classes.
From roughly the same number of years in university, (8ish) I can say there were some clear professional benefits from me having done a masters' program. From my advanced software engineering course I was able to talk to my teammates about Gang of Four and better ways go about problems than if-else loops. From my theory courses I was able to identify problems they were seeing, tell them which algorithms would probably be worth looking into etc...
That is all stuff one can learn outside of university, but I learned it while I was there. I get giddy whenever I can try something new and all the writing I did (I wrote a few academic papers) made me really comfortable with writing technical design specs and documentation. When I was in academia I had to document everything anyways so its become second nature to me.
I think 2-3 more years of working would have certainly looked better on a resume, but I think my skill set has added to my team as a whole and I'm glad I did it.
Well I would argue that universities are here to create acidemics, not create job candidates. That vocational work is exactly what you need to be a software engineer. I think the issue is more people don't understand what university level CS is, or that it's NOT a "get a job" pass.
I don't disagree with most of what you say but I can't shake the feeling that for the most common types of web project - nothing touches the productivity possible with frameworks such as Rails or Django.
(I don't know enough about mature PHP MVC frameworks to comment on whether Laravel et al should also be in that list)
Is there anything for node that offers that wide range of functionality and a mature ecosystem for content-driven sites?
> If (and it's a damn big if) you outgrow Node.js you're doing well. Then (and only then) look at the alternatives like Play Framework, Spring Boot, Vert.x or whatever else floats your boat.
I couldn't disagree more. If you like the concept of type safety, don't waste your time.
You called out the fact that you see repeating patterns in the industry, and go on to mention Node.js as a great environment for rapid prototyping. That made me chuckle because that's exactly what I heard about RoR just a few years ago.
Often, people who think that they're "too good" for Node.js are people who had a single bad experience; they designed their Node.js app poorly (E.g. they didn't think through their architecture enough) and then instead of blaming themselves for their ignorance, they put all blame Node.js.
It's always convenient to blame the tool. But I've used a lot of different tools and I can tell you that the tool is almost never the problem.
I've found that people who don't like Node.js are often people who were forced to learn it because their company used it. These people had resentment against Node.js from the very beginning - And they kept this resentment with them while they were 'learning' it - And as a result, they never really learned it properly - They never fully understood it.
Not everyone has to love Node.js but there is no reason to hate it either. It's a damn good tool.
It's always convenient to blame the tool. But I've used a
lot of different tools and I can tell you that the tool
is almost never the problem.
This is both true and also misses the point. Given a sufficiently smart software engineer Javascript is fine. But like the old adage about compilers the joke is that there is no sufficiently smart engineer. Even the best will occasionally make mistakes. Over time those mistakes will accumulate. When enough of them accumulate you experience serious pain.
Any argument that rests on the idea that all you have to do is: "Just hire engineers that can be perfect in perpetuity" is doomed to be a poor one. No language is perfect, however there do exist lanaguages that make it possible to fix problems after the fact with higher levels of confidence and more guarantees. Javascript is not one of those languages.
Not to mention that single threaded callbacks have an inherently lower ceiling on concurrency that multithreaded approaches. Some times you have to make the decision on whether to save thousands-millions of dollars on infrastructure so you can stick with your current codebase or to rewrite and take a medium term hit on velocity instead.
There most definitely is such a thing as outgrowing Node.js.
Anybody can criticize a language or platform, but it doesn't mean much if there aren't any better alternatives.
This article presents an extreme conclusion without much supporting evidence, so it's pretty pathetic that this made the front page. Nobody even uses callbacks anymore now that we have Promise and async/await.
Yes, Javascript isn't the best language (though ES6 improves tremendously on ES5). But right now it's the only language you can use in the browser (aside from languages like Clojurescript that compile to Javascript). The biggest advantage of Node.js is that you can reuse the same code on the client and server, and thus it's ideal for creating universal single-page web apps. Being able to reuse the same code on the client and server is a massive advantage that can't be understated.
Also, Node.js with Nginx is more scalable out of the box than Ruby on Rails, Python/Django, PHP, etc. Hell it's comparable to Java, which is incredible for a dynamic language. The difference is, you can write a Node.js web application 10x faster than the equivalent application in Java, and with a drastically smaller codebase (less code = less code to maintain). These days developer time is the biggest cost.
These rants come off as coming from either (1) back-end developers who never touch UI code or anything on the client-side (not where Node.js thrives) (2) armchair commentators who don't actually have to get shit done in terms of building and deploying web apps on deadlines, and thus have the luxury of criticizing everything without presenting realistic alternatives.
> "There are only two kinds of languages: the ones people complain about and the ones nobody uses." -Bjarne Stroustrup
> he biggest advantage of Node.js is that you can reuse the same code on the client and server, and thus it's ideal for creating universal single-page web apps. Being able to reuse the same code on the client and server is a massive advantage that can't be understated.
This advantage is totally overblown, and in fact I am not sure it even is an advantage. It definitely makes things easier in the short run, but it always comes around to bite you in the ass. The fact is, objects on the server and objects on the client are different things, and while you write less code up-front because the differences aren't always immediately obvious, you end up writing a lot more code later because you didn't think about the very important differences. Representing them as the same thing enables shoddy programmers to not think about the context of where their code will be run.
> "There are only two kinds of languages: the ones people complain about and the ones nobody uses." -Bjarne Stroustrup
"[A] quotation is a handy thing to have about, saving one the trouble of thinking for oneself, always a laborious business." -- A. A. Milne
I think this is right on. They are completely different environments and I can't imagine very many instances where you'll be able to write a generic function you really want on both front and back end. I certainly haven't run into any in real life
I'm guessing you've never written a universal/isomorphic single-page application?
If you try to write one without Node.js, you're going to be writing a lot of the same code twice - once in Javascript to run on the client-side, and twice to run the same exact logic on the server-side (eg. fetching data on the server to pre-render a page and parsing that data, making an AJAX call for the same data on the client and parsing that data in JS).
One codebase is easier to maintain than two codebases in two different languages.
> These rants come off as coming from either (1) back-end developers who never touch UI code or anything on the client-side (not where Node.js thrives) (2) armchair commentators who don't actually have to get shit done
I agree with most of your saying, but the second rant is by Ryan Dahl, creator of Node.js. Often these rants come from people who are very competent and know what they're talking about, but anybody can get burned out by working too much, and the annoyances of flaws can become overwhelming. It's worth remembering that we can always iterate and improve on what we have, even if it's good enough, if nothing else than for the sanity of the people who have to work on cleaning it up later.
> Also, Node.js with Nginx is more scalable out of the box than Ruby on Rails, Python/Django, PHP, etc. Hell it's comparable to Java, which is incredible for a dynamic language.
Citation or link to any benchmarks ?
> The difference is, you can write a Node.js web application 10x faster than the equivalent application in Java, and with a drastically smaller codebase
Building a prototype, maybe; But's it's hard to believe that Node.js give a 10x boost in programmer productivity in the long run. The recent success of typescript is an indication of the limit of dynamic languages for big projects
The number one issue I've found with Node.js is when developers make things overly complicated for no apparent reason. The bar may be too low to get in so possibly you'll get a higher degree of poor design decisions.
The second would be the overuse of build scripts in that the build seems more complicated than the app in both time to get the thing up and complicated chaining steps. I've not had much fun debugging grunt, gulp or webpack in some of these fortune 1000 projects and I have a hard time wanting to give a shit about knowing them in great detail as the app should be the focus.
The parts of node that I most like are the core libraries that come with the install. When I try to stick with those as much as possible rather than using some half-baked npm module for every whim, I have a very pleasurable experience.
The async/wait and promises, etc along with piping streams are quite elegant in how modules can be snapped together like lego pieces but I find that people fuck it up terribly when they half know it as I initially did and it becomes akin to the messiness of es5 callbacks.
It does take some time to really utilize async well so I would recommend to read up on those concepts in great detail prior to jumping in.
Everything in node is just slapped together with no rhyme or reason. npm module quality is piss poor - even the flagship express web server where nothing works out of the box. God help you if you find a bug in a node core library - the typical reaction is that developers now depend on the buggy behavior and as result it won't be fixed. I've returned to sane programming languages after using node for years and I don't miss node's async nonsense where you can't perform a computation without halting all users of your application without performing major programming gymnastics.
That comment reminds me of those informercials where people can't open a carton of milk without pouring it all over themselves so they need some fan dangled gadget for $19.95 to help them do it. Node has many issues but for me its just really easy to use, maybe because I only use it in a minimal sense, similar to lambda scale as you can see on my sebringj github repos.
It would be nice if there were build warnings npm acceptance would give you so you had the chance to clean up things, such as build tests etc and also a recommended standard way to build them. Then if npm developers were lazy in that sense and letting the warnings pass regardless, when you install an npm, it would give you a warning, "this asshole put in a shitty module" or something more PC.
Grunt, Gulp and Webpack are for building front-end assets. They are all built using Node.js, but their complexity has to do with managing complex front-end code base and then optimizing it for the browser.
If they weren't built with Node, then it would be Ruby, or Java, or Python, or even Make.
It is a shitty shitty problem that people are still trying to figure out what the best way is.
I still don't understand why people couldn't have just learned Make, instead of reimplementing it badly two or three times in every different special-snowflake language.
It's telling that none of the posts defending Node.js are talking about it's technical merits. They're all saying:
1. Attacks on people--you're being too negative, you're saying this to feel superior.
2. Choosing Node.js is a tradeoff! We can't really say what you get in exchange for using this crappy ecosystem, but "tradeoff" sounds good even if you're trading using a reasonable ecosystem for nothing.
If you really think Node.js isn't a flaming pile of crap, I challenge you to come up with something it does that isn't done far better in another ecosystem.
I've been working on a project that uses node for the better part of the year. I'm in charge of most of its architecture and design.
After all these months, my conclusion is exactly the same as yours: there's nothing that can be done in done that I can't do better in other platform/ecosystem.
Sure, it's unique (event loop) and has a lot of good things (simplicity), but for anything serious, it's lacking real advantages.
Most of my issues with it comes with javascript itself, and the absence of proper tooling. I have no idea of the advantage of choosing this platform given the level of immaturity.
Oh, for those wondering: Node was chosen by a person caught on the hype (my boss, a friend and mentor who can make mistakes as anyone), who hadn't done any real world programming in it (only hello world stuff).
> Most of my issues with it comes with javascript itself, and the absence of proper tooling.
Does anything in ES2017, FlowType, or TypeScript resolve your issues with Javascript itself? If not, what are the three biggest?
> absence of proper tooling
What do you mean by proper tooling? What are you comparing it to? I think you might find that many of the other modern options also lack tooling in terms of debugging, IDE support, etc.
No, a technical counterargument could be valid if Node had any technical merit.
I'm claiming that no valid technical counterargument exists. If you disagree, it should be very easy for you to present a valid technical counterargument instead of trying to offload the burden of proof onto me.
> I challenge you to come up with something it does that isn't done far better in another ecosystem.
A single, standard event loop that literally every library in the ecosystem uses so you don't have to worry about it.
The other options are compiled languages (Windows message pump, Qt or GTK+ event loop, etc.), which aren't what people seem to want for Node's use case, or things like Twisted. For all of those options other than maybe Windows, you don't get to assume that the entire ecosystem speaks the same event loop protocol. And I don't think any of these options do it far better than Node does. Twisted is pretty good, probably a little better, but Node is just fine too.
Note that I am not a Node.js programmer. I can kind of work with existing apps in Node because I can kind of write JS sometimes (I'm really more of a C person). I just know what other technologies are good at.
> A single, standard event loop that literally every library in the ecosystem uses so you don't have to worry about it.
Using an event loop with all the other threading models available out there is like bringing a knife to a gunfight. I could maybe argue that JavaScript's event loop isn't as good as other event loops, but while actor models exist, I'm not sure why we'd even talk about event loops.
There are some impressive libraries in the Node ecosystem. As an example, passportjs gives you support for a lot of weirdly custom OAuth implementations, Kerberos, etc
Passport JS is horrible. Sure it's easy to use, but it's not secure, which is kind of the entire point.
There are other libraries that are interesting, but that's more a function of necessity than of it being a good thing--people write JavaScript because it's the only thing that runs on the browser, and then people port it onto Node. And it's worth noting that a lot of these have been held back significantly by being on Node. Take Babel for example; it's useful and interesting, but the code is a mess because of callbacks, which makes it hard to modify, and it was taken down by the left-pad fiasco a few months back (which broke thousands and thousands of builds worldwide). Some interesting things have been built on Node, but that's despite Node, not because of Node, and they would be more reliable if they weren't built on Node.
The hyperbole in every criticism of Node must be there for a reason. I think it is more a reflection on the state of mind of the critic rather than the language.
I would posit that many have dedicated huge amounts of time to a new or less-popular language that may not be around in 5 years.
JS is a safe, long-term bet. It's tentacles have made it into every platform and use case out there. Backend, frontend, embedded, mobile, desktop, etc.
I'm curious, what does your vision of the future of web and mobile development look like?
Which language do you wish was in browsers instead of JS?
How would you objectively rank what makes a "good" language and a "crap" language, and how would you test these hypotheses?
> The hyperbole in every criticism of Node must be there for a reason. I think it is more a reflection on the state of mind of the critic rather than the language.
"It's telling that none of the posts defending Node.js are talking about it's technical merits. They're all saying:
1. Attacks on people--you're being too negative, you're saying this to feel superior."
> JS is a safe, long-term bet. It's tentacles have made it into every platform and use case out there. Backend, frontend, embedded, mobile, desktop, etc.
By that argument, Java is a better language than JavaScript.
There are a lot of established languages that aren't going anywhere. And certainly with WebAssembly in the works, I'm not as confident that JS will be popular in two decades as you are.
JS is not a safe long term bet if you want to build reliable systems. There are better languages out there for this purpose. Just because JS is widespread does not mean it is good.
Its always funny to read something like this coming from proponents of Go. Now thats a language that threatens to single-handedly bring us back 30 years in the past. A typed language invented in 2009 lacking generics and algebraic data types, and being proud of it (or at least its users being proud) - thats pretty much at the exact same level as JS "reinventing" callback-based async programming in 2009. And its users were similarly proud of this in 2010-2011.
At least Brendan Eich is modest enough to apologise for JavaScript's flaws and push as much as possible for fixes without backward compatibility breakage of the web.
There is an easy sense of superiority that comes with derision of "X" and the authoritative sounding romanticization of idealized "Y" seemingly adds weight to the argument. Clearly these "Y" people are smarter and better than all those "X" people.
Look at all this horrible code! So sad that all these people are not as smart as me. Look at this horrible language! So sad the people that created it are not as intelligent as me!
Really? There is no more "problem" with Node.js than there is a "problem" with any other platform. There is no more problem with JavaScript/ES-(name your flavor) than there is with any programming language. Different languages are different. Different platforms are different. Of course every system has its own problems. Sometimes people who appreciate them call these "tradeoffs" or the superior types call them idiotic.
The cliche´ of hacker news haters is really really really getting old. So here are some things that are actually good.
As much of a pile of hot steaming code as it is, Babel as an idea (AKA transpiling one language to another) is pretty cool. Of course you can do this in other places but its featuring prominently in the JS community leading to an interesting result. The language and its features become configurable, easy to adapt and change and evolve over time and suit to your liking. This is interesting!
Finally as opposed to what others may have said about the community being childish I have found the opposite. I find it to be very welcoming and supportive, friendly and honestly creative. Of course there are lots of negatives, lots of horrible code, lots of mistakes happening. But what is missed in all of this? Theres A LOT of stuff happening that is good even great! It's beautiful chaos! So go on hating, but I see lots of great stuff out there. As one great systems and iOS developer told me the other day "Have you tried Express? Its awesome!" HA yeah. But he just tried it, and loves it!
No, I think criticism is a good thing. Rants are good. They highlight issues with our current tools and over time that's what leads to progress. Using a plow isn't just different from digging in the dirt with a stick. It's better.
For instance, nulls are clearly a problem. After countless rants we're seeing languages that have better ways to deal with missing or optional values enter the mainstream (Swift, Scala, Rust).
J2EE's original idea to configure a system through separate XML files was heavily criticised for being too bureaucratic. After countless rants we got configuration by convention, better defaults and annotations as part of the source language.
Of course progress is not a straight line and quite often it's not clear what is and isn't progress because there are many trade-offs. But where would we be without a constant process of criticising our tools?
Totally not against solid critique. But it becomes a culture, a signal of intellectual superiority. Which is too easy. You can be, and I have been highly productive in plain ole JavaScript. Same with Java, Same with Ruby, same with Python. Are the language debates irrelevant, absolutely not. But
“Node.js is one of the worst things to happen to the software industry” Hey if it is then I fully submit that I'm just some stupid fool that should rage quit the internet and give up because.... Wait no that would be stupid.
Rants without proposing any alternative solutions can't redeem any use at all. Their only reason to be is to wrongly make their authors feel smart or skilled.
No, J2EE's original idea was to separate wheat from chaff, i.e. good programmers from mediocre ones. Good programmers would then work on containers and mediocre ones would write standardized apps to deploy into those containers, with containers being easily interchangable. In this worldview it's irrelevant whether those standardized apps are developed with lots of XML, or convention over configuration or whatever, because mediocre programmers won't complain.
Now as much as this worldview is flawed, this is just one of the manifestations of an attempt to make software development easier, which is, of course, a noble goal that even Node.js aspires to.
> For instance, nulls are clearly a problem. After countless rants we're seeing languages that have better ways to deal with missing or optional values enter the mainstream (Swift, Scala, Rust).
No they are not. They are a state of an object. What is the problem is a lack of documentation.
One thing I've seen coming up, at least in some code I've looked at, is the @Nullable and I've not seen any complaining about null from those who use this tag.
The idea is to always document when a variable or type can be null. This way you know, with 100% certainty (given a strict type system adherence) that you're not going to erroneously see a null object where it isn't expected.
From this, you have a huge benefit: a performant error state or a better method of representing an issue without throwing an exception.
Riddle me this, how would a Map be implemented if Null was stripped from Java? Should it throw an exception if a key is not found? If that is the case, then you should always need to check for this exception, otherwise you're prone to bugs. You've also increased the execution time of your code by in some a huge performance hit [0] that is more cumbersome.
This is why I think rants are a big problem. They don't describe the opposition to a statement accurately. They are one person's opinions and after reading them, if it all sounds nice, you're usually willing to take it all as fact. "Hey, they're writing an article! They must be smarter then me. I'll take their word for it." As a result, our communities don't really evolve we just spew memes back and fourth dictating what someone else thought.
What really matters, in every sense, is reading a discussion had between two experts in opposing camps. That way you can see both sides of the arguments, their weak points, and make an educated choice on what side to agree with.
That being said, what do you think of my points? Can you address them? My oppinion is that NPEs can be easily avoided by using very simple measures AND null more closely resembles most problems while being more performant and less cumbersome then throwing exceptions willy nilly. I'd love to hear how you feel about this.
Also...
> J2EE's original idea to configure a system through separate XML files was heavily criticised for being too bureaucratic. After countless rants we got configuration by convention, better defaults and annotations as part of the source language.
I'd say that is a good example of discussion, brain storming, and coming up with a much more accurate representation of the ideas we are trying to convey (the use of annotations to configure methods).
No, I think the real issue here is people taking languages personally, when there really shouldn't be any reason to. It's very interesting that people form such a personal bond with programming languages, to the point where they will refuse to see how they could be perceived as bad, or cumbersome.
Pretty much everyone does it, myself included, and it's one of the more problematic things when it comes to actually discussing languages. People just won't accept that their "baby" (or pet, as was mentioned below) might just be bad.
While elitism isn't great, not all criticism and ranting is done from a "We're/I'm better than you" perspective and most rants aren't directed at people. The people using language X might feel they are, but that's simply their own insecurity showing. I think a good portion of defensive posts show this fairly clearly.
In the end, is it really that surprising that some of the newer ideas are better than old ideas? Why is it only "fair" to say that a later version of X is better than an earlier version of X, but not that Y can be better than X?
> It's very interesting that people form such a personal bond with programming languages, to the point where they will refuse to see how they could be perceived as bad, or cumbersome.
From what I've seen and personally experienced, this happens most often when someone is only strong in that one language. If you've only ever used that language to build applications and someone comes along telling you it sucks, it feels like a personal attack because it would mean you've been doing things wrong or your code/applications suck. It's part of an identity. As people learn new languages and actually gain professional experience with them, that original language is no longer what defines them as a programmer so they no longer need to defend its legitimacy.
Great post; completely agree. I feel a lot of us associate "things" like programming languages with our identity. So if our identity is attacked, we take it personal.
Generally you'd think new ideas would win out on old ideas. But you hope the new at least learns from the old. No need to repeat mistakes. This is the criticism I usually see from BSDs towards Linux or programmers towards Golang. Not that I disagree/agree, just an observation.
> Really? There is no more "problem" with Node.js than there is a "problem" with any other platform.
While I agree in principle, I've been bitten by some incredibly bad node packaging architectural decisions. Just yesterday I was attempting to install tfx-cli in order to CICD our build scripts to our build server - it went as you would expect with npm: MAX_PATH issues. The git clean then failed on all subsequent builds. A problem became unsolvable by virtue of the platform that was used to write it - had tfx-cli been written in almost anything else, this would not have been a problem (I say "almost" because I'm egregiously assuming that at least one other package manager is also broken in this way).
So there's an example of node-specific problem and there doesn't seem to be any impetus surrounding solving it.
> Oh but look at that callback YUCK! Cmon
In defense of Node, it now supports ES6: callback hell is self-imposed for all new code.
> So there's an example of node-specific problem and there doesn't seem to be any impetus surrounding solving it.
??? There's so little impetus around solving it that both sides(npm and Microsoft) have put in significant effort to do it - npm 3 reduced the chance of having a very large path, and preview builds of the .net file api are removing the MAX_PATH limit.
> There is an easy sense of superiority that comes with derision of "X"
There's also an easy sense of superiority which comes from being actually superior.
> There is no more "problem" with Node.js than there is a "problem" with any other platform.
Well, yes, there are problems with Node.js, to include the concurrency model and the language — as indicated in the article.
> Different languages are different. Different platforms are different. Of course every system has its own problems.
Yes, every system has its own problems, but some systems have fewer or better problems than others, while others have more or worse.
There are continua of languages, platforms & OSes. JavaScript-the-language is a bad language. It's not as bad as INTERCAL, but it's worse than C or Lisp. Node.js-the-platform is a bad platform. It's probably not as bad as the Java platform, but it's worse than Go. POSIX is a bad OS. Not nearly as bad as Windows or Mac OS Classic, but worse than Plan 9.
One has a very limited number of hours in one's life. Why waste those hours in pursuit of anything other than excellence? Node.js isn't excellent; why spend any time on it?
I agree with the general sentiment, maybe not with all the choices of what's better or worse. And this is where the trouble starts: What is opinion and what are facts?
I also agree with your conclusion that avoiding wasting precious lifetime on shi^Wnon-excellence is undesirable. However, given the first problem I mentioned, one can easily waste just as much time trying to figure out what the excellent things to choose are.
There are certainly languages that are better than others.
Some of them excel in a particular domain, others do well in other domains. But in general you can tell if a programming language pushes you to write good code or bad code.
Not every tool is the right tool for the job, and finding the right tool for the job is part of doing the job correctly.
The thing that always bugged me about node is the argument that using the same tools on the server as in the browser is somehow an advantage. That idea is not a given.
> The thing that always bugged me about node is the argument that using the same tools on the server as in the browser is somehow an advantage. That idea is not a given.
Not a given, sure. But there's an argument to be made there, beyond the scope of mere programming, as a web-focused company can benefit from having a dev team that is versed on both frontend and backend.
Interesting things aren't necessarily good. JavaScript is very difficult to read and maintain, there are dozens of dialects and versions, npm is a complete mess, it's hopelessly single threaded, and we've arguably hit a performance wall optimizing JavaScript in the general case. It is a necessary evil.
If you're going to address the "haters", actually address real complaints rather than make very vague comments about beauty and forum culture.
The cliche´ of hacker news haters is really really really getting old.
I don't think your rebuttal of a criticism as exhibiting a hater culture is any less cliche.
Until the disgustingly contrived artificial hype, and regular poorly-founded misguided hype are all derided too, the haters persist to balance the equation.
Or, to rephrase my point in a business-friendly way: garnter_hype_cycle.jpg
No, like my cat likes to snuggle but sheds and my dog likes to play but poops too much. Either way they both deserve a bowl of water and some food at the end of the day. You can get all pedantic and say cats are better or dogs are better but guess what. I don't care, I just like my pets.
Node is very similar to continuation passing style which is used primarily as an intermediate representation in compilers. It's typically considered poor for production code because it's very error prone.
>Really? There is no more "problem" with Node.js than there is a "problem" with any other platform.
Javascript's type system really doesn't stand up well to scrutiny. That's more crucial than you'd think - the type system is the foundation the rest of the language is built upon.
I suspect this is partly why javascript cycles through technologies so often. The foundations are shaky.
It's dynamic typing with a prototype-based object model. This type system tends to be very good at iterating quickly and testing out many alternative approaches to solving a problem without committing to a data representation up-front. It tends to be pretty bad at communicating invariants to other programmers or nailing down APIs for other subsystems to build off.
Small wonder, then, that the Node.js ecosystem tends to very good at iterating quickly and testing out many alternative approaches to solving a problem, and pretty bad at communicating invariants to other programmers or providing a stable API for other programs to build off. You tend to get what you ask for.
I really don't think that people have been coming up with new frameworks because you can add [1] to 5 and get nonsense.
Javascript cycles through technologies quickly because web developers cycle through technologies quickly, and they do that because they get to work on greenfield projects with minimal expectation for them to be around in 5 years a lot. Check out all the different server side frameworks and infrastructure components that have been in vogue with web developers over the last decade.
In comparison, people who aren't writing consumer-facing websites and webapps tend to have much more clearly-defined goals, longer lifespans of their apps, and don't often get the choice to use anything but industry standard technology. Because of the slower cycle, there's less opportunity to redesign frameworks and core technologies even when it's allowed.
Creativity is awesome. But when someone asks you to deliver something, spending all day wading through "beautiful chaos" might not be the best approach. In that case boring is better than interesting, IMHO. The more attention can be devoted to the problem at hand, the higher the chance to get the solution right.
> Look at all this horrible code! So sad that all these people are not as smart as me. Look at this horrible language! So sad the people that created it are not as intelligent as me!
It's not about being smarter; smart people are a dime a dozen. It's about the fact that what happens in the industry happens to all of us. When a large chunk of the industry adopts callbacks as their threading model, that means I have to either work with their horrible threading models, or not take those jobs.
Don't psychoanalyze people who you disagree with; that's just an ad hominem attack.
> Really? There is no more "problem" with Node.js than there is a "problem" with any other platform. There is no more problem with JavaScript/ES-(name your flavor) than there is with any programming language. Different languages are different. Different platforms are different. Of course every system has its own problems. Sometimes people who appreciate them call these "tradeoffs" or the superior types call them idiotic.
This is the favored defense of people whose favored languages/tools are under attack. But this is absolutely not a tradeoff. A tradeoff is when you make a choice that has some downsides, but you get something for it.
With JavaScript in the browser, the tradeoff is clear--you use this shitty awful language and you get to run your code on the browser, because that's pretty much the only reasonable way to run your code in the browser right now. There are alternatives (TypeScript, CoffeeScript) but then you're limited to a smaller community with fewer resources.
But with JavaScript on the server, there's no tradeoff. You use this shitty awful language and you get... a shitty awful language. You don't get a reasonable threading model, you don't get a reasonable type system. You don't get anything you couldn't get from another language.
It's not a tradeoff, it's just a bad choice.
> As much of a pile of hot steaming code as it is, Babel as an idea (AKA transpiling one language to another) is pretty cool. Of course you can do this in other places but its featuring prominently in the JS community leading to an interesting result. The language and its features become configurable, easy to adapt and change and evolve over time and suit to your liking. This is interesting!
Interesting, yes, and useful. But there's really no reason this had to be written in JavaScript, and the code would likely be a lot less of a "pile of hot steaming code" if it were written in a more reasonable ecosystem.
> Of course there are lots of negatives, lots of horrible code, lots of mistakes happening.
The problem isn't that there is bad code, it's that there isn't any good code. If you needed to write a server-side program and you chose JavaScript, your code is bad and you should feel bad. It's literally impossible to write good server code in JavaScript because it doesn't provide adequate types or threading primitives. It would be different if there weren't alternatives (like in the browser), but there are alternatives which are better.
Your argument proves too much, i. e. that all languages / runtimes / frameworks are of equal quality and usefulness and all differences come down to individual taste – and that's obviously absurd.
There are a lot of things to like about Node.js, but the primary thing that bothers me about it is the obsession with async. It's a language/framework designed to make pretty much everything async.
In the real world, almost everything is synchronous, and only occasionally do you really want async behavior. By that I mean, you almost always want A, B, C, D, E, F, G in that order, and only occasionally would you say that you want async(H,I). But with Node, it's the other way around. You assume things are async, and then specify synchronous behavior.
Instead of assuming A; B; C; D; E; F; G;, you wind up with a ton of code like A.then(B).then(C).then(D).then(E).then(F).then(G);
...I know that's a contrived example, and I know you don't really need to do it that way, but so many people do, and it really illustrates the point. In Node.js, you are explicitly synchronous / implicitly async. Most other coding paradigms (including Go) better match what I consider reality, which is that everything is implicitly synchronous, and you specify async behavior when you need it.
Basically, I think it's backward. But perhaps like the OP, I just can't wrap my head around it.
The NPM stuff... well, I think all ecosystems have their pros and cons. I'm not a huge fan of NPM, but it does the job for the most part, and I'm curious as to how people would actually improve it, rather than just complain about it all the time. I don't really have any good ideas (knowing nothing about how package management actually works under the hood).
I learned this as I dove into low-level hardware work - drivers and embedded systems. When you're dealing with hardware, life is fundamentally driven by interrupts. Processors spend most of their time asleep. When something happens, the signal level of an input pin changes, and the processor reacts: it wakes up, it starts executing the interrupt handler for that signal, it does things, it sends messages, and then - its job done - it goes back to sleep.
So what, we're talking about normal computers here, you say, surely they're different, right? But no, they really aren't; it's the same process of signal, interrupt, handler, return, all the time. Every context switch, every packet receipt or transmit completion, every timer tick, all of it, it's all driven asynchronously; the system is a hierarchy of event handlers waiting for their opportunity to respond to some external signal.
If you're trying to build an IO system, then, the synchronous model is an expensive illusion. You can force it, but it isn't reality. You spend time waiting, you spend time blocking, you build complex APIs that pretend to poll arrays of connections in parallel; and all you're doing is forcing that illusion of synchrony onto a fundamentally asynchronous world.
It's backwards, all right, but it's backwards because our traditional way of describing computation is backwards, especially the languages which follow the imperative tradition. Functional languages have gained popularity in part because the mental model works better for asynchrony; instead of describing a series of steps to follow, and treating the interrupt as the exception, you describe the processes that should be undertaken in certain circumstances; you effectively construct a state-machine instead of describing a process, and then you leave that state-machine to be poked and prodded by whatever external signals come along to drive it.
EDIT: Wikipedia link: https://en.m.wikipedia.org/wiki/Asynchronous_circuit
What you are saying is that they react to interrupts asynchronously, but this has nothing to do with the synchronous nature of a CPU. Moreover I think that if you have a CPU that doesn't respect the barriers and reorders the instructions to execute them in parallel, then it's a bugged CPU.
What difference does it make if from the processor's perspective I have:
```js somepoeration().then(function() { // do stuff }); ```
or
```js somepoeration(); // do stuff ```
In both cases my program will just wait for 'somepoeration' to complete before doing anything useful.
The programmer would like to think of the series of operations involved on a per-connection basis. Whereas from the point of view of the processor a "connection" is not a thing; there is only a stream of incoming message fragments at occasional intervals from the network and I/O subsystem. It can request data from persistent storage by what is effectively snail mail: send a message and wait a few million cycles for it to come back.
So the software must consist of a set of state machines. We can push those up into the operating system and call them threads, or down into the user's code and call them callbacks and continuations. Each approach has advantages and disadvantages, and it's important to understand what they are.
(Possibly the language which does this best is Erlang, although it's not as easy to get started with as node.js. Theorists really overlook the vital metric of "time to hello world" in language learning.)
I could never understand how this works better as a mental model. Say you ask somebody to buy you a gadget in a store they don't know. What do you do tell them:
a) "drive in your car on this street, turn left on Prune street, turn right on Elm street, the store will be after the second light. Go there, find "Gadgets" isle, on the second shelf in the middle there would be a green gadget saying "Magnificent Gadget", buy it and bring it home"
or:
b) when you find yourself at home, go to car. When you find yourself in the car, if you have a gadget, drive home, otherwise if you're on Elm street, drive in direction of Prune Street. If you're in the crossing of Elm street and Prune street, turn to Prune street if you have a gadget but to Elm street if you don't. When you are on Prune street, count the lights. When the light count reaches two, if you're on Prune street, then stop and exit the vehicle. If you're outside the vehicle and on Prune street and have no gadget, locate store and enter it, otherwise enter the vehicle. If you're in the store and have no gadget then start counting shelves, otherwise proceed to checkout. Etc. etc. - I can't even finish it!
I don't see how "steps to follow" is not the most natural mental model for humans to achieve things - we're using it every day! We sometimes do go event-driven - like, if you're driving and somebody calls, you may perform event-driven routine "answer the phone and talk to your wife" or "ignore the call and remember to call back when you arrive", etc. But again, most of these routines will be series of steps, only triggered by an event.
For some time it resulted in callback hell (http://callbackhell.com/). This was addressed through Promises and more recently Observables, Generators, and async/await.
With transpiling, adoption of these asynchronous concepts has been possible quicker than would have been achieved otherwise.
I would suggest revisiting some of the modern JS constructs to support better use of the underlying cpu architecture.
I will however admit, getting my head around Promises took about a month before it really clicked how powerful this approach can be to support non-blocking IO.
From an Ops perspective, I disagree. At a systems level, everything already is async, whether you like it or not. So then the options are either to embrace the admittedly hectic nature of async, or pretent that things are synchronous and impose those constraints to maintain a logical purity from a programmer's standpoint.
For example, in a synchronous setup, it's perfectly logical to service an incoming http request with a few database queries followed by some more logic and some more DB queries or API calls; wrap all the result and send it back. The same in node.js forces the code author to think about those initial DB queries (which may run simultaneously), about how to roll up the result when they are all done and pass it to callbacks.
But while thinking about those callback interactions, there's a motivation to reduce the number of steps, or to factor them out into their own module or microservice. This promotes thinking at a systems level instead of just at the level of "1 request serviced by 1 handler". Perhaps that big request can be broken up into several API calls and the web page can load those asynchronously. Perhaps the smaller microservices end up building up less memory usage per request (or spread it out in a way that makes clustering more efficient).
One may see async as a hurdle in the way of logical abstraction, or as a way to align thinking with the realities of network programming.
Logical purity is important. It helps people reason about their code better, which in turn makes them more productive.
The reason we have frameworks and high-level languages is so that we can take advantage of abstractions and not worry about the underlying complexities of the system. If a framework is making you "embrace the hectic nature" of those complexities, why use that framework in the first place? Might as well use a language like C, which actually is a low-level language designed for developing hyper-optimized software. Node.js, on the other hand, is supposed to be a Web (read: high-level) framework. That's why implicit-async doesn't fit its nature, and in large codebases you see nested callbacks all the way down.
Node came of age about a decade after epoll was introduced, when not having access to nonblocking IO was considered a big liability for a couple of dominant web programming languages, and they built their concurrency model around the semantics of epoll.
However, there are languages like Haskell, Erlang, and Go that IMO did the right thing by building a synchronous programming model for concurrency and offering preemptable lightweight processes to avoid the overhead associated with OS thread per connection concurrency models. These languages offer concurrent semantics to programmers, yet still are able to use nonblocking IO underneath the covers by parking processes waiting on I/O events. It's not the right tradeoff for every language, particularly I think lower level languages like Rust are better off not inheriting all the extra baggage of a runtime like this, but for higher level languages I think its probably the most convenient model to programmers.
They certainly make sense from an implementation perspective (I've got this interpreter and I want to do concurrency... I know, I'll just use it to trampoline callbacks).
But callbacks are too low level to reason about easily to some extent, it's kind of like writing everything as a series of goto statements. Sure you can do it, but good luck following it.
The advantages of asynchronous execution are lost. This isn't limited to nodejs, I've experienced similar problems with Python twisted...
Channels (such as in clojure/core.async) or lightweight processes and messaging (such as in erlang/beam), are much more enjoyable to work with.
This shouldn't really be a shock, however, they're higher level abstractions based on real computer science research by people who really thought about the problem.
Promises (which were pure JS before they were ever added to the language) are a more-composable structure for reasoning about concurrency. I don't think I've put more than one line of callback-style code into production per month.
I agree with everything you say, and I want to add to it. People compare node to things like Go, PHP, Python, etc, which is a mistake. As someone who had to write a fairly complex native module for node that a lot of people still use (5000 downloads this month, according to npm), nodeJS is a framework not a language.
Node is not in control of the underlying code that implements the ES spec. Node is a bunch of C/C++ middleware wrapping V8 and implementing various server and process utilities. That is all it is.
Why is this important? Because it biases the community towards piling on feature after feature and moving at an insane pace, sometimes in ways that are outside of the community's control. Node is biased against stability, because they feel the need to keep up with V8. Counter this with Go, which has been frozen as a language for 8 releases now, with each release fine tuning the stability of the compiler/language, runtime, and libs.
That native module that I mentioned earlier, I have a bunch of open requests to make it work with nodeJS 6 (no idea what this is), which is the fourth breaking V8 interface change I've had to deal with. The native module clocks in at >2000 lines of C++. I implemented the same library in Go and it clocks in at 500 lines. I've only had to modify the Go version of this library once, and it was to take advantage of a new feature (i.e. I didn't have to update it).
Node's continual quest for features is going to keep it in the niche web development space it has come to dominate. There is simply no way I would architect a system with such an unstable platform.
But there is one great reason why node's async first mentality is superior- when you want to do async in node, you don't have to worry that some library you are using is going to lock up your thread.
In any other language, you have to painstaikingly make sure everything you use isn't doing sync, or try and monkeypatch all io operations (python has something like that).
Frankly, for me, when I'm dealing with webservers I find myself needing to use async quite a lot.
No; for example, in Go, every time the code does IO, the scheduler will re-assign another goroutine, and they asynchronously get back to the other when the IO finishes. It doesn't need any special support by the library.
I'm currently working on some lighting (DMX-based theatre lighting) software and the ability to have my 'effects' written in a sequential manner by using 'yield' is actually incredible. It's simplified my code a huge amount and made it a lot easier to reason about.
There is a function called every frame, which then calls each effect. Since each effect is a Python generator function, it can be written sequentially and just has to yield whenever the frame is computed.
> In any other language, you have to painstaikingly make sure everything you use isn't doing sync
I haven't found that to be the case TBH. In most other languages i've used i don't have to worry about foo() being syncronous or not. I can assume that. In other words, after the control returns from the foo() call, i can assume that what foo() is supposed to do has been completed. In the case that foo() needs to do some IO operation, i can usually rely on it doing a syscall that will block the thread and put it to sleep, instead of doing something nefarious like busy waiting.
And that's it. The current thread will sleep while the IO operation in foo() completes, and the OS (or VM if we're talking green threads) will switch the CPU to do something else in the main time. And if it's a web server we're talking about, that something else may be another web server thread, serving a different request. Yay, preemptive multitasking!
In the case of node, you do very much have to worry about a function call being synchronous or asynchronous, as it defines the way it has to be called. In the former case, it can be called "normally":
In the async case, some code gymnastics need to be involved: It may not seem a big problem at first, especially if using the `await` syntax is an option. But to me the biggest problem of this technique is that the "async-ness" of a function cannot be abstracted away. If a function `foo` that has always been synchronous, and that is used in several places, needs to be changed, and this change implies `foo` calling an async function `bar`, then `foo` will need to become async too, and all the places where `foo` is called will need to be changed. Even though this internal `bar` async call was an implementation detail of `foo` that shouldn't have concerned `foo` callers. And this propagates all the way up: if any of the places where `foo` was called was, in turn, a synchronous function too, then that function will also need to be refactored into an async one, and so on and so forth.And that's basically why i find node's distinction between sync and async functions so frustrating :(
PS: Cooperative multitasking does not imply this weird syntax complexity of having two different kinds of function calls that cannot be freely intermixed. For instance, Eralng "processes" use cooperative multitasking, but the "yields" are managed implicitly by the VM, so you don't need to explicitly yield control of the current thread to let other stuff run concurrently.
We do this daily in real life. It's more efficient for me to have 10 minutes down time at work while I'm waiting for someone else than it is for me to start something else then either get interrupted or finish another task before I respond to the person I'm waiting on.
I work on services where any given endpoint may handle many thousands of requests per second. We don't care about a slight penalty to query the database, because these are very short, and our services return responses on the order of 100 ms.
Maybe these are things you care about in a language like Javascript, but in something statically typed, they just don't seem like a problem.
This isn't intrinsically bad. It just makes Node.js biased towards use cases that benefit from asynchronous I/O, which is a perfectly valid design choice. Sensible programmers already know that computationally intensive tasks that benefit from actual parallelism are best served by a different tool.
What's absolutely horrible about Node.js is how they make everything asynchronous: explicit continuation passing everywhere. In other words, the user is responsible for manually multiplexing multiple logical threads through a single-OS-threaded event loop. If it sounds low level, it's because it is low-level. A high-level programming language ought to to better than this.
Other languages get this right: Erlang, Go, Clojure, Scala, Haskell, Rust, etc.
2) All I/O (if you're doing it right) is asynchronous.
Node does things the right way around; it's just a shame that Linux (unlike Windows) doesn't have async I/O baked into the heart of the OS and well supported with useful kernel primitives.
Synchronous I/O by default means you're wasting your CPU cores. When running at Web scale, you ALWAYS want to leverage async I/O in order to not have the CPU idle blocking on some I/O operation, and you don't necessarily want the synchronization and memory overhead of multiple threads. That's why Node is designed the way it is.
By using the 'co' library and generators you can write async code almost as if it were sync. It works like the inline-callbacks feature of Twisted.
But that's the thing: synchronous code doesn't mean synchronous execution. The language/platform should be able to make it async for you, without forcing you to re-shape your code. And it's not a rare system: even plain old threads work like that - in fact, they're often a good solution: https://www.mailinator.com/tymaPaulMultithreaded.pdf
What about epoll()?
> Synchronous I/O by default means you're wasting your CPU cores.
You can implement synchronous IO on top of an async backend using the above mentioned epoll. I believe that is how read() and similar syscalls are implemented. They block the programs thread, but the core is free to run other threads while it waits for IO.
When a process becomes blocked (like due to synchronously waiting for IO to complete), the kernel will context switch away to another runnable process. The process that becomes blocked essentially goes to sleep, and then "wakes up" and becomes runnable again once the operation completes. The CPU is free during this time to run other processes.
If you need to do multiple different of these things concurrently, then you can run multiple processes. Writing a single process with async code won't make that process faster. To do more things at the same time you can run multiple processes. Context switching between different processes is what the kernel scheduler is designed to do, and it does so very efficiently. There isn't much overhead per thread. If I recall correctly, Linux kernel stacks per thread are 8 kilobytes (with efforts under way to reduce that further [4] - also discussed in [1]), and the user stack space is something the application can control and tune. The memory use per thread needn't be much.
Using all available cores to perform useful work is the most important thing to achieve in high-throughput code, and both async and sync can achieve it. Async doesn't become necessary for high performance unless you're considering very high performance which is beyond the reach of NodeJS anyway [2]. Asynchronous techniques win on top performance benchmarks, but typical multithreaded blocking synchronous Java can still handily beat async NodeJS, since Java will use all available CPU cores while Node's performance is blocked on one (unless you use special limited techniques). There's some good discussion about this in the article and thread about "Zero-cost futures in Rust" [1]. The article includes a benchmark which compares Java, Go, and NodeJS performance. These benchmarks suggest that the other tested platforms provide 10-20x better throughput than Node (they're also asynchronous, so this benchmark isn't about sync/async).
Folks might also be interested in the TechEmpower web framework benchmark [2]. The top Java entry ("rapidoid") is #2 on the benchmark and achieves 99.9% of the performance of the #1 entry (ulib in C++). These frameworks both achieve about 6.9 million requests per second. The top Java Netty server (widely deployed async/NIO server) is about 50% of that, while the Java Jetty server, which is regular synchronous socket code, clocks in at 10% of the best or 710,000 R/s. NodeJS manages 320,000 R/s which is 4.6% of the best. In other words, performance-focused async Java achieves 20x, regular asynchronous Java achieves 10x, and boring old synchronous Jetty is still 2x better than NodeJS throughput. NodeJS does a pretty good job given that it's interpreted while Java is compiled, though Lua with an Nginx frontend can manage about 4x more.
I agree that asynchronous execution can provide an advantage, but it's not the only factor to consider while evaluating performance. If throughput is someone's goal, then NodeJS is not the best platform due to its concurrency and interpreter handicap. If you value performance then you'll chose another platform that offers magnitude better requests-per-second throughput, such as C++, Java, Rust, or Go according to [1] and [2]. Asynchronous execution also does not necessarily require asynchronous programming. Other languages have good or better async support -- for example, see C#'s `await` keyword. [3] explores async in JavaScript, await in C#, as well as Go, and makes the case that Go handles async most elegantly of those options. Java has Quasar, which allows you to write regular code that runs as fibers [5]. The code is completely normal blocking code, but the Quasar runtime handles running it asynchronously with high concurrency and M:N threading. Plus these fibers can interoperate with regular threads. Pretty gnarly stuff (but requires bytecode tampering). If async is your preference over Quasar's sync, then Akka might be up your alley instead [6].
> By using the 'co' library and generators you can write async code almost as if it were sync.
For an interesting and humorous take on the difficulties of NodeJS's approach to async, and where that breaks down in the author's opinion, see "What color is your function?" [3].
[1] https://news.ycombinator.com/item?id=12268988
[2] https://www.techempower.com/benchmarks/#section=data-r11&hw=...
[3] http://journal.stuffwithstuff.com/2015/02/01/what-color-is-y...
[4] https://lwn.net/Articles/692208/
[5] http://docs.paralleluniverse.co/quasar/ [6] http://akka.io/
Not true. Synchronous I/O is higher-throughput. If you need to do a large amount of high-latency I/O (e.g. web requests) then asynchronous can end up being lower-overhead overall, but making everything async is just as bad as making everything sync.
1) `npm install` on two different machines at the same point in time was guaranteed to install the same set of dependency versions.
1b) shrinkwrap was standard.
2) it was faster.
3) you could run a private repo off a static file server.
Basically, it was lightweight threads which would yield execution to other threads whenever waiting for I/O, or explicitly yielding. It allowed for a very straightforward linear programming style, no callback hell.
Coupled with a functional programming style, there was rarely a need for mutexes or other synchronization between threads.
When a thread yielded, the current continuation was captured and resumed when the thread was ready to continue. At the bottom of it all was a simple event loop where the Scheme interpreter dispatched I/O and timeouts. Scheme happens to have support for continuations built in to the language, so the implementation was actually quite simple.
The interpreter is single-threaded but there is simple concurrency between the "processes", that is a process yields when it has nothing more to immediately do.
Old online demo build through Emscripten (it misses many new features such as functions): https://rawgit.com/ambrop72/badvpn-googlecode-export/wiki/em...
Docs currently still dying on Google Code archive: https://code.google.com/archive/p/badvpn/wikis/NCD.wiki
Source: https://github.com/ambrop72/badvpn
How is this different from a normal multithreaded C program on a POSIX kernel?
When I call read(), my thread is suspended until the data comes back, and another thread can run.
It's like the worst of two worlds.
I work an a real-time system that is controlled by RPC from a desktop. We claim we want to keep the real-time code small and simple.
Yet I find people pushing complexity down onto the RT system even when the real-time guarantees aren't needed because the PC code responds to it using a callback driven async model. It's easier to put a non-real time for loop into the RT thread than to emulate a for loop on the PC.
I love being able to use one syntax to write 95% of my code(instead of having to switch between JS / python / php etc.) but some of NodeJS's programming and ecosystem realities are annoying to say the least.
People have tried to retcon node's choices as being based on some sort of deep technical justification, but that's not the case: its limits are the limits of the material it's made of, not a specific design choice.
If you look at more advanced VMs like the CLR or JVM then they support all modes of operation: synchronous/blocking (which is often what you want), synchronous/blocking with many threads, async with callbacks, async with continuations ... node only supports one. How is that better?
BUT
Just because you want to do ABCDE does not mean it's feasible.
You can access a file, read it, compute something, depending on that access another file, read it, write something via Http Post somewhere else.
BUT the nature of i/o is such that the problem is 'intrinsic' - it's not a 'node.js' problem so much.
Of course you know that you can do everything syncrhonously if you want, but then you get hangups and what not waiting for underlying resources, networking calls etc..
The nature of a distributed system is inherently async, and so we have to live with it.
That said - maybe you have some examples of where async definitely should not be used?
The other huge problem with Node.js is the vast, crazy expansion of tiny libraries, supported by ghosts, that change all the time. A simple lib can depend on a dozen others, with varying versions, everything changes almost daily. A small change somewhere can really jam things up.
This is by far my #1 concern, although it can kind of be avoided if you're careful.
Every single language is able to do ABCDE, except for JavaScript. Yes, I guess that means it's impossible.
> The nature of a distributed system is inherently async, and so we have to live with it.
The nature of a distributed system is the one you impose onto it by design. Computer systems are not something that appear at nature and we just harvest.
There are plenty of synchronous architectures for distributed systems, from distributed logical clock ones, for clock domain ones. People tend to avoid async ones anyway.
At least in interviews I found it tells a lot more about their proficiency than say knowing how to invert binary tree in under 20min or solve a digital circuit diagram in with object oriented principles.
Node.js is a technology that raises red flags when someone advocates it. I've heard stuff like "it's async so faster", "it makes things non-blocking so you get more performance not like with threads", "you just have to learn one language and you're done", "...isomorphic something..." When digging in to discover if they knew how event dispatching works or how these callbacks end up called data comes in on a TCP socket, and there is usually nothing.
The other red flag is the community. Somehow Node.js community managed to accumulate the most immature and childish people. I don't know what it is / was about it. But there it was.
Also maybe I am not the only one, but I've seen vocal advocates of Node.js steam-roll and sell their technology, often convincing managers to adopt, with later disastrous consequences. As article mentions -- callback hell, immature libraries, somehow the promised fast performance guarantees vanish when faced with larger amount of concurrent connections and so on. I've seen that hype happen with Go recently as well. Not as bad, but there is some element.
Now you'd think I am 100% hater and irrational. But one can still convince me that picking Node.js was a good choice. One good thing about Node.js is it is Javascript. If there is a team of developers that just know Javascript and nothing else. Then perhaps it makes sense to have a Node.js project. Keep it small and internal. Also npm does have a lot of packages and they are easy to install. A lot of them are un-maintained and crap but many are fine. Python packaging for example used to be worse, so convincing someone with an "npm install <blah>" wasn't hard.
The truth is that there are good developers using node.js, there is good code in the ecosystem, and someone that's worked with for a while has learned lessons.
I agree with your performance complaints. On my last project we had to spend considerable time reworking components of our application due to those components blocking the event loop with CPU intensive tasks.
I would say that node.js is probably selected more than anything else for speed of getting a project up and running. It's easy to find JavaScript devs. JavaScript doesn't require a compilation step so iterating and debugging small changes is much faster. There's a ton of pre-build frameworks for serving up APIs even with very little code for CRUD apps.
It's not that there's something you can do with node.js you can't do with other languages. There's just less of a barrier to entry.
There's _way_ more bad Perl in the world than good Perl (Matt's Script Archives anyone?), but these day's it's easy to find well written Perl and appropriate and useful Best Practices for Perl projects of any scale.
There's a _lot_ of bad PHP out there - but Facebook and HipHip clearly show that there are sensible, scalable, and well understood ways to write good PHP code.
Nodejs seems to me to be like the Perl world was in '95 or the PHP world in 2000 or the Python world in 2002 or the Java world pretty much forever ;-) There's not enough examples of "Good Nodejs" yet, and all the Google searches show you are "typical" Nodejs code - which as Sturgeon's law dictates "90% of everything is crap" - so most of the Nodejs code that gets seen or discussed is crap. That will _probably_ change as we "forget" all the worst written Node, and upvote, links too, copy, and deploy well written Node.
There's more similarity to PHP that other languages in my opinion too, in that Node _is_ Javascript, and like PHP, it's a fairly easy route into "development" for an html savvy web designer, which means there's a _much_ larger pool of novice Javascript/Node devs with little or no formal training. You don't need 3yrs of CS degree to dive in and start "scratching your own itches" in Javascript - and in "that part of the industry" it's much easier to leverage "a great looking portfolio but no CS degree" into a job offer than in, say, an enterprise Java or DotNet shop (or a GooFaceAmaUber "don't even respond it they don't have a PhD in another field as well as their CS one" reject-a-thon...)
Writing JavaScript for the browser and writing it for Node.js are different beasts. It's "easy" to find JavaScript devs because most people have tinkered with jQuery and think that qualifies them. Furthermore, since the Node.js explosion in 2012, lots of posers have been trying to get into this scene.
>JavaScript doesn't require a compilation step so iterating and debugging small changes is much faster.
As another commenter pointed out, this is true, but most projects use a Grunt pipeline or something similar at this point, because they'd feel left out if they didn't. Just more groupthink from the Node.js camp.
This isn't a unique property to Node.js. Pretty much everything that isn't Java, C#, or mega-crusty pre-Perl CGI has it. However, compilation is actually pretty useful; it's not something you necessarily want to throw away. Java and C# devs seem plenty capable of training their Alt+Tab / F5 fingers to get tweaked code running fast.
>There's a ton of pre-build frameworks for serving up APIs even with very little code for CRUD apps.
There may be, but the JavaScript ecosystem changes so quickly and integrates so many esoteric, not-supported-anymore-after-next-Tuesday things that it's offputting. My experience with the code quality of a lot of common libraries has not been great either.
I personally detest the Node.js fad and can't wait for it to die out. I have never been able to find someone who can actually give me a good reason for it to exist. At least when RoR was the fad there was a sensible reason behind it: PHP sucked. I really don't know why Node.js even exists except to push buzzwords and execute a really terrifyingly bad idea of making everything JavaScript.
Even Eich admits that JavaScript was a one-week, publish-or-die project that did many things wrong. It's frankly embarrassing that we still use it as the primary language in our browsers 20 years later. I don't know who looks at that and says "Let's take this to the backend, baby!"
Personal ambition is basically the only reason I can think of for Node.js to exist, both in general and in any specific organization. People see it as a tool to colonize parts of their company. That's the only explanation, because they assuredly don't see any technical benefits in it.
If you have a team of developers that know just JavaScript and nothing else, you are probably dealing with 100% front-end developers that have little to no understanding of systems programming and what you need to do to be performant and secure outside of a browser.
This leads to a lot of issues with security in the Node.js ecosystem, and is one of the reasons I have a job.
The one case I've found where node really makes a lot of sense is if you're trying to build a webapp with a native version using the same code. Then node-webkit is the only game in town that doesn't involve a transpiler. Then again, the component parts are flaky enough that maybe the transpiler isn't so bad afterall.
That argument is a pretty bad one and is the one most people like to assume people are talking about so they can make fun of it.
The real argument, at least in my mind is that you can specialise and pretty much run your entire stack completely in js with only a bit of json and some html. Build tools, database access, deploy tools, dependency management, server side code, client side code, its all JS.
For me, I don't agree that it matters, I don't even think having a diverse stack takes much longer to learn. But I do understand the fanfare for it.
The problem is assuming language proficiency is the defining characteristic of a developer. We really need to disabuse management of that idea.
I'd rather teach a good 'microservice developer' javascript than teach a good 'javascript programmer' what makes a good microservice.
Being able to do front-end and back-end in js is really nice, especially passing data back and forth in js-object like format.
If you have not tried it, do so, I feel it does streamline things nicely.
Node.js is a little lighter than most things and if you use it correctly I think it has advantages.
I wouldn't build a bank on it however :)
But Elm is actually moving in that direction, the last release added a process abstraction inspired by Elixir, and I've read they're even considering BEAM as a compile target, in addition to the current JS target and plans for a WebAssembly target.
While BEAM of course has facilities at the VM level that support that, you'd have to extend javascript to take advantage of them on top of building a JS->BEAM compiler.
At which point, the benefit of using JS rather than a language designed to deal better with concurrency and parallelism is dubious.
Deleted Comment
I think basic CompSci courses should really have a course or two on managing software projects and handling the problems of what framework do I use to build my new software app? Because fundamental language or framework decisions have both a technical and a business component and even as a front line programmer it helps to be aware of both.
Node.js is a great environment for getting a server side app going fast and it has very good tooling thanks to the rest of the JS community with additions like npm, gulp, bower, express etc. There's obvious benefit in having shareable libraries between client and server side and most importantly (to software companies) hiring coders who can work with it is far, far easier than say finding that rarest of unicorns - an experienced Haskell developer.
If (and it's a damn big if) you outgrow Node.js you're doing well. Then (and only then) look at the alternatives like Play Framework, Spring Boot, Vert.x or whatever else floats your boat.
Rants can be useful in giving a kick up the asses of the relevant community to go address certain bug bears. This rant though is so damn generic it reminds me of those Perl developers at college pouring cold water over the idea of using PHP because they felt threatened by it.
I've done both bachelor and master level CS studies and job-preparation-wise would probably have gotten as much (or more) from a 1-1.5 years (2-3 semesters) vocational training than I did from 8 years of university.
Probably the most apparent perk my studies have gotten me career-wise was being invited to interviews at major tech companies like google and amazon.
From roughly the same number of years in university, (8ish) I can say there were some clear professional benefits from me having done a masters' program. From my advanced software engineering course I was able to talk to my teammates about Gang of Four and better ways go about problems than if-else loops. From my theory courses I was able to identify problems they were seeing, tell them which algorithms would probably be worth looking into etc...
That is all stuff one can learn outside of university, but I learned it while I was there. I get giddy whenever I can try something new and all the writing I did (I wrote a few academic papers) made me really comfortable with writing technical design specs and documentation. When I was in academia I had to document everything anyways so its become second nature to me.
I think 2-3 more years of working would have certainly looked better on a resume, but I think my skill set has added to my team as a whole and I'm glad I did it.
Then why not major in Software Engineering? Is that not an option in American universities?
(I don't know enough about mature PHP MVC frameworks to comment on whether Laravel et al should also be in that list)
Is there anything for node that offers that wide range of functionality and a mature ecosystem for content-driven sites?
I couldn't disagree more. If you like the concept of type safety, don't waste your time.
Often, people who think that they're "too good" for Node.js are people who had a single bad experience; they designed their Node.js app poorly (E.g. they didn't think through their architecture enough) and then instead of blaming themselves for their ignorance, they put all blame Node.js.
It's always convenient to blame the tool. But I've used a lot of different tools and I can tell you that the tool is almost never the problem.
I've found that people who don't like Node.js are often people who were forced to learn it because their company used it. These people had resentment against Node.js from the very beginning - And they kept this resentment with them while they were 'learning' it - And as a result, they never really learned it properly - They never fully understood it.
Not everyone has to love Node.js but there is no reason to hate it either. It's a damn good tool.
Any argument that rests on the idea that all you have to do is: "Just hire engineers that can be perfect in perpetuity" is doomed to be a poor one. No language is perfect, however there do exist lanaguages that make it possible to fix problems after the fact with higher levels of confidence and more guarantees. Javascript is not one of those languages.
Not to mention that single threaded callbacks have an inherently lower ceiling on concurrency that multithreaded approaches. Some times you have to make the decision on whether to save thousands-millions of dollars on infrastructure so you can stick with your current codebase or to rewrite and take a medium term hit on velocity instead.
There most definitely is such a thing as outgrowing Node.js.
This article presents an extreme conclusion without much supporting evidence, so it's pretty pathetic that this made the front page. Nobody even uses callbacks anymore now that we have Promise and async/await.
Yes, Javascript isn't the best language (though ES6 improves tremendously on ES5). But right now it's the only language you can use in the browser (aside from languages like Clojurescript that compile to Javascript). The biggest advantage of Node.js is that you can reuse the same code on the client and server, and thus it's ideal for creating universal single-page web apps. Being able to reuse the same code on the client and server is a massive advantage that can't be understated.
Also, Node.js with Nginx is more scalable out of the box than Ruby on Rails, Python/Django, PHP, etc. Hell it's comparable to Java, which is incredible for a dynamic language. The difference is, you can write a Node.js web application 10x faster than the equivalent application in Java, and with a drastically smaller codebase (less code = less code to maintain). These days developer time is the biggest cost.
These rants come off as coming from either (1) back-end developers who never touch UI code or anything on the client-side (not where Node.js thrives) (2) armchair commentators who don't actually have to get shit done in terms of building and deploying web apps on deadlines, and thus have the luxury of criticizing everything without presenting realistic alternatives.
> "There are only two kinds of languages: the ones people complain about and the ones nobody uses." -Bjarne Stroustrup
This advantage is totally overblown, and in fact I am not sure it even is an advantage. It definitely makes things easier in the short run, but it always comes around to bite you in the ass. The fact is, objects on the server and objects on the client are different things, and while you write less code up-front because the differences aren't always immediately obvious, you end up writing a lot more code later because you didn't think about the very important differences. Representing them as the same thing enables shoddy programmers to not think about the context of where their code will be run.
> "There are only two kinds of languages: the ones people complain about and the ones nobody uses." -Bjarne Stroustrup
"[A] quotation is a handy thing to have about, saving one the trouble of thinking for oneself, always a laborious business." -- A. A. Milne
If you try to write one without Node.js, you're going to be writing a lot of the same code twice - once in Javascript to run on the client-side, and twice to run the same exact logic on the server-side (eg. fetching data on the server to pre-render a page and parsing that data, making an AJAX call for the same data on the client and parsing that data in JS).
One codebase is easier to maintain than two codebases in two different languages.
Half this post is from the creator of Node.js
Sadly this is not true (yet)
Then every one just kinda woke up. It's not even a debate.
Citation or link to any benchmarks ?
> The difference is, you can write a Node.js web application 10x faster than the equivalent application in Java, and with a drastically smaller codebase
Building a prototype, maybe; But's it's hard to believe that Node.js give a 10x boost in programmer productivity in the long run. The recent success of typescript is an indication of the limit of dynamic languages for big projects
The second would be the overuse of build scripts in that the build seems more complicated than the app in both time to get the thing up and complicated chaining steps. I've not had much fun debugging grunt, gulp or webpack in some of these fortune 1000 projects and I have a hard time wanting to give a shit about knowing them in great detail as the app should be the focus.
The parts of node that I most like are the core libraries that come with the install. When I try to stick with those as much as possible rather than using some half-baked npm module for every whim, I have a very pleasurable experience.
The async/wait and promises, etc along with piping streams are quite elegant in how modules can be snapped together like lego pieces but I find that people fuck it up terribly when they half know it as I initially did and it becomes akin to the messiness of es5 callbacks.
It does take some time to really utilize async well so I would recommend to read up on those concepts in great detail prior to jumping in.
Please npm responsibly.
If they weren't built with Node, then it would be Ruby, or Java, or Python, or even Make.
It is a shitty shitty problem that people are still trying to figure out what the best way is.
Maven has faults too. But it would be a mistake to ignore its strengths just because it is ugly.
1. Attacks on people--you're being too negative, you're saying this to feel superior.
2. Choosing Node.js is a tradeoff! We can't really say what you get in exchange for using this crappy ecosystem, but "tradeoff" sounds good even if you're trading using a reasonable ecosystem for nothing.
If you really think Node.js isn't a flaming pile of crap, I challenge you to come up with something it does that isn't done far better in another ecosystem.
After all these months, my conclusion is exactly the same as yours: there's nothing that can be done in done that I can't do better in other platform/ecosystem.
Sure, it's unique (event loop) and has a lot of good things (simplicity), but for anything serious, it's lacking real advantages.
Most of my issues with it comes with javascript itself, and the absence of proper tooling. I have no idea of the advantage of choosing this platform given the level of immaturity.
Oh, for those wondering: Node was chosen by a person caught on the hype (my boss, a friend and mentor who can make mistakes as anyone), who hadn't done any real world programming in it (only hello world stuff).
Does anything in ES2017, FlowType, or TypeScript resolve your issues with Javascript itself? If not, what are the three biggest?
> absence of proper tooling
What do you mean by proper tooling? What are you comparing it to? I think you might find that many of the other modern options also lack tooling in terms of debugging, IDE support, etc.
I'm claiming that no valid technical counterargument exists. If you disagree, it should be very easy for you to present a valid technical counterargument instead of trying to offload the burden of proof onto me.
A single, standard event loop that literally every library in the ecosystem uses so you don't have to worry about it.
The other options are compiled languages (Windows message pump, Qt or GTK+ event loop, etc.), which aren't what people seem to want for Node's use case, or things like Twisted. For all of those options other than maybe Windows, you don't get to assume that the entire ecosystem speaks the same event loop protocol. And I don't think any of these options do it far better than Node does. Twisted is pretty good, probably a little better, but Node is just fine too.
Note that I am not a Node.js programmer. I can kind of work with existing apps in Node because I can kind of write JS sometimes (I'm really more of a C person). I just know what other technologies are good at.
Tcl? Erlang/OTP?
Using an event loop with all the other threading models available out there is like bringing a knife to a gunfight. I could maybe argue that JavaScript's event loop isn't as good as other event loops, but while actor models exist, I'm not sure why we'd even talk about event loops.
There are other libraries that are interesting, but that's more a function of necessity than of it being a good thing--people write JavaScript because it's the only thing that runs on the browser, and then people port it onto Node. And it's worth noting that a lot of these have been held back significantly by being on Node. Take Babel for example; it's useful and interesting, but the code is a mess because of callbacks, which makes it hard to modify, and it was taken down by the left-pad fiasco a few months back (which broke thousands and thousands of builds worldwide). Some interesting things have been built on Node, but that's despite Node, not because of Node, and they would be more reliable if they weren't built on Node.
The hyperbole in every criticism of Node must be there for a reason. I think it is more a reflection on the state of mind of the critic rather than the language.
I would posit that many have dedicated huge amounts of time to a new or less-popular language that may not be around in 5 years.
JS is a safe, long-term bet. It's tentacles have made it into every platform and use case out there. Backend, frontend, embedded, mobile, desktop, etc.
I'm curious, what does your vision of the future of web and mobile development look like?
Which language do you wish was in browsers instead of JS?
How would you objectively rank what makes a "good" language and a "crap" language, and how would you test these hypotheses?
"It's telling that none of the posts defending Node.js are talking about it's technical merits. They're all saying:
1. Attacks on people--you're being too negative, you're saying this to feel superior."
> JS is a safe, long-term bet. It's tentacles have made it into every platform and use case out there. Backend, frontend, embedded, mobile, desktop, etc.
By that argument, Java is a better language than JavaScript.
There are a lot of established languages that aren't going anywhere. And certainly with WebAssembly in the works, I'm not as confident that JS will be popular in two decades as you are.
Its always funny to read something like this coming from proponents of Go. Now thats a language that threatens to single-handedly bring us back 30 years in the past. A typed language invented in 2009 lacking generics and algebraic data types, and being proud of it (or at least its users being proud) - thats pretty much at the exact same level as JS "reinventing" callback-based async programming in 2009. And its users were similarly proud of this in 2010-2011.
At least Brendan Eich is modest enough to apologise for JavaScript's flaws and push as much as possible for fixes without backward compatibility breakage of the web.
Look at all this horrible code! So sad that all these people are not as smart as me. Look at this horrible language! So sad the people that created it are not as intelligent as me!
Really? There is no more "problem" with Node.js than there is a "problem" with any other platform. There is no more problem with JavaScript/ES-(name your flavor) than there is with any programming language. Different languages are different. Different platforms are different. Of course every system has its own problems. Sometimes people who appreciate them call these "tradeoffs" or the superior types call them idiotic.
The cliche´ of hacker news haters is really really really getting old. So here are some things that are actually good.
As much of a pile of hot steaming code as it is, Babel as an idea (AKA transpiling one language to another) is pretty cool. Of course you can do this in other places but its featuring prominently in the JS community leading to an interesting result. The language and its features become configurable, easy to adapt and change and evolve over time and suit to your liking. This is interesting!
Finally as opposed to what others may have said about the community being childish I have found the opposite. I find it to be very welcoming and supportive, friendly and honestly creative. Of course there are lots of negatives, lots of horrible code, lots of mistakes happening. But what is missed in all of this? Theres A LOT of stuff happening that is good even great! It's beautiful chaos! So go on hating, but I see lots of great stuff out there. As one great systems and iOS developer told me the other day "Have you tried Express? Its awesome!" HA yeah. But he just tried it, and loves it!
Oh but look at that callback YUCK! Cmon
For instance, nulls are clearly a problem. After countless rants we're seeing languages that have better ways to deal with missing or optional values enter the mainstream (Swift, Scala, Rust).
J2EE's original idea to configure a system through separate XML files was heavily criticised for being too bureaucratic. After countless rants we got configuration by convention, better defaults and annotations as part of the source language.
Of course progress is not a straight line and quite often it's not clear what is and isn't progress because there are many trade-offs. But where would we be without a constant process of criticising our tools?
Rants get me down. It really is possible to be constructively critical. This author failed.
Now as much as this worldview is flawed, this is just one of the manifestations of an attempt to make software development easier, which is, of course, a noble goal that even Node.js aspires to.
No they are not. They are a state of an object. What is the problem is a lack of documentation.
One thing I've seen coming up, at least in some code I've looked at, is the @Nullable and I've not seen any complaining about null from those who use this tag.
The idea is to always document when a variable or type can be null. This way you know, with 100% certainty (given a strict type system adherence) that you're not going to erroneously see a null object where it isn't expected.
From this, you have a huge benefit: a performant error state or a better method of representing an issue without throwing an exception.
Riddle me this, how would a Map be implemented if Null was stripped from Java? Should it throw an exception if a key is not found? If that is the case, then you should always need to check for this exception, otherwise you're prone to bugs. You've also increased the execution time of your code by in some a huge performance hit [0] that is more cumbersome.
This is why I think rants are a big problem. They don't describe the opposition to a statement accurately. They are one person's opinions and after reading them, if it all sounds nice, you're usually willing to take it all as fact. "Hey, they're writing an article! They must be smarter then me. I'll take their word for it." As a result, our communities don't really evolve we just spew memes back and fourth dictating what someone else thought.
What really matters, in every sense, is reading a discussion had between two experts in opposing camps. That way you can see both sides of the arguments, their weak points, and make an educated choice on what side to agree with.
That being said, what do you think of my points? Can you address them? My oppinion is that NPEs can be easily avoided by using very simple measures AND null more closely resembles most problems while being more performant and less cumbersome then throwing exceptions willy nilly. I'd love to hear how you feel about this.
Also...
> J2EE's original idea to configure a system through separate XML files was heavily criticised for being too bureaucratic. After countless rants we got configuration by convention, better defaults and annotations as part of the source language.
I'd say that is a good example of discussion, brain storming, and coming up with a much more accurate representation of the ideas we are trying to convey (the use of annotations to configure methods).
[0] - http://stackoverflow.com/a/299315
Pretty much everyone does it, myself included, and it's one of the more problematic things when it comes to actually discussing languages. People just won't accept that their "baby" (or pet, as was mentioned below) might just be bad.
While elitism isn't great, not all criticism and ranting is done from a "We're/I'm better than you" perspective and most rants aren't directed at people. The people using language X might feel they are, but that's simply their own insecurity showing. I think a good portion of defensive posts show this fairly clearly.
In the end, is it really that surprising that some of the newer ideas are better than old ideas? Why is it only "fair" to say that a later version of X is better than an earlier version of X, but not that Y can be better than X?
From what I've seen and personally experienced, this happens most often when someone is only strong in that one language. If you've only ever used that language to build applications and someone comes along telling you it sucks, it feels like a personal attack because it would mean you've been doing things wrong or your code/applications suck. It's part of an identity. As people learn new languages and actually gain professional experience with them, that original language is no longer what defines them as a programmer so they no longer need to defend its legitimacy.
Generally you'd think new ideas would win out on old ideas. But you hope the new at least learns from the old. No need to repeat mistakes. This is the criticism I usually see from BSDs towards Linux or programmers towards Golang. Not that I disagree/agree, just an observation.
While I agree in principle, I've been bitten by some incredibly bad node packaging architectural decisions. Just yesterday I was attempting to install tfx-cli in order to CICD our build scripts to our build server - it went as you would expect with npm: MAX_PATH issues. The git clean then failed on all subsequent builds. A problem became unsolvable by virtue of the platform that was used to write it - had tfx-cli been written in almost anything else, this would not have been a problem (I say "almost" because I'm egregiously assuming that at least one other package manager is also broken in this way).
So there's an example of node-specific problem and there doesn't seem to be any impetus surrounding solving it.
> Oh but look at that callback YUCK! Cmon
In defense of Node, it now supports ES6: callback hell is self-imposed for all new code.
??? There's so little impetus around solving it that both sides(npm and Microsoft) have put in significant effort to do it - npm 3 reduced the chance of having a very large path, and preview builds of the .net file api are removing the MAX_PATH limit.
https://github.com/Microsoft/nodejstools/issues/69
https://github.com/Microsoft/nodejs-guidelines/blob/master/w...
There's also an easy sense of superiority which comes from being actually superior.
> There is no more "problem" with Node.js than there is a "problem" with any other platform.
Well, yes, there are problems with Node.js, to include the concurrency model and the language — as indicated in the article.
> Different languages are different. Different platforms are different. Of course every system has its own problems.
Yes, every system has its own problems, but some systems have fewer or better problems than others, while others have more or worse.
There are continua of languages, platforms & OSes. JavaScript-the-language is a bad language. It's not as bad as INTERCAL, but it's worse than C or Lisp. Node.js-the-platform is a bad platform. It's probably not as bad as the Java platform, but it's worse than Go. POSIX is a bad OS. Not nearly as bad as Windows or Mac OS Classic, but worse than Plan 9.
One has a very limited number of hours in one's life. Why waste those hours in pursuit of anything other than excellence? Node.js isn't excellent; why spend any time on it?
I also agree with your conclusion that avoiding wasting precious lifetime on shi^Wnon-excellence is undesirable. However, given the first problem I mentioned, one can easily waste just as much time trying to figure out what the excellent things to choose are.
The thing that always bugged me about node is the argument that using the same tools on the server as in the browser is somehow an advantage. That idea is not a given.
Not a given, sure. But there's an argument to be made there, beyond the scope of mere programming, as a web-focused company can benefit from having a dev team that is versed on both frontend and backend.
There are not only two jobs: frontend and backend. Saying X language is good/bad for backend..
If you're going to address the "haters", actually address real complaints rather than make very vague comments about beauty and forum culture.
Liking any particular language doesn't make anyone a bad or stupid person though. Nor does preferring another make you better or smarter.
Deleted Comment
I don't think your rebuttal of a criticism as exhibiting a hater culture is any less cliche.
Until the disgustingly contrived artificial hype, and regular poorly-founded misguided hype are all derided too, the haters persist to balance the equation.
Or, to rephrase my point in a business-friendly way: garnter_hype_cycle.jpg
As a sidenote, Babel didn't invent compilers (yes, that is what it is called) or DSLs.
Javascript's type system really doesn't stand up well to scrutiny. That's more crucial than you'd think - the type system is the foundation the rest of the language is built upon.
I suspect this is partly why javascript cycles through technologies so often. The foundations are shaky.
Small wonder, then, that the Node.js ecosystem tends to very good at iterating quickly and testing out many alternative approaches to solving a problem, and pretty bad at communicating invariants to other programmers or providing a stable API for other programs to build off. You tend to get what you ask for.
Javascript cycles through technologies quickly because web developers cycle through technologies quickly, and they do that because they get to work on greenfield projects with minimal expectation for them to be around in 5 years a lot. Check out all the different server side frameworks and infrastructure components that have been in vogue with web developers over the last decade.
In comparison, people who aren't writing consumer-facing websites and webapps tend to have much more clearly-defined goals, longer lifespans of their apps, and don't often get the choice to use anything but industry standard technology. Because of the slower cycle, there's less opportunity to redesign frameworks and core technologies even when it's allowed.
It's not about being smarter; smart people are a dime a dozen. It's about the fact that what happens in the industry happens to all of us. When a large chunk of the industry adopts callbacks as their threading model, that means I have to either work with their horrible threading models, or not take those jobs.
Don't psychoanalyze people who you disagree with; that's just an ad hominem attack.
> Really? There is no more "problem" with Node.js than there is a "problem" with any other platform. There is no more problem with JavaScript/ES-(name your flavor) than there is with any programming language. Different languages are different. Different platforms are different. Of course every system has its own problems. Sometimes people who appreciate them call these "tradeoffs" or the superior types call them idiotic.
This is the favored defense of people whose favored languages/tools are under attack. But this is absolutely not a tradeoff. A tradeoff is when you make a choice that has some downsides, but you get something for it.
With JavaScript in the browser, the tradeoff is clear--you use this shitty awful language and you get to run your code on the browser, because that's pretty much the only reasonable way to run your code in the browser right now. There are alternatives (TypeScript, CoffeeScript) but then you're limited to a smaller community with fewer resources.
But with JavaScript on the server, there's no tradeoff. You use this shitty awful language and you get... a shitty awful language. You don't get a reasonable threading model, you don't get a reasonable type system. You don't get anything you couldn't get from another language.
It's not a tradeoff, it's just a bad choice.
> As much of a pile of hot steaming code as it is, Babel as an idea (AKA transpiling one language to another) is pretty cool. Of course you can do this in other places but its featuring prominently in the JS community leading to an interesting result. The language and its features become configurable, easy to adapt and change and evolve over time and suit to your liking. This is interesting!
Interesting, yes, and useful. But there's really no reason this had to be written in JavaScript, and the code would likely be a lot less of a "pile of hot steaming code" if it were written in a more reasonable ecosystem.
> Of course there are lots of negatives, lots of horrible code, lots of mistakes happening.
The problem isn't that there is bad code, it's that there isn't any good code. If you needed to write a server-side program and you chose JavaScript, your code is bad and you should feel bad. It's literally impossible to write good server code in JavaScript because it doesn't provide adequate types or threading primitives. It would be different if there weren't alternatives (like in the browser), but there are alternatives which are better.