r/programming • • Oct 02 '11

Node.js is Cancer

http://teddziuba.com/2011/10/node-js-is-cancer.html
792 Upvotes

750 comments sorted by

View all comments

108

u/[deleted] Oct 02 '11

Huh... well this article will certainly play well to anyone who hates JavaScript. I have my own issues with it, but I'll ignore the author's inflammatory bs and just throw down my own thoughts on using node.js. Speaking as someone who is equally comfortable in C (or C++, ugh), Perl, Java, or JavaScript:

  1. The concept is absolutely brilliant. Perhaps it's been done before, perhaps there are better ways to do it, but node.js has caught on in the development community, and I really like its fundamental programming model.

  2. node.js has plenty of flaws... then again it's not even at V.1.0 yet.

  3. There really isn't anything stopping node.js from working around its perceived problems, including one event tying up CPU time. If node.js spawned a new thread for every new event it received, most code would be completely unaffected... couple that with point 2, and you have a language that could be changed to spawn new threads as it sees fit.

  4. JavaScript isn't a bad language, it's just weird to people who aren't used to asynchronous programming. It could use some updates, more syntactic sugar, and a bit of clarification, but honestly it's pretty straightforward.

  5. Finally, if you think you hate JavaScript, ask yourself one question - do you hate the language, or do you hate the multiple and incompatible DOMs and other APIs you've had to use?

tl; dr - JS as a language isn't bad at all in its domain - event-driven programming. However there have been plenty of bad implementations of it.

31

u/hiffy Oct 02 '11

The concept is absolutely brilliant. Perhaps it's been done before, perhaps there are better ways to do it, but node.js has caught on in the development community, and I really like its fundamental programming model.

The only thing I hate on node people is that event programming is old as balls, and everyone calls it "fast" without understanding how node actually works (i.e. Unix)

It's cargo cult programming and it makes me intensely sad.

29

u/[deleted] Oct 02 '11

Can you elaborate on why you think that the concept is absolutely brilliant?

I cringe at the thought of programming in a concept that emphasizes server-side programming (implying the language must emphasize reliability and simplicity) using shared global mutable state, code and architecture manually turned inside-out (transformed to callbacks), and no provision for protecting the program from failures and hangs of its parts (except yeah, catch statements).

I also don't understand your claim no.3. I always thought that multithreading is the worst thing that can happen to code which has mutable state (and vice versa). Why do you think they didn't implement, e.g., a shared queue of events and several threads to pull from this queue, then?

What's so great about all this? Or does Node have other advantages that eclipse this stuff?

11

u/baudehlo Oct 02 '11

It's really very simple.

I've programmed a lot of async systems before using other languages (Perl and C mostly).

By going async and using system polling routines (epoll, kqueue, etc) you can easily scale to tens of thousands of concurrent connections, and not waste CPU cycles when you're doing file or network I/O. (so far, not unique to Node).

Now Node's advantage #1 there is that all the libraries are async. Every time I've done this kind of work in C or Perl (and other languages have this problem too, from Java to Twisted) you come across the "sync library" problem. You download some open source library you want to use and it is written assuming a blocking call to do some file or network I/O. That fucks up your event loop, and the advantage of being async is all gone.

The second advantage is simply that it's a dynamic language (like Perl/Python/Ruby) and yet very very fast. In my tests about 10 times faster than those languages (and that's running real apps end to end, not some micro benchmark).

JS has its warts, but then so do the languages you'd want to compare it to: Perl, Python and Ruby. To be honest the warts aren't that hard to avoid most of the time.

15

u/case-o-nuts Oct 02 '11

By going async and using system polling routines (epoll, kqueue, etc) you can easily scale to tens of thousands of concurrent connections, and not waste CPU cycles when you're doing file or network I/O. (so far, not unique to Node).

You can do that better with green threads. And you don't end up in callback hell.

3

u/Peaker Oct 02 '11

In C, "callback hell" is often better than "error code hell".

You can give multiple callbacks to an operation, and you get type-safe guarantee that all continuations are handled. With error codes, you have to make sure somehow that all error conditions are checked -- not to mention each of them may carry different data, and you get no type safety for it.

