Why I struggle with Node
Graham Cox Jan 3 '17
I'll admit it, I actually quite like Node.js. Especially in the ES6/ES7 variety. It's not perfect, but what language is? But what it is is a very quick way to get a webapp going, using the same mental model on both the client and the server, and even sharing the actual API model layer between the two. That's huge. That makes development really efficient. It also has plenty of tools to make development fast.
And yet, every time I use it, I end up getting frustrated. It's nothing to do with the language. It's everything to do with the tooling ecosystem.
I'm an enterprise Java developer. There are certain tools that I'm used to as standard - either as part of the build system, or as part of the language. As I said before, my use of Kotlin makes some of these harder to use, if at all possible, but not all of them.
The tools that I make use of on a daily basis are:
- Build system - In the Node world I normally end up using Grunt for this, but it's not always easy. I'm a Maven fan in the Java world though, and it's a close match.
- Unit testing - In the Node world I usually use Mocha, Chai and Sinon. I've just recently discovered TestDouble which I prefer to Sinon though.
- Compilation - This is more important than people think, because it catches certain classes of errors very early. The fact that I use ES6/ES7 via Babel gives me this though
- Code Coverage - Node does have this, but because I normally use a transpiler it makes it difficult to actually use
- Static Checks - Node does have some support for these, but not as much as I'd like. Specifically I end up using ESLint and JSCPD
- Integration/Acceptance/Verification/E2E Testing - This is where it starts to get really messy
- Logging - Node does this, but it's just not as nice as what I'm used to.
- Debugging - Again, Node does this but it feels very clunky
- Application wiring - In Java I use Spring for this. In Node you use separate modules and require. This works but it's not as clean, because you can't trivially swap out a replacement, or inject a mock for testing purposes.
So, why is Integration Testing such a mess? Integration testing is where you test the entire service as a whole, against a real data layer, and ensure that it works as expected. If there are any third-party service dependencies - other APIs, for example - then these can be mocked out reasonably, but the entire core logic should be present.
In the Java world, using Spring, I can do this really easily. If I'm using a SQL database I use HSQLDB. If I'm using MongoDB or Postgres then I can use the fantastic libraries from Flapdoodle OSS. If I'm using Neo4J or Elasticsearch or Solr then I can just run them in-process since they are Java anyway. And so on and so forth. And the best part of all of this, is that it's entirely built into the application and build system with nothing at all needed to be done to make it work. You just run
and it all happens.
In the Node world, the best I've been able to find so far is to use Docker. This works well enough, but it's a lot of burden on the developer to get things set up. They need to ensure that Docker is correctly installed - which isn't always trivial. They need to ensure that certain ports are available on their local machine. They need to know how to get it all set up and running. It's non-trivial just to get a first build working.
So, in short, Node works absolutely fine and if it works for you then that's fantastic. I just personally always end up missing the ecosystem that I've grown accustomed to in Java. And yes, I know that the correct answer is that I should help build this ecoscystem in Node. It's not exactly rocket science to do this, but it does take time and that's an ever more precious resource.
This post discusses how a children's book can draw parallels with the world of software development, how a well thought out recipe for a project can quickly turn into a mess, and why that's completely okay.