

The node.js aesthetic - substack
http://substack.net/posts/b96642

======
gbog
Hmm, seems a bit hand-waving to me, but to be fair I have never tried Node.js
and would probably have hard time convincing my finger to type javascript code
for server things.

Javascript always seemed to me a language that you used, and tried to use
correctly, because you _had_ to. On a server I have the choice, right? So my
choice is currently Python so I read this article with some bias.

In the section "batteries not included":

> modules in the core distribution get an unfair advantage over the libraries
> in userspace by virtue of availability and prominance.

I would call it "unfair" if some modules had access to special backdoors and
APIs, but it seem to not be the case for most modules I checked in Python. For
instance, 20 mn ago, I did vim /usr/lib64/python2.6/unittest.py and could
check this piece of code directly. I did see no special magic that could not
be provided by other modules, like Nose or py.test. Moreover, the code was not
looking like "neglected code", even if it did not look like the most modern
kind.

> experimenting, and iterating is much harder

Well, that's what I like the most with core modules: they don't change
overnight, and, while some of them like urllib(2) may be replaced by some
because they have a better competitor, most of them are just good old friends,
like scipy, that don't need to be put upside down every month because someone
found a slightly more elegant way to call two of it's functions.

> "core modules"' advantage evaporates in the face of baked-in concurrent
> library versioning and sophisticated package management.

I have never been considered as a shy sysadmin when I was sysadmin. I am
actually strongly against the "Tool X have misbehaved once therefore tool X is
evil and will never put a foot again on my machine" philosophy. I know some
guys who are. (I was also sound engineer before and mostly all musicians I met
are this way, by the way). But still, having dealt with library version issues
sometimes, I think "concurrent library versioning" and "sophisticated package
management" sound awfully nightmarishly black-magic to me. I guess I would be
more on the "let's understand the most of what happens and not change what
don't need to" kind.

~~~
substack
> But still, having dealt with library version issues sometimes, I think
> "concurrent library versioning" and "sophisticated package management" sound
> awfully nightmarishly black-magic to me. I guess I would be more on the
> "let's understand the most of what happens and not change what don't need
> to" kind.

Concurrent versioning lets you do the "not change what don't need to" part
really, really, well. Versions of packages that have been proven to work well
with one another can continue to do so because if you need a newer version of
some library then both the old and the new version can live harmoniously in
the same codebase. You don't need to be paranoid about upgrading modules
anymore because node and npm are already paranoid on your behalf.

~~~
moe
_because node and npm are already paranoid on your behalf._

Yes, until npm blows up in obscure ways. Which it likes to do frequently.

~~~
IsaacSchlueter
What do you mean by "blow up in obscure ways"?

It generally dumps a ton of colorful errors to your terminal. If you find them
obscure, please be comforted by the fact that I certainly do not find them
obscure, and would love to fix whatever problem they indicate. It's actually
my job.

Also, npm is a software program, and not intelligent. It doesn't "like"
things. To the extent that it has preferences, it loves semantic versions,
accurate metadata, and especially you.

~~~
moe
Not sure if your question is serious?

Here's the obscure blowups that were filed in _November_ alone;

<https://github.com/isaacs/npm/issues/1793>
<https://github.com/isaacs/npm/issues/1790>
<https://github.com/isaacs/npm/issues/1809>
<https://github.com/isaacs/npm/issues/1785>
<https://github.com/isaacs/npm/issues/1801>
<https://github.com/isaacs/npm/issues/1770>
<https://github.com/isaacs/npm/issues/1727>
<https://github.com/isaacs/npm/issues/1654>

~~~
IsaacSchlueter
Moe,

So, when you say "obscure blowup", do you mean, "someone posted any issue
whatsoever"?

Several of those are feature requests, or node-core bugs, or things that have
been fixed.

I find your blowup very obscure. As for npm failing for you, gist or it didn't
happen.

~~~
moe
_So, when you say "obscure blowup", do you mean, "someone posted any issue
whatsoever"?_

No, I mean "random malfunction without clear indication of what happened and
how to fix it".

 _Several of those are feature requests, or node-core bugs_

Might be a mindset thing. How many of the 8 bugs qualify as feature
requests/node bugs for you?

 _gist or it didn't happen._

The gist is that npm needs to become more defensive, clearly state its own
dependencies, and not barf random stack traces in the face of problems. You
should also have a unix-guy fix the installer. Demanding 'sudo' is not only an
embarrassing but also a potentially dangerous mistake.

------
zzzeek
>You don't need to reason about multiple instruction pointers or mutexes or
re-entrant, interruptible execution because all the javascript you write just
lives in a single thread.

I see....on the other hand, when I write request-handling code using (any web
framework ever on any platform), you're suggesting that we _do_ worry about
mutexes and reentrance (not to mention, handling a single request uses
_multiple_ threads?) and that these details aren't already handled by (any web
framework ever) ?

Interesting.

~~~
uast23
My understanding is:

