Guillaume Jacquart
Sep 18, 2017 · 9 min read

I’ve been using the .Net stack for 5 years now, both professionally as a backend developer and architect, and personnally for open and closed-source side projects. I do mostly web applications, but also work with Big Data stacks.

After a few years of tinkering with the PHP ecosystem, and a reasonable amount of time spent doing Java, the C# language appealed to me for both its effectiveness and its pleasantness to work with. It is consistent, thoroughly designed, and Visual Studio is to me the most developer-friendly IDE (when working with C#). Moreover, the ASP.Net framework contains everything a developer needs to develop web applications without requiring additional frameworks or librairies.

The only issues with the .Net ecosystem to me were of ethical matter : the language and framework were closed-source, and the ecosystem was very Microsoft-oriented (even though Mono provided a nice alternative to run on Linux, but with a constant gap in supported features).

That’s why, when Node.js started to get traction in the early 2013’s, I could not help but have a look, even though my colleagues kept trashing JavaScript as a dysfunctional language and Node.js as a buzz. I was thrilled with learning about the single-threaded callback system, I enjoyed using ExpressJS to create REST APIs easily and very fast.

Then Microsoft announced the ASP.Net Core, which carried hopes of natively cross-platform and open-source .Net environment. That’s when I begin to go back and forth between the two technologies for cross-platform backend development.

What follows are my insights on the differences between the two platforms in terms of development environment and features. I gathered theses pieces of information after many years of using these tools, and they help me to choose the best tool for the job when I start a new project. Hopefully they will help you in that regard as well.

Request handling model

  • The Node.js model

Node.js is famously known for its single-threaded callback handler. What does that mean ? It means that instead of handling every new HTTP request inside a dedicated thread or process (like Apache does), it does it inside a single thread.

That makes the request handling in Nodejs single-threaded, whereas in Apache/PHP it is multi-threaded for instance. But Node.js takes advantages of that because it uses asynchronous system IO calls that does not block the request thread. When an IO call is made by the request thread, the IO call is executed on its own thread, and the request thread keeps going. When the IO call is done, a callback is fired on the request thread to handle the response.

This makes Node.js well-suited for IO bound applications, but also introduces the so-called “callback hell”, which can introduces cyclomatic complexity to your code. Hopefully, since v7.6, Node.js implements native support for async/await of promises which makes you code far more readable and maintainable.

The single-thread request handling can be clustered using Node.js native clustering, Nginx load-balancing with multiple application process, or Node.js process managers such as PM2, which makes use of all the server cores to start up as many application process as the CPU can handle.

  • The old ASP.Net way (synchronous)

Historically, ASP.Net MVC (or WebApi) handles requests the traditional way, which is the Apache / PHP way : each request is processed through its own thread inside a thread pool. And each IO call is then processed synchronously inside that thread.

This makes the synchronous ASP.Net model less suitable than Node.js for IO bound applications, which is most of the modern applications front side.

Hopefully, since .Net Framework 4.5 (08/12), C# makes use of the async/await pattern, which introduces asynchronous programing for request handling.

  • The new ASP.Net way (asynchronous)

The async/await pattern introduces asynchronous programing for request handling. Indeed, each request handlers can be tagged as asynchronous, and IO calls can be awaited, which means every request will run on its own thread, and each IO call inside the request handling will be non-blocking.

This Task-based model can make use of callbacks, promise, or async/await programing models.

ASP.Net Core advocates the use of the async/await pattern when writing IO bound web applications.

  • Node.js async/await VS ASP.Net Core async/await

Node.js code for asynchronous database query :

const express = require(’express’)
const app = express()
const MongoClient = require(’mongodb’).MongoClient
const url = 'mongodb://localhost:27017/example'
app.get('/', async (req, res) => {
const db = MongoClient.connect(url);
const col = db.collection('data');
const results = await col.find({}).toArray();
res.json(results);
})

The equivalent ASP.Net Core code (without boilerplate startup code):

using System.Collections.Generic; 
using Microsoft.AspNetCore.Mvc;
using System.Linq;
using MongoDB.Driver;
namespace TodoApi.Controllers {
[Route("api/[controller]")]
public class TodoController : Controller
{
[HttpGet] public async Task<IHttpActionResult> GetAll()
{
var client = new MongoClient("mongodb://localhost:27017");
var database = client.GetDatabase("example");
var collection = database.GetCollection<BsonDocument>("data");
return Ok(await collection.Find(new BsonDocument()).ToListAsync());
}
}
}

The difference between the two process model is that on highly concurrent environment, ASP.Net Core will handle more requests because requests are handled in parallel. Although the context switching between threads can have its cost when using a lot of thread-shared variables. In that situation, Node.js could be faster.

Many “mainstream” modern languages such as C# implements some sort of asynchronous IO mechanism, which are often misunderstood by the Node.js developer community, and can generate unexpected outcome (https://stackoverflow.com/questions/43920942/unexpected-outcome-of-node-js-vs-asp-net-core-performance-test/44073433).

This makes Node.js far less revolutionary in that regard. More information on that can be found here.

Programing language

  • Features and safety

Coming from a C# background means that I’ve been exposed to a lot a criticisms regarding Javascript dynamic typings and surprising boolean conversions. Must of these criticisms still lay on solid grounds, considering that JavaScript was build in 10 days as a “glue” language to bring dynamic features to HTML pages.

