Ask HN: Is NodeJS stable enough to build the next Twitter?
I'm well versed in Python, and if things go balls up, I can rewrite the entire backend in a weekend in Python. But having used Python before in many other projects and been rather pissed with its overhead of realtime operations, I picked Node as the way to go.
But am I right? If things have gone wrong for people here using NodeJs, where does it happen? Which parts should I be most careful in during the architecture of the project?
EDIT:
Thanks for the answers till now. To clarify, no, I'm not building the next twitter. I took it as an example to explain a worst case scenario(or best case scenario, the way you look at it). Recently Jeff Atwood wrote about why he choose Ruby, and he made a valid point that isn't the cool language anymore. It has matured, stable and all of that. Considering that I wasn't using Node since it came into existence, I can't comment on its maturity.
Most examples in Node available on the web, try to clutter up everything in one file. So, I wanted to know the best practices when it comes building an architecture in node.
59 comments
[ 3.4 ms ] story [ 105 ms ] threadNode's pretty well tested by now for heavy traffic. It's not my personal cup of tea but I imagine that the obvious bugs and performance degradations have all been squished.
It should be stable enough for building a large scale application but maybe not easily for an app as big as Twitter (we are talking about a shit-ton of requests per second). You are not there yet though, are you? :)
If things go wrong in python, you'll probably have an easier time identifying it and fixing it due to actual helpful stack traces.
You should be very very careful when architecting your project and make sure you understand error handing to a T.
This also isn't a case of premature optimization at all, this is just about making good choices that will persist with the project for a long time. Premature optimization used to mean unnecessarily writing assembly or borderline obfuscated C in the name of performance, which led to programs being difficult to comprehend, hence it being the root of all evil. Today this has been perverted to mean "hey, buddy, if you think about performance you're optimizing prematurely!"
Do you launch today with SquirrelClonr, or delay launch by 6 months to switch to the Squirrel Clone-o-matic? Since it's possible to increase the speed by 12 times, surely you should switch to the Clone-o-matic, right?
It depends on your customers. If you can be profitable by selling squirrels cloned in 12 hours, why wait when waiting could allow a competitor to monopolise the squirrel-cloning market. Plus, if you find out that nobody wants cloned squirrels, however long they take to produce, you've saved six months.
So premature optimisation (in this sense) means "thinking about performance of what you're trying to build before you've even established whether or not you should be building it". If you have no customers, it doesn't matter how quickly your code runs.
The difference is it doesn't take much more to write something with Node than it does Python or anything else, and thinking about good architecture (particularly database architecture) is prudent. I said it before, but it's worth repeating: today's code become's tomorrow's legacy code, and certain early architectural decisions can make a hell of a difference later.
Your argument is like saying you're going to build a house but don't know if anyone will want to live in it, so instead of doing it properly you're going to use anything to build it regardless of how suitable, and regardless of how well the construction will stand up against the weather. If someone chooses to live in it, well, crumbling is a great problem to have because you got there first. If you're going to say this analogy is stretched (I'd disagree), think about it in terms of security. Writing decently secure software takes a little bit more mental overhead. Is it worth it to write a secure application from the ground up in spite of not knowing whether users will adopt the service? (The answer is always yes.)
Also, first mover advantage is a myth. There are plenty of examples of the first, or early mover, being toppled by someone who came along later.
Premature optimization means creating code that's difficult to comprehend without justification in the name of performance, not thinking about which language/framework/vm is going to yield a decent performance profile overall.
The first houses in a complex are usually sold before being built, on the basis of a prototype - be it blueprints, 3D walkthrough, or a show-home. If they can't sell those houses, they don't build the rest. It's the cheapest way of establishing whether there's a market for the properties they intend to sell, and it's much cheaper than finding out there's no market having built 50 of them.
> "today's code become's tomorrow's legacy code"
Worry about it tomorrow. It's a nice problem to have as it means you're still in business. Most businesses started today won't be.
Until you've established that your product is going to have customers, the performance is irrelevant.
My favourite example is the fake 'Buy now' button. You don't need to build the payment process until you know people want to pay, and you definitely don't need to worry about how quickly it runs.
If people want it, they'll pay even if it's slow. If people don't want it, why are you building it? Nobody buys a product just because it's quick (unless performance is the central feature.)
Writing code that's maintainable, secure, and performs well isn't all that difficult, and it's certainly not some horrible burden that will eat up all your time at the expensive of customer acquisition. There's no excuse for sloppy work, and yes, customers can jump ship if they think your product is substandard and response time adds to that feel.
You're also not going to be less busy in the future and having a codebase that's difficult to maintain is going to put excessive pressure on you or your team. Sloppy code is harder and more expensive to debug than well written code in all respects. You're also more likely to have major problems (performance, security, grim bugs) that could have been avoided simply by thinking a little up front. It's not all that much more expensive to write decent code. It's also not appreciably more time consuming to write a decent NodeJS app than it is to write a Rails or Django app.
I strongly suspect you're conflating over-engineering with making wise decisions that don't require a huge time burden and make life easier in the long run.
Specifically to answer you question, no. Node could work as a thin publishing veneer on a much larger stack but you just don't get what you need from Node.js end-to-end.
http://nodejs.org/jsconf2010.pdf
Why does everybody here seems to stick to words in such a "nerdy" way? :) Use common sense sometime guys.
Twitter started with Rails, and at some point decided it was more efficient to do an incremental rewrite on the JVM.
I'd bet it took Twitter a bit longer to replace their infrastructure, and they survived just fine.Build in whatever is rapid, for you. At this very moment. Using your resources.
Keep things service-oriented, decoupled. It'll be easy to replace things one component at a time, if needed.
in summary..
I think it's a perfectly cromulent platform to build on in terms of speed and scalability. But, in case you and I are wrong, follow my advice about staying decoupled and it won't hurt as much.
From what I gather, if that's the word you meant, then it doesn't seem that you believe Node.js is a viable platform. By default, of course, I'll assume the expression was just beyond me!
sorry, I didn't intend it to be obscure. :)
it started from a Simpsons reference long ago (17 years, wow) but over time has fallen into (infrequent) usage to mean[1] acceptable. :)
[1]: http://dictionary.reference.com/browse/cromulent
Much of their stack is node.
Like others have said, 'too many users' is a first world problem that many would like to have.
Also lots of other important companies use it behind the scenes.
A few things to look out for:
1. Error handling. In node you can't just wrap your code in a try/catch. And even if you register a listener for uncaught exceptions, you almost certainly have state shared between requests (for example, a database connection pool), which makes trying to handle such exceptions risky. To use node effectively, you need to be very careful to prevent exceptions from being thrown in unexpected locations.
2. Code rot. It is a lot less obvious what is idiomatic in Javascript as compared to Python, etc. Its easy to end up with a wide range of styles and patterns, which make maintenance difficult.
3. Missed or double callbacks. These are interesting mostly because they are not something you would see in synchronous code, and they can be quite difficult to troubleshoot (especially double callbacks).
Mitigating these issues is as much a cultural challenge as it is technical. Lint everything, review code aggressively, and don't merge code that doesn't have tests. Choose libraries carefully (the ecosystem has come a _long_ way in the last few years).
All of that being said, these are things you should be doing anyway. Develop a good tech culture, but get your product out and grow your user base. If you become the next Twitter you'll have the resources to undo any mistakes you make now.
I'm doing as many things right as possible, but I would rather take as much advice as I can get than being sorry later. Node, like Python, isn't really forgiving.
Error handling is the major issue, because you need to handle all errors manually, i.e. you can't use try catch to trap all errors further down the stack. If you don't handle all errors your Node process will terminate and you may lose some state. Even if you're confident in the stability of your code, I strongly advise that you use a watchdog process like supervisor to start a new process if the current one terminates.
We've handled this issue and kept business logic code simple by using https://github.com/olegp/common-node which uses fibers to present synchronous APIs, allowing us to use exceptions for error handling.
Be very careful when choosing third party packages, since if they don't handle all errors, again your process will terminate and there's nothing you're able to do about it, even if you're using fibers.
One last issue is changes to the core APIs. Since some of them are still in flux, it is advisable to provide an abstraction layer above them so as to be able to weather any changes. For example when streams2 came out, we only needed to upgrade Common Node, with no changes to the application itself.
I do agree with the sentiment of your point though but I'd be interested to know what the parent handles at peak
The main thing that can cause shit to hit the fan is not properly handling errors. I highly suggest using domains, and that when an error occurs, if you can, that you gracefully exit. If not, then all other requests will just abort and that isn't very user friendly.
You will also want a way to be notified of errors, so you can stay on top of them and fix them right away. I use winston and have the error level set to email me.
If you want to talk in more detail, contact me... address is on my profile.
Also domains are in 0.8
Regarding bugs, there are too many to list, just see the tracker and spend some time browsing through them https://github.com/joyent/node/issues?direction=desc&pag... I'm fairly sure you can find pretty much anything.
I'm considering removing all the npm modules from my project and go raw, since most of the obscure bugs are in the ecosystem anyway.
Somehow I missed domains are also in 0.8, thanks!
With the changes to streams in 0.10, I can understand it having some issues. That slipped my mind... but I don't do much streaming so I just assumed it worked.
On a side note, if you've already written your entire backend, I'd recommend user testing, then releasing your product (so that you actually have a chance of becoming the `twitter' you so desire) instead of focusing on your particular (seemingly already-decided-upon) tech stack.
Either case start getting traction, don't over optimize at the start and watch out for memory leaks: there are tiny bits of best practices that you must follow (always consume the response? check. close the request appropriately? check. don't crash the whole node process? check.), some of which not really well documented, that can ruin your day should you get some important press and are not implemented correctly.
Take a look at the issue tracker of the libraries you are going to use, check if there's something that can affect you and perhaps contribute back!
We're also dealing with servers in 4 different locations and some requests need to be proxied to a central location. With a ~300ms round trip from Singapore to Washington, Node's asynchronous nature has been a win when it comes to handling concurrency in the face of backend latency.
The real problem with node.js is the libraries. Just don't use them.
A huge portion of existing libraries is full of hidden bugs, shortcomings, race conditions, edge cases, security issues, unscalable, or unmaintainable (and unmaintained).
This is exacerbated by the fact that npm makes it really easy to publish a library.
Many small buggy libraries.
Core modules are too low level (e.g. http), and you really don't want to use an overlay library.
Not to mention that doing something not trivial fully asynchronously is not as fun as it sounds. You will spend a significant time tracking bugs, fixing edge cases, and making your code stable.
There is still no way to make async code better in core (no promises); and there are a handful of incompatible promises implementations.
Oh, and node.js is not really fine actually. It's not doing everything using asynchronous I/O as you would expect. Node.js uses a thread pool for things like DNS resolution and disk I/O. Only 4 threads by default, for all those things with very different latencies. This means that 4 DNS queries can occupy node.js's 4 hidden worker threads, and block your disk I/O for minutes.
Promises not being in core is a good thing. Eventually many of those use cases will switch to using ES6 generators.
If you want to scale node, you would use multiple processes.
Your DNS example is a corner case. There are discussions around it, and such issues impact all frameworks.
As for security issues, unscalable, unmaintainable etc, those are too generic in nature to comment. I can say this though; node is in production at some of the largest companies in the world and they are talking about it too.
One of the many corner cases that will kill your application or open it to DoS (malicious or not).
I.e. you can DoS any nodejs application if
> There are discussions around itI've seen tickets opened since more than a year on this, without anything showing a willingness to improve that. Version 0.9 even removed the possibility to increase the number of thread (which they re-added in 0.10).
> such issues impact all frameworks
When you start using node, you don't expect that your bottleneck is a thread pool.
In non async frameworks you know you'll have this kind of problems, you can design around it, and a DNS query in some module can't block I/Os for the whole application.
> If you want to scale node, you would use multiple processes.
By "unscalable" I meant libraries using O(n) or O(n^2) algos, with 'n' the number of users or the size of your data, where it would have been easy to do it in O(log) or O(1).
> Promises not being in core is a good thing
Why ?
> Eventually many of those use cases will switch to using ES6 generators
It hope it will improve, but we are discussing the current state of nodejs
How you manage the database sharding could be more important than if you use nodejs, C, php, cgi + perl...