Something just doesn’t feel right about node.js.
After coding in it for almost a year, it’s been fun, but I’ve decided it’s just a waypoint to somewhere better.
But I can write programs for the network really easily!
There is no doubt that node.js got some things right. Some while ago I wrote a very high-performance Syslog Collector that could process and parse 250,000 log messages a second. It could do this with thousands of active simultaneous TCP connections. It also supported UDP, HTTP, and SSL. It was written in C++ and used the Boost ASIO framework. It almost never crashed, and didn’t leak memory. But it took 5 months to write (and then re-write), test, and deploy. So learning how to write similar programs in node was a real breakthrough when it came to speed-of-development.
Need a HTTP server? require the http module. Ditto the net module to bring up a socket server. And node is an event-driven framework, promising performance superior to the old way of a thread-per-connection model. node.js became popular in a way Twisted never seemed to.
What’s wrong with node.js?
Or how about this, using the node.js REPL?
>  + 
Why doesn’t 1 empty array plus another empty array equal an empty array? It does in Python.
>  +  * 5
Everywhere I look node.js developers are debugging programs in a really primitive manner, and constantly express frustration with the tools available to them. Tracking down why exceptions are thrown in callbacks, and in unit test frameworks like Mocha and Chai, is often a nightmare. Hours are spent trying to work out why programs are failing. Liberal use of console.log() seems to be the only tool most developers even use, when it comes to debugging issues.
Most programmers, within 36-hours of first programming in node, discover callback hell. Entire websites have been dedicated to explaining how to avoid this problem. It’s deeply ironic that the asynchronous, event-driven nature of node, which gives it so much performance advantage, results in such unsightly and shaky code.
But we’ve got Promises, you say! I agree. If it wasn’t for modules like Bluebird I think most node programmers would have given up by now. I really like working with Promises — it introduces an aesthetic back into the code. Chaining a sequence of Promises is a subtle way to program a computer and a consciousness-raising exercise. In fact Promises are so much fun that it makes you forget that they only exist to fix the really, really bad problem of callback hell — a problem which, I suspect, seriously threatened the viability of writing serious programs in node.
I’ll take one core please, but just one
node.js always runs in a single thread. This makes it much easier to deal with data that is shared across functions and modules. It doesn’t mean there are no race conditions, but it does mean you can be sure that your code won’t be interrupted while modifying an object. But once your node program maxes out a single CPU core, where can you go? You can’t make use of all the extra processing power that exists on multi-core machines. You’re stuck. Sure, there is Cluster Node, but it’s experimental and significant chunks of your program may need a re-write to use it. And your overall program design may never have considered that you may need more than one process on the same machine to cope with the load.
Where to go next?
Writing high-performance programs for the network and Distributed Systems has now become a extremely important element of contemporary programming. And frameworks which minimize context switching — like node does — allow us to build high-performance servers. But better alternatives to node.js exist.
An obvious candidate is Go. It comes with a robust standard library, built for implementing programs for the network, so makes coding servers as easy as node. Goroutines allow the programmer to forget about callbacks and code in the way you, the programmer, naturally think — in a linear manner. And underneath, Goroutines are really lightweight so the cost of the context switch is much less than an OS thread — and it was the OS thread context switch that node.js was built to avoid.
Go can also take advantage of multiple cores without too much trouble. It’s built into the language. I’ve written a couple of significant programs in Go, and it’s been such a better experience than writing node code.
Don’t take my word for it