There are so many software development languages available these days its hard for many software developers to know which is best for a given application project. What many developers don’t consider is the long term maintenance and support needs of their application as well as the scalability of the application in the long term, if its poorly built from startup using a language that will not be fully backwards compatible in the long term then supporting it and expanding its functionality over time may not be viable.
Another point often neglected is the original purpose of the language and what it was developed for. I have come from an assembly language background, then started using “C”, then moved to C++, Java, PHP and all the current languages, so picking a language for a task is based on years of programming skills and engineering software and hardware systems. The result has been an understanding of the effort required to engineer software and what tools are needed to build maintainable clean engineered solutions.
A closer look at “GO” and Node.JS
One interesting issue in Node is its inability to directly Export or Import a Module, to achieve this Node uses constructs from RequireJS and CommonJS. Go provides a project like structure that supports multiple files, libraries and there is a keyword called “import” for importing libraries.
Go is suitable for heavy CPU applications and lots of IO, while Node is geared towards server IO use cases, such as a backend server to a client server AJAX application. In fact Node’s native support for JSON makes building REST / JSON api’s easy and is something Node.js excels at.
Unlike C++ and Java, Go does not implement OO concepts the same way, there is no Class construct but it does have an “interface” construct and methods can be defined to operate on a common type. Go implements simple data structures and methods that can operate on them. Node implements what are best described as “pseudo” classes using Prototypal inheritance. On the face of it, Node would look more like a typical Object Oriented application and many libraries are built using this form of OO programming.
Concurrency and Threads
Node has a non-blocking I/O model that does not implement threads at the language level, although threads are implemented underneath the hood. Non-blocking IO makes Node a great choice for wrapping other data sources such as databases or web services and exposing them via a JSON interface. Its heavily geared for web based IO and its eventing model makes handling multiple connections possible. It achieves this by implementing an event loop at the language level. In Node this works very well and overcomes many issues that threads introduce.
“Goroutines” are what Go implements to make concurrency easy to use. The idea is to multiplex independently executing functions, “coroutines”, onto a set of threads. When a coroutine blocks, such as by calling a blocking system call, the run-time automatically moves other coroutines on the same operating system thread to a different, runnable thread so they won’t be blocked. As a result, goroutines have little overhead beyond the memory for the stack, which is just a few kilobytes. It is practical to create hundreds of thousands of goroutines in the same address space. If goroutines were just threads, system resources would run out at a much smaller number.
Node is single threading, it provides an event model using callbacks, but there are no threads in Node that your code runs in. node expects calls to return quickly, realistically if you need some level of concurrent operation then run multiple Node applications and use IO to pass messages between processes.
Node has an active community which has also seen a number of functionality enhancements like JXcore, RequireJS and CommonjS being the most common. There is also “Passport” for authentication, “Blanket” for unit testing, URIjs, pdfkit, aws-lib, LevelDB and ElasticSearch to name just a few. All are geared towards using Node.JS as a backend servicing web clients in one form or another either directly or as a backend service.
Go also has tons of packages, but a glance through all of them appears to cement Go at the system utility development model.
Performance is always a contentious issue, rather than reinvent the wheel I did some research on what others have done, this article on a bubble sort Benchmarks: Nodejs vs GO 1.1 and a further update here Node vs GO 2014 by Jonathan Warner showed a simple effective testing method that drew a lot less “negative” comments than other testing comparisons.
At a glance – Google Go
For those that haven’t done any Go yet, it’s “a fast, statically typed, compiled language that feels like a dynamically typed, interpreted language”. It is backed by Google, and is used at Google, Heroku, and other places.
- Statically-linked compilation which makes deployment extremely easy
- High concurrency
- Large core library with components like tar, zip, blowfish, gifs, pngs, templating engines, websocket library are in core
- Primitives aimed at concurrency – Atomic Channels to reinforce that you should share memory by communicating, instead of communicating by sharing memory. They make it very easy to implement concurrent, scalable solutions without race-conditions.
- Extremely fast and on par with C for some tasks.
- Easy to keep up with latest release – The gofix utility makes keeping up with new releases trivial, as it automatically fixes your code
- Deploys as a single executable binary.
- Type-safety – Can be tedious such as converting strings to byte buffers, and back.
- Third-party libraries are third-class citizens – With the focus on a large standard core, third-party libraries have been largely left behind.
- Lack of community – Possibly due to the community being composed of Google.
- Naive garbage collection – Some reported performance issues with applications stopping during garbage collection, this may have now been fixed.
- Focus on doing things ‘the right way’ – This is probably shown most in the ‘gofmt’ utility which automatically formats your code so everyone’s code looks the same. Uniformity is highly prized in the Go community.
At a glance – Node.js
- Large existing community and libraries to draw from. Though most browser libraries aren’t applicable, they do mean there is quite a bit of experience with the language.
- Third-party libraries are first-class citizens
- The focus is on keeping the core library as small as possible (reverse of Go).
- Excellent package manager (NPM) included from the beginning. The number of NPM libraries are growing astonishingly quickly.
- Everything is a package. In Node the prevailing wisdom is to make everything an NPM package. Bouncy (an HTTP Proxy), Forever (a Monit-like process manager), and even NPM itself are all packages which can be required and used programmatically.
- Socket.io and JSON – Native support for JSON data and execution.
- Excellent cluster management tools – dnode, hook.io, replicant, cluster to name a few.
- Focus on isolation of processes and fault-handling – Node’s single-threaded design forces it to be scaled by using lots of isolated communicating processes. This has pros and cons, because while it is initially more difficult, it also helps avoid race conditions, and means that scaling across machines is not much harder.
- Poor error handling and exception handling resulting in ‘Crash-only software’ for handling serious errors. On the bright side if you write each component of the system in isolated, separate processes, the catastrophic failure of any one component can’t bring down the entire system.
- Single-threaded – Since Node is single-threaded you rely on forking, and communicating between processes to make use of multiple cores. The upside is that Node sees less difference between multiple core concurrency and multiple machine concurrency.
- Not ‘proven’ at scale – Lots of blog posts on moving away from Node.JS to Go due to high I/O based performance issues, few blog pages on moving the other way.
Along came Docker
Google Go has had a significant impact in the last 2 years with the development and release of Docker, an application container engine that will revolutionize the application deployment space and cloud computing in general. In fact to cement Go as more the systems level language, Docker’s creator Solomon Hykes cited Go’s standard library, concurrency primitives, and ease of deployment as key factors, and said “To put it simply, if Docker had not been written in Go, it would not have been as successful.” For more detail on Docker see our article What is Docker? and Docker Basics: A practical starters guide.
Both languages appear “production” ready, with 100’s of documented live deployments and an ever growing user base. From the usage models, most Node.JS deployments appear to be as web server back ends while Go appears to have been deployed in system level applications, mainly by Google and now Docker.
Numerous companies have made the switch from Node to Go citing:
- Performance improvements as the #1 core reason.
- The callback model in Node is cited as “Hell” in large applications.
- Large CPU overheads in high I/O output scenarios especially with string outputs back to web clients.
Wrapping up – Some thoughts from various communities
I like this one – “In short, Go feels like it is what C would be like if it were developed today.”
and this rather good comparison –