
Hard-won lessons: Five years with Node.js - ingve
https://blog.scottnonnenberg.com/hard-won-lessons-five-years-with-node-js/
======
lasfter
I can't imagine _choosing_ to write Javascript on the server, but considering
its popularity I'm wondering if I'm wrong. So I'm curious as to the reasons
people chose Node.js and whether you would recommend it, anybody willing to
share their experiences?

~~~
ralusek
NodeJS has a handful of things that tend to get brought up. Callback soup and
difficult error handling. Both are resolved by promises. Once you get the hang
of promises, you are capable of doing concurrent asynchronous tasks in a
manner that would be significantly more difficult in any other language.

On top of that, NodeJS is very, very fast. Compared to Python or Ruby,
straight computing is significantly faster, but that's not even where the
meaningful gains come from. Because NodeJS is non-blocking, the core is
IMMEDIATELY available to serve the next request once an asynchronous call is
made (read from DB/Cache/HTTP call). Because of this, each core can serve
thousands of simultaneous connections.

Because JS is single threaded, in order to take advantage of multi core
hardware, you just run the cluster module to effectively launch a concurrent
instance of your application for each core.

As far as the language itself, once I understood it, I loved it. At first, I
thought it was terrible, but when I went to ES6 syntax, and fully groked how
to write asynchronous code, it rocketed to my favorite language.

I started with Java, moved to Python, and although it took me a while to come
around to JS, I would absolutely never go back.

~~~
lod723
> Once you get the hang of promises, you are capable of doing concurrent
> asynchronous tasks in a manner that would be significantly more difficult in
> any other language.

FYI, anyone who has worked with Elixir or Erlang views these sort of
statements about Node as completely ridiculous. The only languages right now
that are making a serious effort to bring real concurrency to modern
programming are Go and Elixir. Node with its single threadedness and global
heap doesn't come close, since those are fundamental problems with JS. The
papering over the defienct language concepts with attempts like promises don't
address these issues at all.

~~~
ralusek
For concurrent asynchronous tasks, being single threaded or multithreaded
shouldn't make any meaningful difference. To make a million concurrent
asynchronous calls on a single thread takes couple MS, to make on multiple
threads takes about the same. For concurrent SYNCHRONOUS tasks, being single
threaded or multithreaded makes a huge difference, but that's not what we're
talking about here. It's also not at all fair to call NodeJS single-threaded
in regards to its concurrency model, as it utilizes all cores through parallel
application instances.

~~~
lod723
The multiprocess Node concurrency model is brittle and fault intolerant. The
event loop scan actually has real bottlenecks at a certain level of fds in
flight. A real scheduler really wins here; libuv sits below the knowledge of
the runtime to really be optimal. You become CPU bound far sooner than you
expect.

With a global heap, you also become memory bound far sooner than you expect as
well.

~~~
chrisco255
Your typical Node instance is using an order of magnitude less memory than the
beastly JVM. We use Java microservices with Spring at my job and each "micro"
service consumes close to 1GB of memory. I've never written a Node service
that had more than 100MB footprint.

~~~
empthought
The default maximum heap size for the JVM on systems with 4GB or more of RAM
is one gigabyte. Has this been changed in your microservices' startup
parameters? If not, it is no surprise that Java uses the memory that you have
allocated to it before garbage collecting.