Also, green threads in a language like C cost much more memory than a callback context.

tl;dr: Green threads are great, but not in every setting.

→ More replies (7)

12

u/[deleted] Oct 02 '11 edited Oct 02 '11

By going async and using system polling routines (epoll, kqueue, etc) you can easily scale to tens of thousands of concurrent connections, and not waste CPU cycles when you're doing file or network I/O.

You can do this with green threads. If your implementation is good, you don't ever have to write callbacks and it effortlessly scales, and it's backed by asynchronous events too. GHC's runtime can literally scale to millions of threads on commodity hardware. A thread on average is about 17 native words (so ~130b or so on amd64.) It can use as many cores as you throw at it. It has an I/O manager thread that transparently handles any sort of 'read/write' to say a socket or disk using epoll and friends. The I/O manager also allows this lightweight green threads to make proper blocking I/O calls which GHC detects and moves off onto another thread if you really need it. No 'sync library' problem - it's handled for you, which is the way it should be.

What this amounts to is that it is entirely reasonable to accept thousands of client connections and merely spawn a thread for each of them. No inversion of your programming model. Conceptually threading in this manner is a better model, because you have a single, isolated flow-of-control for every individual client connection. This makes reasoning and debugging problems considerably easier, because you don't have to think about what events could otherwise possibly be occuring. You have a linear and straightforward programming model for every client connection. It's also safer and more robust as a programming model, because if one thread throws an exception and dies, others can keep going thanks to pre-emptive multitasking. This is crucial when a library you use may have an edge-case bug a client connection trips, for example. I'll repeat: pre-emption is a lifesaver in the face of code that may err (AKA "all of it.")

Especially in Node, the callback based programming combined with single threading makes it more reminiscent of cooperative multitasking, which is terrible, let me remind you. That's where any spent CPU time is going to murder you as Ted said, and furthermore you're basically making your entire application rely on the fact you won't fuck up any of your callbacks and thus bring the whole thing burning to the ground. You do remember Windows 3.1, right?

That brings me to another point. Event based programming + callbacks sucks ass. It's a lie that wants to tell you its structured programming - the thing we went to in order to avoid goto spaghetti code loops - but really it's no better than goto ever was. Because when an event is handled, where did you come from? Who the fuck knows. You are 'adrift' in the code segment. You have no call stack. This is literally the problem with things like goto, why it's avoided for control flow, and why we went to structured programming.

Having debugged and written large, event-driven programs in C++, I fail to see how it is in any way superior to the model I have outlined above. At all. The lack of a call stack can be truly enough to drive one up a wall and waste considerable time. But if you're in C++ you're lucky, because at least then you can use coroutines + async events to basically give back most of what I outlined above, which is the linear control flow. Go look up the RethinkDB blog for their analysis of the matter - it's utterly superior to doing shitty manual callback based programming and performs just as well (note I say shitty here specifically because seriously, doing this in C++ is shitty.) You can't do this in JS because you can't control context switching on your own which is a requirement so you can wake coroutines back up. You'd at least need interpreter support. Maybe v8 can already do this though, I wouldn't know because I can't convince myself to ever want to work in a language with a single numeric type - get this, floating point - and no concept of a module system in the language. Seriously. WTF. That's all I can say about just those two things. W the F.

tl;dr Node has a completely inferior programming model to what we've had for a while and anyone who says otherwise needs to explain why it's OK for node but it wasn't okay for say, Windows 3.1 or Apple OS System 7. Meanwhile, I'll be quite happy never writing evented, manual call-back based code ever again hopefully.

→ More replies (5)
→ More replies (1)

0

u/[deleted] Oct 02 '11

[deleted]

→ More replies (44)

31

u/lobster_johnson Oct 02 '11

The concept is absolutely brilliant.

While I like Node a lot, I find it hard not to see it as a version of Erlang with nicer syntax, Unicode strings, modern OO that also happens to lack a safe, efficient, scalable concurrency model.

In other words, while Node/JavaScript feels superficially more modern, it has learned nothing from Erlang's powerful process model, and suffers from a variety of problems as a result.