When you are using a ruby or python based web framework, each request is a
blocking request i.e. after each request you wait for the response before
proceeding further e.g. urllib.urlopen(), which as a result causes the no of
requests handled/second to go down. Using eventmachine or gevent respectively
for ruby or python is one way to overcome this.

In node.js there is no such concept of blocking requests and everything is
processed in a single thread. But you can always fork multiple processes using
its cluster module <http://nodejs.org/docs/latest/api/cluster.html>

edit: Oh, I was not trying not advocate anything. nodejs is indeed just a
choice, if I didn't make myself very clear when I said "My understanding is:"

~~~
aphyr
Ruby and Python (not to mention basically every other language) have a
plethora of threaded, forking, and evented models for concurrency.
Thin/Rack/Sinatra, for example, behaves capably in a thread-per-request mode.
Reactor systems are not the only (nor are they even preferable in many cases)
choice.

------
sktrdie
The way modules are looked up - localized to the piece of code that needs it,
inside the /node_modules directory - is one of the greatest strengths of the
node ecosystem.

It's just dead simple and solves dependency conflicts like nothing I've ever
seen before.

------
munificent
> So if a module "foo" was tested against and depends on "baz@0.1.x" and a
> module "bar" depends on "baz@0.2.x", then when you go to use both "foo" and
> "bar" in your own program, "foo" and "bar" will both use the version of
> "baz" that they were tested and depend against!

That sounds like a recipe for disaster if I get an object from "bar" that came
from "baz@0.2.x" and try to give it to "foo" which then gives it to
"baz@0.1.x".

I'm suspicious of silver bullets in general and I'm deeply suspicious of any
silver bullet that claims to slay version hell. Versioning is hard.

~~~
benatkin
The Law of Demeter applies here, even if traditional objects aren't being
used. If the library that uses it encapsulates everything about it, or at
least documents things that aren't encapsulated, it tends to work out just
fine.

<http://en.wikipedia.org/wiki/Law_of_Demeter>

------
scubaguy
> Instead of the http server being an external service that we configure to
> run our code, it becomes just another tool in our arsenal.

This is unfair. If I create a framework cal Java.js and make it possible to
create a httpserver by running server.start(8080, aCallbackObject), does that
make Java great? One day someone might make node.js "configurable" via a
configuration file (that is not written in JavaScript). Will having a
configuration file make node.js less useful?

> Note also that the http snippet from earlier isn't inheriting from an
> http.Server base class or anything of the sort.

That is also unfair. You typically don't use inheritance in JavaScript because
you can compose objects or clone from a prototype. There is no static analysis
of classes, which in the main benefit of classical inheritance. Programming
via callbacks is simply the JavaScript style. This might even change when the
language provides better support for classes, or when more people write their
node.js code using CoffeeScript.

> If software ecosystems are like economies then core libraries are at best
> government bureaucracies and at worst nationalized state corporations.

This is very subjective and I strongly disagree. There is a great advantage in
using a language and framework that has a strong core library and a common way
of doing thing, especially when you start building teams or start recruiting
people to maintain legacy code. If anything, Node should start including more
batteries, such as a module for concurrency.

PS - Here's the thing. I am really excited about the event driven nature of
node.js and the community. However, I am new to node.js and I want to see
quality critical analysis of its strength and weaknesses. If you are going to
tell me its great, you better have pretty solid reasons. This article simply
doesn't cut it.

------
phamilton
Could someone clarify something for me?

Is http.createServer robust enough for production?

It is my understanding that running it as is in production is not a good idea.
You want to at least configure nginx between node and the world. If that's the
case, doesn't that undermine the whole "focus on your application, not the
configuration" point he's making? Sure you can call startServer multiple
times, but then you would still have to focus on configuration.

~~~
viscanti
I don't want to sound like I'm over qualifying things, but it really depends
on the situation. For most applications http.createServer will be robust
enough for production. There are node based solutions for dealing with load
balancing, that would make nginx unnecessary in almost all cases.

It really comes down to tradeoffs. If your primary concern is the most
simultaneous connections, then you'll need to find a special tool, and Node or
most other non-haskell web frameworks probably aren't for you.

Node makes it really easy to do some things. That focus makes some other
things more complicated than they should be (because it's asynchronous, you
end up needing to deal with flow control, often times using queues that
mitigate some of the asynchronous advantages). It's like everything else with
computers. There's tradeoffs. http.createServer can be used when it can be
used (which happens to cover most use cases).

------
koen
> A big part of what empowers node to make these kinds of interfaces possible
> is its asynchronous nature. No longer do you have statelessness imposed from
> on high by the likes of apache or rails. You can keep intermediate state
> around in memory just like in any ordinary program.

Should that not be:

A big part of what empowers node to make these kinds of interfaces possible is
its "statefull" nature. ...

Really, that it is asynchronous is nice for performance (but only complicates
the implementation). But the fact that you keep state in memory is indeed a
big win for ease of implementation, and also performance, especially if state
is not global but only relates to a single session. Of course, that is usually
frowned upon by the web developer community which believes that this somehow
hurts scalability (while it actually helps scalability).