Spring Boot microservices that don't do much shouldn't need more than 32MB
heap -- see [https://spring.io/blog/2015/12/10/spring-boot-memory-
perform...](https://spring.io/blog/2015/12/10/spring-boot-memory-performance)
for details.

------
Daishiman
Reading up on all these Node.JS posts I'm surprised at just _how many gotchas_
the platform has and how there's no single standardized way to solve them.

It's not encouraging stuff. On paper the platform looks decent but aside from
a few use cases it seems that there's more hype than it merits.

~~~
int_19h
What really scares me about the Node ecosystem is bugs like these:

[https://github.com/npm/npm/issues/10999](https://github.com/npm/npm/issues/10999)

[https://github.com/npm/npm/issues/9633](https://github.com/npm/npm/issues/9633)

I have personally hit both while trying to _build_ (not even write!) Node
software.

The former bug is an example of "broken by design", and it's interesting that
it remains open (therefore admitting that it is a problem?), but no solution
came yet. Although I believe Yarn doesn't have it - but then why doesn't the
entire Node community move to Yarn?

The latter bug is, to me, just insane. Several dozen bug reports (and even
more if you google for the error message); and when it shows up, it can be an
outright blocker. It was for me - I plainly couldn't build the software that I
wanted, and no workarounds helped. And no fix, 1.5 years later and counting.

~~~
joekrill
Technically those are NPM bugs, not Node bugs. And Yarn seems to do a much
better job these days.

~~~
int_19h
That's why I said "Node ecosystem". NPM is definitely a part of said
ecosystem, and it still the de facto standard package manager (which, also -
why, when there's Yarn?).

------
jack9
> Verify All Assumptions

When you have to worry about the environment unexpectedly reusing internal
variables, when is it considered foolish to use a framework where you have to
question every relationship between every concept? Just stop using it.

~~~
thesmallestcat
Or one could learn how `this` works, which is that example is really talking
about. I was unimpressed by these lessons. There are no dark corners of Node,
or JS in general, here. It's more of a narration of the author's growth as an
engineer. The "call the callback last" recommendation is particularly odd.

~~~
ninkendo
The counterintuitive behavior of "this" in JavaScript doesn't need any
defending. I've never heard a justification for this or any other of
JavaScript's quirks that wasn't just a thinly veiled lecture on how it works.

~~~
bbcbasic
They should name it arg0, as that is more indicative of what it is than
"this".

------
josteink
This looks like how I used to work with BASIC back in the 80s:

> I finally added extremely verbose logging ... started adding logging ...
> Verbose logging to the rescue ... I had the right logging in place ... My
> first step was to jump in and add some key logging statements ... added
> extremely verbose logging

Weird to see people happy to be limited to such stone-age work-flows when
fully capable debuggers have existed for almost all proper languages out there
the last 30 years.

Based on this, it's hard not to state that current Node-developer are the new
PHP-developers: Unsophisticated, completely unable to see when the tools at
hand are lacking, and happy with a with whatever they can get running.

~~~
libertymcateer
> Based on this, it's hard not to state that current Node-developer are the
> new PHP-developers: Unsophisticated, completely unable to see when the tools
> at hand are lacking, and happy with a with whatever they can get running.

Comments like this are not the problem with HN.

The fact that they get voted, literally, to the top, is.

~~~
mattmanser
It's not a bad comment, it's just blunt. I also have a similar stories as the
parent commentator, 8-10 years ago when you had even more awful programmers I
would sometimes agree to fix something for someone desperate as their own
coder had disappeared/failed/etc.. Sometimes even in a language I'd never even
used. I'd open some random PHP project, or VBScript, or whatever and find
loads of commented out print statements surrounded by some godawful code.

And it'd often be quite a simple logic bug that I could see straight away.

Regularly using logging for debugging is often an inexperienced developer's
crutch. It's a red flag, the sign of the desperate, that they can't comprehend
their own code or don't seem to understand the business logic.

It _can be_ a bad substitute for replicating the problem, reading the code +
using a debugger.

Sometimes it really is justified. The author seems to have relied on it a lot
though.

~~~
brlewis
>Sometimes it really is justified.

Like in the situations in the article. That's why the comment in question is
not just blunt. It's a bad comment.

A good comment would take a specific instance of using logging and said why
setting a breakpoint / using a debugger would have solved that particular
mystery faster.

~~~
mattmanser
I've read the article. Obviously I haven't seen the actual problem, but as an
experienced developer, I feel that:

    
    
        NaN bug doesn't sound like it needed logging
        Mutability bug doesn't sound like it needed logging
        Dependencies and versions bug doesn't sound like it needed logging
        The Documentation and versions bug doesn't sound like it needed logging
    

Obviously we're not seeing the whole picture or the whole bug. But that's
4/5\. Most of those _sound_ like they needed a simple step-through.

For example, in the documentation and versions async.js bug, he added
"extremely verbose" logging where it appears that a simple step-through would
have immediately revealed to him the passed arguments were wrong and the
signature of the method didn't match the documentation. Which would have made
him realize he had the wrong version.

The only one where I would expect to have to use logging is the tests bug.

~~~
cookiecaper
Interactive debugging is a useful tool, but it can encourage lazy reasoning.
People just write something and step through until they notice some change
they don't expect. That's fine and everything, but talking about "crutches"
and tooling, it sounds like you may be a little biased toward big-IDE style
development that makes the debugger the automatic answer v. some reasoning
over the code and extra logging of potential trouble spots. There's nothing
wrong with logging output v. stepping through in an interactive debugger _per
se_.

~~~
mattmanser
Logging is lazy reasoning. Stepping through is simply seeing what is happening
with a bug you can replicate. Stepping through is the step in debugging
_after_ reading the code.

Even after you've read the code and think you've spotted the problem, you
should step through to 100% confirm you've understood the problem.

It's simply confirming the bug and the solution, it's good science, it's good
practice. It's empirical.

------
garysieling
The biggest hard lessons I've had with Node.js have been around handling
errors - when I first deployed
[https://www.findlectures.com](https://www.findlectures.com), I was shocked to
realize that uncaught errors could take down the whole site. Not handling
error callbacks is also a big problem - TypeScript has been an awesome
solution though, because it can show compilation errors if you screw up
function arguments.

~~~
always_good
That's not Node.js. That's Express or a similar abstraction.

You can execute everything inside a promise like with Koa, which amounts to
something like:

    
    
        http.createServer((req, res) => {
          handle(req)
            .then((response) => res.send(response))
            .catch(() => res.send(500))
        })
        
    

Where `handle` is an async function and thus all your application logic is in
async/await space.

~~~
stickfigure
It's Node.js - the root of the problem is also Node's strength, which is that
there's effectively no stack. You can manage zillions of concurrent
connections, but there's no easy way for the system to unwind bad behavior by
one of those connections.

In a Java app you except all the way to the request response.

In a Go app you return all the way to the request response.

In a Node app, you have to call your way back to the request response. Or you
don't, potentially leaving the whole _process_ in an inconsistent state. The
amazing concurrency of node comes at a price.

~~~
jcheng
Domains were Node's answer to that, and it seemed to work pretty well. I
adopted the approach for my own web framework (not JS based) and it has been
an extremely useful abstraction.

They're soft-deprecated in Node now though, and AFAICT work on a replacement
has stalled.

[https://nodejs.org/api/domain.html](https://nodejs.org/api/domain.html)

------
thibaut_barrere
Something I found surprising was the 1.5 GB limit per process when used on
Heroku ([https://devcenter.heroku.com/articles/node-
concurrency](https://devcenter.heroku.com/articles/node-concurrency)), which
afaik can be circumvented elsewhere ([https://futurestud.io/tutorials/node-js-
increase-the-memory-...](https://futurestud.io/tutorials/node-js-increase-the-
memory-limit-for-your-process)).

This can be problematic at times if you need to open a very large number of
concurrent connections (crawlers, slack connections etc), and can force you to
organize a "cluster" of processes before you really want it.

~~~
jononor
You can have quite a number of concurrent connections in 1GB of RAM, 10k at
least? That seems allright for a single node, and scaling horizontally after
that. For something like crawling, which has a lot of time spent on CPU
intensive parsing and I with unpredictable times (hitting external service) I
would throw that in a background worker from the start.

~~~
thibaut_barrere
I do not have the numbers handy, but it was below 10k (by quite a margin),
unfortunately.

------
ambicapter
Wow, working with Node sounds like a huge pain in the ass.

~~~
senorjazz
This is always my take away. Quoting from an above post

>>Ie. Be okay if a syntax error crashes your server.

Really? I need to set something up to stop a syntax error not crashing __the
server __

These rapid prototype arguments don 't hold either imo. I can knock out quick
and dirty java / c# apps plenty quick enough that work fine for their needs.
In my forays into dynamic languages there is no rapid development due to all
the errors, the lack of IDE capability, the compiler replacing tests I have to
write

------
sandGorgon
I have a hypothesis - I think elixir,golang and java+vertx are carefully
designed, highly productive and high performance frameworks.

However with Reactjs + React Native, you have an unavoidable component of your
company that is in javascript (or will have soon). Why would you not do JS
based server runtime ? With typescript/flow, you actually have an excellent
language with no compile steps. Plus, and probably the most important, you
have a cross pollination of engineering talent and time .

I think its the same case with data science - it is much more productive to
build a python based data science startup, because of
flask+sqlalchemy+jupyter+pyspark+numpy+tensorflow+keras. Yes, you can use
scala or java... but you lose that one-language-to-rule-them-all productivity
multiplier.

From a quick prototyping point-of-view, i think golang would have
theoretically killed nodejs.

------
z3t4
Regarding throw and NaN, when the program throws an error, make sure it's
restarted! When JavaScript throws, it stops doing whatever it was doing. But
the rest of the program will continue to run like if nothing happened. NodeJS
does the right thing by "crashing" / exit.

When you asume something in JavaScript, for example that the second parameter
in the function is a callback _function_ make sure it is!

    
    
      if(typeof callback != "function") throw new Error("Expected callback=" + callback + " to be a callback function!");
    
      if(isNaN(someNumber)) throw new Error("someNumber=" + someNumber + " is NaN! someState=" + someState);
    
      if(something == undefined) throw new Error("something=" + something + " is undefined!");

------
ausjke
spent 2+ years learning js/nodejs etc I finally turned to PHP for server side,
it just seems easier for me to get the job done with php, or it's just me.

~~~
goatlover
Not a big fan of PHP, but it has certainly improved it's performance. Wouldn't
be surprised if PHP 7.1 isn't competitive with Node.

------
joaodlf
I can't help but feel like this reads as Stockholm syndrome. Everything about
Node is hard, but I'll keep on struggling.

~~~
dolzenko
Author calls it 'emotional' :)

------
didgeoridoo
These read almost like Zen koans. Such good stuff.

~~~
savanaly
I agree. I wouldn't quite call it koan-level but the breezy style is
definitely attractive to me.

------
rusk
I have had occasion to work on a Node.js based app in the last while. Well
actually mostly a React/Redux-based app served out of Node.js, so 90% client-
side but ... I think it's great! For quickly hammering something together and
getting something bootstrapped and up and running it is so easy and
straightforward.

But .. I have this niggling feeling .. as my code develops I'm noticing the
expressiveness of Javascript lets me do things that look nice, and impressive
but that I know will come back to bite me when it comes to enhancement and
maintenance.

In this respect Javascript kind of feels like perl with OO and functional
semantics built in from the ground up. It really is a lovely language but I
wonder how well a JS-based system will scale over time. I've heard people say
_" never use ruby in production"_ and I wonder does the same reasoning apply
here.

The argument goes that building apps that use the same language client-
side/server-side carries the virtue that you can write _" pure"_ apps - i.e.
that share code front-end and back-end. That is definitely something that
makes sense, especially if you need shared libraries you won't need to write
the same code twice for each end.

But beyond that, I feel the expertise required at both ends is quite
different. As a systems programmer I feel that strongly-typed rigid languages
are very much the way to go because they give you a better sense of how robust
the code is. Typically on the back-end you want to get something to work once,
and leave it, so you need to be sure it's right.

My more limited experience working client-side suggests that you need far more
flexibility there. Issues are far more easy to spot front-end and the key
virtue is to be able to make changes and enhancements quite quickly and you
don't have the same stringent code-confidence needs as you do on the backend
because the quality requirements are different. Niggles and gotchas can be
spotted, diagnosed and worked around on the front-end but on the back-end they
become a royal pain.

So this, is what I think Javascript is great for on the front-end. I do love
the simplicity of bootsrapping Node.js on the backend and the actual reactive
framework for building apps is really cool. But yeah, for systems code "at
large" I see limitations there for sure: The same we had with Perl, Ruby and
Python. Perhaps what we need is some alternative language support e.g. Haskell
or something that gives the same code-confidence, but which can interoperate
perhaps with the "pure" javascript libraries similar to how say Scheme and
Java can interoperate.

------
z3t4
The article mentions synchronous performance, but there are no mentions of
optimizations. You can do a lot of optimizations in JavaScript and the VM
itself is already well optimized. If your naive implementation is slow, my
experience says _you can speed it up 100x_. Then it's not really worth
switching to C/C++ assembly for another 10x performance. Then it's better to
take the step to pure hardware.

------
flukus
I would have like more detail on what they were doing with node. Building lots
of small apps or one big one? Green fields or brown?

~~~
garysieling
I've only worked on a large side project, but from that experience I have a
theory that it is one of the triggers for microservices as a pattern, because
the pain of debugging a Javascript server application seems to scale a lot
more than linearly.

------
cdbattags
The sheer number of responses/comments here (hell, even the article itself)
justify Node by community alone in my opinion.

I like the fact that I've been able to follow along as it's matured and this
gradual bit by bit learning process has made me a better developer because of
it.

------
crimsonalucard
Too many lessons learned and arbitrary rules to follow equals flaws with
design/language design

~~~
Safety1stClyde
What language does not have these kinds of problems? I have encountered them
with every programming language.

~~~
Daishiman
There are certainly _huge_ differences in the number of gotchas different
plaforms have.

I have been absolutely disappointed by Node's tremendous number of gotchas in
deployment and massive complexity of build pipelines needed to get around the
inherent issues of the platform.

My deployment with Django, for example, have been _much_ simpler to handle.

~~~
Safety1stClyde
For your information: to assess what projects you might be referring to, I
clicked on your profile, but your hackernews profile points me to a URL with a
broken security certificate belonging to Ukrainians, and even when I bypass
the security warnings in the browser, I am sent to a page which gives me the
message "404 not found".

~~~
mattmanser
Hackernewsers was a thing from a few years ago, not his fault it's shut-down.

~~~
Safety1stClyde
The above message is purely for the person above's information. I have no way
of contacting them off this noticeboard, so I wrote a private message to them
about that as a reply, and marked it as such with the words "For your
information". As far as it being someone's fault, the profile on
news.ycombinator.com is editable, so he or she could edit it as they please.
What a nuisance it is to continually have to deal with people who misinterpret
what other people have written.

------
ralphc
I'm seeing a lot of Node hate here, especially for large projects in
Enterprises, but I've been watching a lot of Node Interactive talks, and
companies such as Walmart, Netflix, GoDaddy, Paypal are switching. so why are
they happy with it?

------
linkmotif
Super interesting the part about converting to SSR and React synchronicity.
Thank you!

------
jkchu
I just want to thank the author for a very well-written piece. I found it
incredibly interesting and really connected with the format/flow of the post.
I would love to see similar posts for various frameworks and languages.

------
Kiro
Isn't the whole section about Classes just due to the cache mechanism when you
use require? I.e. all modules are singletons automatically. Is it really
CoffeeScript causing it?

------
kuon
Use elixir on the server side and elm on the client side, done.

------
frik
> Re: "New Relic doesn’t get it"

Can someone recommend a Nodejs performance monitoring solution that works
great?

------
traf68
Sorry to say. Node.js isn't the problem. It is a symptom.

Devs these days are craptastic coming from dev->systems with devops nonsense.
Just stop forcing this terrible language into server and js is as fine as it
will ever be client side.

------
wcr3
so... many... links...