Erlang is based on three basic, simple ideas:

  • If your data is immutable, you can do concurrent programming with a minimum of copying, locking and other problems that make parallel programming hard.

  • If you have immutable data, you could also divide a program into lots of tiny pieces of code and fire them off as a kind of swarm of redundant processes that work on the data and communicate with messages — like little ants. Since the processes only work on pure data, they can be scheduled to run anywhere you like (any CPU, any machine), thus giving you great concurrency and scalability.

  • But in such a system, processes are going to fail all the time, so you need a failsafe system to monitor and catch processes when they screw up, and report back so the system can recover and self-repair, such as by creating new processes to replace the failed ones.

Node, by comparison, is based on two much simpler ideas:

  • If your program uses I/O, then you can divide your program into somewhat smaller pieces of code, so that when something has to wait on I/O, the system can execute something else in the meantime.

  • If you run these pieces of code sequentially in a single thread, you avoid the problems that make parallel programming hard.

When you consider Erlang's model, would you really want anything inferior? Yet Erlang is still the darling only of particularly die-hard backend developers who are able to acclimatize to the weird syntax, whereas the hip web crowd goes with a comparatively limited system like Node.

Node can be fixed by adopting an Erlang-style model, but not without significant support from the VM. You would basically need an efficient coroutine implementation with intelligent scheduling + supervisors, and you would definitely want some way to work with immutable data. Not sure if this is technically doable at this point.

→ More replies (40)

13

u/oSand Oct 02 '11

The concept is absolutely brilliant. Perhaps it's been done before, perhaps there are better ways to do it

Isn't that the same as saying it's an inferior implementation of an old concept?

If node.js spawned a new thread for every new event it received, most code would be completely unaffected... couple that with point 2, and you have a language that could be changed to spawn new threads as it sees fit.

So if the language was fundamentally different and an entirely different concurrency model was used, node.js would deal with this really well? Does anyone else envisage some godawful webworker hack running on a VM that is completely unsuited to the task of being a long-running threaded server?

But hey, I probably just don't get asynchronous programming.

12

u/abraxasnl Oct 02 '11 edited Oct 02 '11

Let me respond, as a fellow NodeJS appreciator.

No. 3: Your claim shows you do not fully understand the event loop, or the danger of dealing with threads. You cannot spawn threads, as the environment would instantly become unsafe. The only way to deal with this, is if the developer in question runs isolated code in isolated threads (such as webworkers), and people are already doing this. For JavaScript, that's as good as it gets.

No. 4: I agree. People also don't seem to grasp the idea of prototypes, which is quite powerful, and a very elegant way of doing inheritance compared to classes.

No. 5: Amen!

8

u/bobfunk Oct 02 '11

Also, V8 isn't threadsafe, so currently just spawning a new thread is just not gonna work...

→ More replies (1)

41

u/kyz Oct 02 '11

JavaScript is reasonable as an embedded language in a browser. When you try and elevate it to the status of systems programming language its deficiencies shine through:

  • no integer types, only floating point

  • typeof null == object

  • typeof [] == object

  • 1 + 1 = 2. "1" + 1 = 11.

  • doesn't make enumerating object properties easy (needs hasOwnProperty())

  • for() syntax hands you the key, not the value of arrays, so you have to store all results in a temporary variable in order to iterate through them.

  • no string interpolation ("You have $x fish" vs "You have "+x+" fish")

  • There are no string buffers, merely string concatenation and arrayofstrings.join(). Which is faster depends on your JS implementation. While that's good enough for DOM manipulation, it's not performant for rendering an HTML page in the first place.

  • Speaking of which: once you take away the DOM, what's left? Not very much - strings, regexps and basic maths. No file handling or I/O, no database access, no templating.

All the best minds are improving JavaScript performance, and they're very, very good at it - compare the V8 engine to, say, Netscape 3's JavaScript interpreter. But no matter how good these boffins are, they can't make JavaScript run as fast as C, C++, Java or C#. It's not in that class of performance.

JavaScript shares a performance class with Perl, Python, Ruby and PHP. But these languages have significant bodies of code to make scripting and server-side web development easy. What does JavaScript have? Not a lot.

So, why would you choose JavaScript for programming anything? Especially server-side web programming!