~~~
virmundi
I think the sessions in Node/Express are helpful when pair properly. For
example, Express can use Redis as its session store. This has the benefit of
making the session cross-cluster populated, while still being fast. Add to
this the fact that almost everything in JS is serializable as itself and you
get a pretty good replication mechanism.

------
MostAwesomeDude
I find the spinning in this article to be _incredible_.

The "limited surface area" is all well and fine in JS, because __there is no
object inheritance in JavaScript __. The author tries to emphasize usability
over extensibility, which is a false dilemma in my book since it's possible to
code to an _interface_ in other languages. You define a usable interface, and
then everybody codes things that fit that interface. You don't even have to
care about whether your objects are inherited or composed. Of course,
languages with inheritance are even more reusable because it's possible to
inherit from, and extend, objects which implement the given interface. This is
a key tenet of design in Java and Python.

The second part of "limited surface area" talks about how namespaces and
qualified imports are great. Yep. Welcome to the party, guys. You're only a
couple decades late.

The "batteries not included" section is a great dig at Python, but he could
have bothered to actually bring up examples. It's easy; things like asyncore
are so god-awful that it's trivial to point out where Python's batteries have
expired.

However, he's comparing apples and pomegranates here; Node is not a language!
It's a _framework_. It has a large library of its own which doesn't come
standard with JS. That library provides stuff which is built-in on other
languages, like unit testing, cryptographic primitives, zlib, filesystem
accessors, URL handlers, buffers, iterables, type checkers, and a REPL. To
repeat: __These are batteries which are native to other languages. __

Let's go ahead and compare with Twisted, shall we? I'll omit things for which
Twisted provides protocols and Node provides streams, since those are
(technically) equivalent. Node doesn't appear to contain these things which
Twisted provides: Common protocols for handling lines, netstrings, and
prefixed strings; non-blocking stdio as a protocol, serial port as a protocol,
DNS as a protocol, a DNS server, a handful of RPC protocols like XML-RPC, AMP,
and PB; and full suites for: NNTP, telnet, SSH, mail, more chat protocols than
I care to remember... Not to mention powerful utilities like credential
handling, and enhancements to the Python standard library like object-based
file and module handling. And that's just what's included in the main tarball;
there's a big community of third-party code which implements whatever you
might happen to need. I didn't bother to list the reverse, because __there is
nothing in Node which is not in Twisted __.

"Core distributions with too many modules result in neglected code that can't
make meaningful changes without breaking everything." Are you not aware of
deprecation? Write the new code, mark the old code as broken or deprecated,
wait a few years, remove the old code. This isn't hard. Of course, if Node or
JS actually provided useful tools to mark things as deprecated, it might
happen more often. Python's got DeprecationWarning; why doesn't Node?

The "radical reusability" section is just the author realizing that modules
are awesome. Again, welcome to the party.

~~~
substack
> Let's go ahead and compare with Twisted, shall we?

Oh goodness, yes.

How about an echo server. This one is from the twisted home page, so I am not
knocking down a strawman.

    
    
        from twisted.internet import protocol, reactor
        
        class Echo(protocol.Protocol):
            def dataReceived(self, data):
                self.transport.write(data)
        
        class EchoFactory(protocol.Factory):
            def buildProtocol(self, addr):
                return Echo()
        
        reactor.listenTCP(1234, EchoFactory())
        reactor.run()
    
    

Versus in node you can do:

    
    
        var net = require('net');
        net.createServer(function (stream) {
            stream.pipe(stream)
        }).listen(5000)
    

This is what I mean by limited surface area. I shouldn't need to define 2
classes to write an echo server. An Echo class AND an EchoFactory? And on top
of that I need to mess with a reactor? How does that pass for good API design?

~~~
MostAwesomeDude
The reactor is explicit. "Explicit is better than implicit." You don't have to
always be in the reactor, if you don't want to. It lets people use Twisted to
build GUIs and other things, without always being in the reactor's context.
Twisted is a general-purpose networking library, not a tiny-web-servers-only
networking library.

An example _used to illustrate and educate_ does not necessarily result in the
shortest code sample. That sample does not use twisted.protocols.wire.Echo,
because it was decided that things should be explicit and obvious in the
samples on the front page.

The reason for separating protocols and factories is simple: Sometimes you
need to store per-connection state, sometimes you need to store per-server
state. The separation permits developers to store things in factories instead
of in global objects. Node doesn't have this distinction, and as a result,
things like tracking all connections currently made on a server are
cumbersome.

Another thing is testing. How should a person test the Node example? Every bit
of the Twisted example is trivially instrumentable; I can access the protocol,
the factory, the reactor. I could, if I wanted, _replace_ the reactor with
something mocked. There's no place to do that in the Node example.

I was gonna type out an IRC bot, the favorite exercise of novice coders, but I
felt that would be petty, since there's no IRC library included in Node.

~~~
htilford
> tiny-web-servers-only networking library

really?

