1. Node.js Cures Cancer

    I apologize for the title. But it’s no less accurate than this embarassing, poorly-reasoned article by Ted Dziuba. It’s flamebait, but I couldn’t resist. Sigh…here we go.

    First, Ted takes issue with this claim on Node’s homepage:

    Almost no function in Node directly performs I/O, so the process never blocks. Because nothing blocks, less-than-expert programmers are able to develop fast systems.

    He then gives an example of a blocking function as some kind of weird disproof.

    Here’s a fun fact: every function call that does CPU work also blocks. This function, which calculates the n’th Fibonacci number, will block the current thread of execution because it’s using the CPU.

    function fibonacci(n) {
      if (n < 2)
        return 1;
      else
        return fibonacci(n-2) + fibonacci(n-1);
    }
    

    I think Ted might be confused about what exactly Node is. Node is not a language or a framework. Node is a small set of JavaScript modules bundled with a JavaScript runtime. “Almost no function in Node directly performs I/O” means that most functions included in Node’s built-in modules are asynchronous—they return as soon as possible and a callback handles the result. It doesn’t mean that any JavaScript function you write won’t block. It’s still JavaScript with normal semantics.

    The first thing Ted does to disprove this claim is fire up Node’s single-threaded HTTP server in a single process and calculate fibonacci(40) on every request. Here’s his conclusion when the request takes 5.6 seconds to complete:

    5 second response time. Cool. So we all know JavaScript isn’t a terribly fast language, but why is this such an indictment?

    I don’t know Ted, why is it? Maybe let’s try the same thing in Python and Ruby so we can see just how terribly fast other languages are by comparison. For reference, Ted’s example takes 8 seconds on my machine.

    Python:

    from wsgiref.util import setup_testing_defaults
    from wsgiref.simple_server import make_server
    
    def fibonacci(n):
        if n < 2:
            return 1
        else:
            return fibonacci(n-2) + fibonacci(n-1)
    
    def fibonacci_app(environ, start_response):
        status = '200 OK'
        headers = [('Content-Type', 'text/plain')]
        start_response(status, headers)
        return [str(fibonacci(40))]
    
    make_server('', 1337, fibonacci_app).serve_forever()
    

    Result:

    $ time curl http://localhost:1337/
    165580141
    real    1m48.732s
    user    0m0.009s
    sys     0m0.007s
    

    1 minute 48 second response time. Cool. This is the fastest it got after multiple runs, by the way. The results are the same for any Python web server because, surprise, it’s measuring the speed of Python. Just like Ted’s example is measuring the speed of V8.

    Ruby:

    require 'rubygems'
    require 'mongrel'
    
    def fibonnaci(n)
        if n < 2
            1
        else
            fibonnaci(n - 2) + fibonnaci(n - 1)
        end
    end
    
    class FibonacciHandler < Mongrel::HttpHandler
       def process(request, response)
         response.start(200) do |head,out|
           head["Content-Type"] = "text/plain"
           out.write(fibonnaci(40))
         end
       end
    end
    
    h = Mongrel::HttpServer.new("127.0.0.1", "1337")
    h.register("/", FibonacciHandler.new)
    h.run.join
    

    Result:

    $ time curl http://localhost:1337/
    165580141
    real    3m18.429s
    user    0m0.011s
    sys     0m0.009s
    

    3 minute 18 second response time. Cool.

    What exactly did he expect to happen in this case? We wrote a function that does block, and told Node to call it on every request. How could Node’s claim that “almost no function in Node directly performs I/O, so the process never blocks” possibly have any bearing at all on this result?

    Maybe the claim that “less-than-expert programmers are able to develop fast systems” is misleading, but so far it’s actually sounding almost reasonable. Getting the response time down to 5 seconds was effortless, after all…

    Ted then discredits Node for disobeying “the Unix way.” This indeed sounds pretty lamentable until you realize he just means that Node doesn’t ship with a CGI module, which is true.

    How odd, then, that Node’s lack of built-in CGI support hasn’t caused an exodus en masse. People still seem to be getting along just fine without it. And even if they weren’t, nothing is stopping anybody from developing a CGI module for Node. So why haven’t they?

    One reason could be that Node’s built-in web server can easily outperform Apache—even in high-concurrency tests. This is apparently dismissible since it violates “the Unix way” of separation of responsibility. All the people who are getting stuff done with Node should just drop what they’re doing, because according to Ted they’re conceptually impure.

    Never mind that most developers will never need the scale that loosely coupled services would provide.

    Conceptually, this is how any web application architecture that’s not cancer still works today: you have a web server program that’s job is to accept incoming requests, parse them, and figure out the appropriate action to take. That can be either serving a static file, running a CGI script, proxying the connection somewhere else, whatever. The point is that the HTTP server isn’t the same entity doing the application work.

    Look, I totally agree with this. From an architectural standpoint, it’s the way to go. I’m sure someday, Node will get its own equivalent of WSGI for Python and Rack for Ruby. That will be an exciting time. But nobody seems to be in a hurry to get there. [Update: Except for Connect and Strata.]

    Finally, Ted concludes that Node was just doomed from the start, since it’s written in JavaScript:

    if (typeof my_var !== "undefined" && my_var !== null) {
      // you idiots put Rasmus Lerdorf to shame
    }
    

    What is this I don’t even…

    Never mind that this code would be even uglier in, say, Python—presumably a non-cancerous language:

    try:
        my_var
    except NameError:
        pass
    else:
        if my_var is not None:
            # Ted needs better examples
            ...
    

    Some people like JavaScript now, Ted. We even have options for people who don’t.

    Look, I’m not that invested in Node, and I don’t have any interest in using its web server for anything. That’s just not what I use it for. But even I could spot the flaws in Ted’s childish article. Please don’t support his inflammatory trash just because you don’t like JavaScript.

    For a much more reasonable take on Node and its performance claims, I recommend this post by Alex Payne of Twitter.

    Discuss this post on Hacker News.

    [Update: I’ve posted a brief followup.]

Notes

  1. rifqi reblogged this from exogen
  2. arch1t3ct reblogged this from exogen
  3. dirpros92 reblogged this from exogen
  4. prosdir23 reblogged this from exogen
  5. healthnewsbytehost reblogged this from exogen
  6. jamesflorentino reblogged this from exogen
  7. exogen posted this