Why We Use Node

[Christopher Alexander's][ca] writings inspired the idea of [software design patterns][dp]. He also wrote about [Turkish Carpets][turk].

In our last post, we explored the reasons behind some of our technology choices. In this post, we’re going to zero in on Node. There’s been a lot of different takes on what makes Node appealing. Some developers seem to believe that Node more or less invented fast, async programming. But if you want speed, you’d use Java or C/C++, and async has been around for years. In fact, Node was originally built on top of an existing C async library. So, if it isn’t speed or async programming…what is it? Why do we use Node? Why should you use Node?

For Want of an API…

We’ve already discussed the appeal of JavaScript (and, by extension, CoffeeScript), not least of which is that so many developers already are familiar with it. It’s a language you pretty much have to know anyway if you’re doing anything with the Web, so a server-side JavaScript platform has intrinsic appeal. But Node also offers—and I think this is one of those things that isn’t talked about much—a great API for network programming.

Particular in combination with JavaScript, it’s pretty hard to beat, at least for particular kinds of problems. Consider this one line echo server:

((require "net").createServer ((socket) -> socket.pipe socket)).listen 1337

What’s impressive about this is that it uses general purpose network programming abstractions, like servers and streams.A one-line echo server, by itself can be done using netcat as nc -lk 1337, but the network programming model we have access to with netcat is relatively limited. More interesting applications can thus be written in two or three lines of code, and applications that typically require dozens or even hundreds of lines of code can often be written in a dozen lines or less.

Lines of code is a crude measure of expressiveness. We talked a bit about this in our last post with respect to CoffeeScript. But it also applies to well-designed APIs that provide an intuitive programming model, like the Node APIs. Expressive code is generally easier to debug and maintain, assuming you’ve learned the language, libraries, and idioms in use.A lot of the griping about any piece of software is at least partly due to discomfort caused by a lack of familiarity. All other things being equal, if it’s easier to write expressive code, we all win.

Concurrency Model

Node gets a lot of attention for it’s async programming model, which is, ironically, not a reason to use Node. Or, at least, it’s not a reason we use it. Node actually provides very limited support for concurrent programming. It has one construct, the callback, and that’s it. This makes it hard to write certain kinds of multitasking applications efficiently and it unnecessarily complicates even the simplest applications. However, this isn’t really Node’s fault. That’s JavaScript’s concurrency model, and to depart from it, Joyent (the company that develops Node) would have to effectively fork the execution model of the language itself. You could do that, but, given that one of the major benefits of using Node is that JavaScript is already familiar to so many developers, it was a reasonable decision not to do so.

Performance

Node also gets a lot of attention for being fast. Performance is a relative thing, with a lot of different dimensions. But on language benchmarks, Node isn’t a big winner. For example, V8 JavaScript is as much as five times slower than Java on these benchmarks and as much as 11 times slower than C++. You can make an argument that Node is faster than Ruby or Python—and the V8 developers (the folks who wrote the JavaScript engine used by Node) continue to improve its performance. But the bottom line is that raw speed, by itself, isn’t really a reason to use Node.

The Bottom Line

That said, effective use of concurrency is crucial for performance in networked applications. After all, I/O is often more important to such applications than CPU or memory. In spite of it’s limited concurrency model, it’s APIs make it relatively easy to handle high-levels of concurrency. Our simple echo server above can handle thousands of concurrent connections, limited mostly by the configuration and memory of the server itself.We often tell clients that, sure, Node is slower than, say, Java, but it’s just as fast at scale, because network I/O is usually their bottleneck at that point. If you can support more concurrency in an I/O bound applciation, you’ll utilize more CPU and memory and your costs go down.

I think this is implicit in what people are getting when they talk about Node being fast. What they’re saying is they need fewer servers or less expensive servers. And that’s the bottom line. Of course, we can always drop into C or Java, and if I really want to, we can write servers that are much faster than Node. In fact, some people have done just that. But Node is “good enough” and it’s much simpler. We can still go back and optimize selected components if we need to, where we find bottlenecks, but there’s no sense in paying for that ahead of time.

The End of the Language Wars

At any rate, when you put this altogether, what it boils down to is that Node offers a simple way to write concurrent, networked programs, using JavaScript. That’s the reason we use Node. And, again, since JavaScript is the lingua franca of the Web browser, most Web programmers already know it. We don’t have to worry about whether a client is a Ruby shop, a Python shop, a Java shop—because they’re all JavaScript shops, too. What’s more, when we deliver a solution, they don’t have to worry about that either. They can hire any decent Web developer to be on the team taking ownership of it. In a tight labor market, that’s a big deal.

A Hot Mess

What I’m not saying here is that Node is better than Ruby or Python or Java. All these languages have enough talented developers out there that they will all steal the best advances from each other anyway. They all have viable concurrency models (in some cases, via add-on libraries), they’re all getting faster with each release, and so on.As I said in the prequel to this post, though, we still probably would have stuck with Ruby—or even had a dalliance with Lua or Factor—except for CoffeeScript, which neutralized a big negative—JavaScript is a hot mess of a language—and maybe even turned it into a positive. Node might be better in one area, but Ruby is better in another, and Python is better in a third—and Java will be usually be faster than any of them. But I think Node’s APIs are generally as good or better than those in other languages—and it’s JavaScript. For better or worse, and more or less completely incidentally, that’s the language of the Web. And that’s Node’s big advantage—marrying JavaScript, and it’s concurrency model (such as it is) to rock solid APIs.