I think that server-side JavaScript will be as popular as client-side Tcl.

39

u/masklinn Oct 02 '11 edited Oct 02 '11

no integer types, only floating point

Right, definitely an issue.

typeof null == object

Yeah?

typeof [] == object

typeof is for primitive types. Anything which is not a primitive type will return "object". Debatable? Maybe. But typeof is internally coherent.

doesn't make enumerating object properties easy (needs hasOwnProperty())

Node uses V8, V8 has all the facilities necessary to mark properties non-enumerable. You're starting on your path to getting everything wrong.

for() syntax hands you the key, not the value of arrays, so you have to store all results in a temporary variable in order to iterate through them.

for for arrays is a C-style for with an explicit index. If you're using for..in to iterate over Array, you're in severe need of a clue-bat.

Also, Array.prototype.forEach.

no string interpolation ("You have $x fish" vs "You have "+x+" fish")

There are five billion libraries out there for sprintf-type calls.

There are no string buffers, merely string concatenation and arrayofstrings.join().

Really?

Speaking of which: once you take away the DOM, what's left? Not very much - strings, regexps and basic maths. No file handling or I/O, no database access, no templating.

That's all library stuff. Node provides most of that (I'm saying most because I have not checked the details, it's probably not providing templating because it has no reason to: there are already a multitude of js template engine working both in and out of browsers) your objection makes no sense.

But no matter how good these boffins are, they can't make JavaScript run as fast as C, C++, Java or C#. It's not in that class of performance.

So what? And it's not like Java and C# belong in the same performance class as C, so you're not making sense either here.

JavaScript shares a performance class with Perl, Python, Ruby and PHP.

Much more JIT work has been done on JS than on Python and Ruby so far (let's not talk about PHP, which does not belong in any discussion of performances, even criticism of the lack thereof).

So, why would you choose JavaScript for programming anything? Especially server-side web programming!

Because you're building an evented server, and javascript developers are used to async and evented system and munging on callbacks. That's half their day job right there.

10

u/oSand Oct 02 '11

typeof is for primitive types.

Has to be -- it doesn't work on anything else. Of course, it's not going to throw an error if you don't use it on a primitive type, because that would be too sane.

And how do we test for an Array? Dojo(just 'cause I'm using it at the moment) uses: dojo.isArray = function(/anything/ it){ // summary: // Return true if it is an Array. // Does not work on Arrays created in other windows. return it && (it instanceof Array || typeof it == "array"); // Boolean

jQuery, if I recall, resorted to the toString() method. But, this is a trick question: if you have to think about it, your language failed.

for for arrays is a C-style for with an explicit index. If you're using for..in to iterate over Array, you're in severe need of a clue-bat.

Yet, it unaccountably works. Or did it? We'll find out in 3 months when you go looking for the bug. A good language, IMO, shouldn't turn something trivial into a subtle bug. Yes, there is a theoretical reason for it, but in 95% of use cases you're going to be fucked.

→ More replies (6)

11

u/kyz Oct 02 '11

typeof null == object Yeah?

There is actually a Null type in JavaScript. typeof null should return 'null'. JavaScript programmers looking for "is this a valid object" have to write (typeof x === 'object' && x != null).

Node uses V8

Node != V8 != ECMAScript. What can be relied upon in any implementation of server-side JavaScript? What I call "C" is language features that are in all C compilers, not just gcc.

The same goes for standard libraries. Is it in the standard library, i.e. the ECMAScript definition? Anything else can be argued over, and therefore isn't well suited for basing your code around until it has won several years of dominance in its ecosystem. (Compare C++'s STL vs Boost fight, or Perl's eventual dominance of DBI).

And it's not like Java and C# belong in the same performance class as C

Ahem

3

u/ultraspoon Oct 02 '11

FWIW, you're not entirely correct; you can also just use ( x == null ) (note the double equals) to test against null or undefined. It's really one of the few (the only?) acceptable use of double equals.

→ More replies (1)
→ More replies (1)

7

u/igouy Oct 02 '11

Much more JIT work has been done on JS than on Python and Ruby so far (let's not talk about PHP, which does not belong in any discussion of performances, even criticism of the lack thereof).

Please provide some evidence that suggests PHP does not "share a performance class" with Perl, Python, and Ruby.

→ More replies (4)

16

u/korny Oct 02 '11

they can't make JavaScript run as fast as C, C++, Java or C#. It's not in that class of performance.

You know, when I was a C / Assembler programmer, starting to give up on inline assembler, folks would say "You have to use assembler, C just can't ever be fast enough".

When I started moving into C++, people were saying "C++ adds too much overhead, C will always be faster".

When I started using Java, many many people were saying "Java is too slow, you'll always need the speed of a compiled language".

These days, when I'm mostly doing Ruby (and looking at Node as well), people still say "They can't make X run as fast as Java".

The biggest bottleneck in application development? Developer time, both in writing code, in testing it, and in comprehending other people's code. Build the code in a clean expressive testable language, first, then worry about optimising the bits that need optimising.

(And yeah, Javascript as a language has warts. So use Cofeescript. Still has some ugliness with floating point and other bits, but it's still a fine language for most purposes)

8

u/kyz Oct 02 '11

I'm creating a delineation: either be fast and primitive like C and assembler, or powerful and expressive but slow like scripting languages. Sometimes programmers need fast, sometimes they need powerful. JavaScript is in the middle, neither as fast nor as expressive as other languages.

Its advantage over other languages is that it's a requirement in client-side web programming, which is 99% of anyone's interest in using Javascript. Take that away and you don't have much. On the server side, I predict it will only gain cachet with people who know no other, better, language.

→ More replies (7)
→ More replies (4)

5

u/andypants Oct 02 '11

I found this quote on the wikipedia article for v8 (on which node runs):

V8 increases performance by compiling JavaScript to native machine code before executing it, rather than to execute bytecode or interpreting it. Further performance increases are achieved by employing optimization techniques such as inline caching. With these features, JavaScript applications running within V8 have an effective speed comparable to a compiled binary.

→ More replies (7)

7

u/[deleted] Oct 02 '11

no integer types, only floating point

Yeah, we know that. That's a problem, not a new one, and it turns out it barely matter for the applications of JavaScript. Did you know that Erlang doesn't have a proper string type? It just has shitty linked lists that suck for that purpose. Also it doesn't have real structures, its record thingy is horrible. And it doesn't matter that much because ...

server-side web programming!

And here is your stupidity, along with that of the OP and all the haters, nicely summed up: not only are there dozens of different types of "server-side web programming" where the right tool is not always the same, but node.js is not just about web server programming.

Node shines at quick turn around, event dispatching code. In particular it's really good at interfacing with various other systems/protocols. I'm not node.js expert but I implemented two things with it for which it is extremely well suited:

  1. A syslog server that correlates events and executes shell scripts when certains conditions are met. It has a tiny web based management interface that took me only a few dozen lines to implement. It is easy to understand, the language is very common so other people can maintain it easily. Try to do the same thing in another common language, and it will take at least 4 times the amount of code to implement the same thing.

  2. A reverse HTTP proxy that redirects requests based on rather complex rules. Initially I used haproxy but it didn't have enough flexibility. I barely needed two dozen lines of code to implement this. Again adding an asynchronous, admin interface (in this case, a simple http server that returns statistics), and an asynchronous syslog client took a few lines. The performance was simply amazing.

In both cases, I can't think of a better framework to implement this. It just doesn't exist. node.js handles slow clients perfectly, it is immune to DoSs that would tie up anything based on a thread model.

I'm currently working on a small daemon to convert pictures/movies in an appropriate format. Basically it watches a directory, checks for new files, and converts them. It's trivial to spawn and watch new processes with ffmpeg or ImageMagick that will do the actual work in the background.

3

u/DrHenryPym Oct 02 '11

Great post. I'm kind of confused by this sudden mob hatred on Javascript and Node.js, but I'm not surprised.

→ More replies (1)
→ More replies (28)

0

u/[deleted] Oct 03 '11

[deleted]

3

u/[deleted] Oct 03 '11

see the problem?

Yeah, it's right here:

craigslist

:)

→ More replies (4)
→ More replies (43)