On the other hand, the language has evolved a lot since then, and the new specifications for JavaScript (ES6/7), for which most of the key features are implemented in Node.js, brings a lot to the table, such as:

  • Classes
  • New identifiers (const, let) that increases code safety
  • Arrow functions
  • String interpolation
  • Generators
  • Object exploration (which looks like C# reflection features)

C# is far more powerful I think, because it makes use of all the benefits of having a strongly-typed object-oriented programing language. The Visual Studio IDE is quite something and provides the best integration for a language (at least that’s what I think).

But for a large majority of cases, and with the growing popularity of the micro-services pattern, a lot of these awesome features are not necessary for small to medium code bases.

  • Learning curve

Coming from classic MVC frameworks, the Node.js / Express combo take some time to get used to, as the new middleware-pattern can be new to some developers. The event driven paradigm is also specific to Node.js and can take time to digest completely. Everything else in the language is quite straightforward as it is simple Javascript.

What can be complicated at first when working on a medium to large application is the patterns to use for code re-factoring and code architecture. Since the Node.js frameworks such as Express.js are very flexible, choosing the right architecture and file structure can be cumbersome, but a must-have to enhance evolutivity and maintainability of your application.

The ASP.Net (Core) MVC / WebApi framework provides the developer with an already made code structure, dependency management and code architecture. The developer still has some code-organization to make such as separating the database layer from the web one, but overall far-less time is needed to think of that. Developers used to POO paradigms will feel at ease rapidly.

For simpler web apps such as light REST APIs, the Expressjs/Node.js is very straightforward and easier to start with as the boilerplate template is only one small js file and a package.json file.

  • Prolificacy

I found that writing simple code is a bit faster for me in Node.js than in C#. To me that is because most simple applications (REST APIs, console application, helpers, …) take advantage of having more flexibility, as you are not designing a lot of the app ahead of development.

More and more studies question the relevancy of static typing to reduce the amount of bugs in an application (https://medium.com/javascript-scene/the-shocking-secret-about-static-types-514d39bf30a3). If such studies happen to hold some truth, then using a strongly-typed language can be more of a drawback than a benefit.

On the other hand, I’ve found that large code bases tend to be far more readable when working with C# than with JavaScript. To me, that mainly comes from the fact that strong OOP paradigms, promoted by the C# patterns, enhance cohesion and decoupling of the code.

Regarding debugging and Unit testing, the C# / Visual Studio combo still provides the best experience, but the Node.js / Visual Studio Code with extensions are not lagging far behind. The building time is also smaller for a Node.js application.

The ecosystem

That is the aspect where the two technologies differ the most. Node.js relies on a very large community writing a lot of modules in its main package management system (NPM). Most of the popular frameworks and libraries rely on a lot of small packages to work.

This provides new developers with a lot of available modules to respond to the always-shrinking development time. But it can also have side-effects on the entire ecosystem when a package is updated or pulled off (http://www.haneycodes.net/npm-left-pad-have-we-forgotten-how-to-program/). A lot of these packages are also not well maintained and error-prone.

The ASP.Net Core framework on the other hand is developed by a dedicated Microsoft team entirely, and provides everything you need to build a fully functional website. Third-party packages are often well built and backed by large companies.

One good example is the field of ORM (Object-relational mapping) tools. Entity Framework, the official ORM for ASP.Net, offers a full set of features such as code-first database modeling, auto-generated migrations and support for the main engines. Node.js is lacking behind with very few ORMs available and far less features supported. Sequelize is getting closer to it though…

This might come from the historic affinity of Node.js with MongoDB and the MEAN stack. MongoDB being a NoSQL flexible JSON data store, a full-featured ORM has no meaning with it.

Deploy & Run

This is the area where Node.js really shines. Node.js is open source, cross-platform, docker-ready and most PaaS compatible. This means you can host your code fast and easy on the following platforms:

  • Your own Linux, Windows or Mac server. You just need a node engine running and a reverse-proxy (Nginx is the most popular and really fast)
  • A Docker container: there is an official docker image (23Mo for the Alpine-based image)
  • Most PaaS cloud providers (AWS, Google App Engine, Azure, Heroku, …). These offer you everything you need to host your app to let you focus on the development
  • There is also Now, which offers a one-liner for Node.js deployment with no configuration.

There are also a lot of CI&CD platform that can build, test and deploy your Node.js code with ease (Travis, Codeship, Circle CI).

ASP.Net on the other hand is a bit harder to handle in terms of deploy & run. Althought the ASP.Net Core framework is cross-platform, there is not yet the same amount of self-hosted and cloud tools for CI, CD and hosting. At the current time here are the hosting platforms known to me:

  • Your own Windows Server with IIS for ASP.Net classic applications
  • Your own Linux server (main distributions) with a reverse proxy: this is getting better for .Net core but the package is still very large and updates are not always handled nicely (for .Net core 1.x at least)
  • A Docker container on Windows (ASP.Net classic and Core) or Linux (ASP.Net Core only): this works nice, but is still quite heavy (120Mo for the latest ASP.net Core linux image)
  • Some PaaS cloud providers: mostly Azure, but some unofficial build packs exist on Heroku

For CI & CD I only know of Visual Studio Team Services that does that in the cloud.

Conclusion

  • Node.js asynchronous event-driven request handling model is not that far from the ASP.Net async/await multi-threaded one
  • Node.js performances are not always better than ASP.Net Core ones. They even tend to be worse, even though the difference might not matter for common apps. CPU-bound apps will generate bottlenecks
  • The JavaScript language is not that bad (getting better !), and combined with Node.js, can lead to fast development cycles
  • ASP.Net (Core) is mostly preferred for large code-base as it provides ready-made software architecture and tooling that fits most needs.
  • For micro-services or small to medium-sized REST APIs, Node.js provides a nice alternative for lean development with many hosting and CI&CD services available
  • As always, there is not a better tool for each scenario, try and pick the one that fits your requirements

Guillaume Jacquart

Written by

Digital enthusiast, web engineer

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade