.NET Core vs Node.js. Arguments and Facts
This article is a continuation of the previous one , which raised many good and interesting questions. When I started writing answers to the comments, I realized that I hadn’t told a lot. Therefore, I decided to write a new full-fledged article, but from a different angle: added personal experience and causal relationships. If you have not read the previous one, then that’s okay. You may not read it.
The main idea is that in mid-2017, I initiated a change in the main development stack of backend applications developed by our company from .Net (C #) to Node.js (typescript). How did this come about, do I regret the decision made and am I not going back? About it below.
First, a short story about yourself. I was born a programmer, not a manager. He made his first application, which is not ashamed to talk about, in 2003 in C ++ in IDE Visual Studio 6 as a student. It was a Transformers game using D3D, DirectSound, and almost all the other modules that were part of DirectX 9 at that time, but why, “I’m a programmer.”
My classmate wrote this game for about a year. This was exactly the programming that I dreamed of since childhood – very high-tech, with the use of encapsulation, polymorphism and other charms of OOP, as well as tricky algorithms and unusual data structures, such as fibonacci heaps.
Then it seemed that for 3rd year students without work experience and a clear portfolio, this was the only opportunity to get a job in a company very popular at the time of GameDev. But nothing came of it. After a 4-hour interview, at which, among other things, we wrote on paper an algorithm for intersecting two lines on assembler, we were refused, citing the fact that we are not ready to contact students now.
C # .Net
A couple of months after this failure, I ended up in a large (more than 100 programmers) software company, where, with the advent of C #, I switched to it.
As a result of communication with more experienced and highly qualified colleagues, in practice I mastered such interesting things as:
- aspect-oriented programming (AOP) to eliminate utility code from business logic;
- code generation on tt-templates, to reduce the routine of creating the same type of code;
- NServiceBus, MSMQ, WCF for interservice interaction;
- Dependency Injection (DI);
- the principle of constructing interfaces based on Data Load Options (DLO) for specifying related objects, information about which should also be obtained as a result of the query. Something like a self-written GraphQL, which at that time did not exist in a public form;
- profiling and optimizing SQL queries;
- creating web applications on ASP.Net MVC, knockout and angular.js.
The main disadvantage was a disastrously small number of interesting tasks, where one could apply the accumulated knowledge of algorithms and data structures, as well as all kinds of patterns. Well, I’m a programmer, I can’t live without it!
The division in which I worked developed interfaces on which data from the DBMS became available to other systems. Year after year, the idea began to grow in my head that in a soulless enterprise everything was just that and that OOP for tasks like “take this, twist it a little and give it back there” was an idea.
At the level of relational DBMS and data contracts in the REST API, OOP is supported only by the enthusiasm of a developer who wants to write on OOP and only on it. And the logic of the intermediate layer (between take from the database and give to the consumer) in terms of complexity and connectedness is never GameDev and can be easily written in a functional style.
In mid-2015, with 8 years of experience in C #, I went to SoftMediaLab for interesting tasks .
Thanks to the experience gained both on the backend and on the front, I immediately became the head of the development of a new fairly large project – the online p2p and b2p lending platforms. The backend was C # .Net Framework 4.6, microservice architecture, WCF microservice communication, postgreSQL 9.5, ORMLite, ORM-class code generation for history tables and scripts for rolling / reloading DBMS objects, about 150 tables in DBMS, postsharp 2.1 for AOP, sockets for AOP SignalR, landing pages and WEB API based on ASP.Net MVC. At the front, angular.js and JS since typescript at that time was not yet widespread. CI / CD, streaming replication to a backup node.
After the project was completed in mid-2016, we, together with the main developers, conducted a retrospective. The following problems were identified on it, they are the same prospects for development:
- the system has a lot of logic that had to be written and maintained in two places – on C # in the backend and on the front in JS. This logic was connected with the work of the loan calculator: calculations of the payment plan, prolongation, early closure of the contract, etc. According to the requirements, this calculator also had to be present on the WEB site and work without going to the backend;
- often there was a need to carry out a small-scale refinement affecting the back and front at the same time, which one full stack did in the same time as two programmers – a separate back-end and a separate front-end. At the same time, in the case of one full-stack, the development and testing of such tasks was much easier to plan in sprints;
- the system has a lot of code related to transforming data models to / from DTO. It would be better to write and maintain this code not by hand, but by code generation;
- Finding a good full-stack programmer on the stack (C #, angular or react) to reinforce the command is difficult.
I had a love for Typescript at first sight.
Of course, this was not such a strongly typed C #, but rather a hint at the typification with which the programmer described his expectations from the structure of the object he was working with. And our expectations, this … well, you remember …
At the beginning of 2017, after typescript showed its best on the front, the idea of trying to write a typescript backend seemed logical to me.
At that time, I had the following requirements for backend development:
- the code for rolling / repainting the DBMS, including trigger functions and histor tables, must be created automatically – by code generation or ORM;
- code for creating DTO models and mappers to / from them should be created automatically using code generation;
- The swagger description of the REST API should be automatically generated from the source code;
- There should be a full-fledged ORM system that allows working with MS SQL, PostgreSQL, mongoDb, as well as writing custom queries for special cases;
- integration libraries with redis, rabbitMQ, socket must exist, as the probability of using these tools on new projects is very high;
- cross-platform – so that developers have a choice of OS;
- stability and fault tolerance;
- the ability to optimize performance bottlenecks on proven and well-established technology;
- convenient IDE, which makes it easy to write, refactor and debug code.
I spent a month researching items that were not related to code generation and found such worthwhile solutions as typeorm, express.js, socket.io, redis, typescript-rest-swagger.
In mid-2017, node.js was already a fairly stable technology. In a couple of hours on Google, I found about a dozen services working under high real-time load conditions implemented on a node. I also learned that performance bottlenecks need to be optimized in C ++.
To study the possibilities of code generation, I hired a sensible graduate of a technical institute with little experience in C # and generally no experience in JS / TypeScript. To, among other things, check the threshold for entering TypeScript.
The monthly result of the work of an intelligent graduate was three libraries –grunt-generate-view-model , grunt-generate-history-model , grunt-generate-database , which our company still uses on projects. Only the prefix “grunt-” is no longer relevant. It stretches from the original versions when they worked also as plugins of the grunt project builder.
At the end of the summer of 2017, I convened a meeting of technical experts at which I presented the capabilities of node.js, and, with a majority of votes, we decided to make the next commercial project in a bundle of node.js (TS) + angular.io (TS).
It so happened that at this time in our company two projects started simultaneously:
- a communication platform that allows office employees to communicate with customers via Telegram, WhatsApp, FB, VK and Viber in one window;
- and a project to automate the internal business processes of one of our customers.
Both projects were successfully implemented on the new stack. Since then, we have been doing all new projects on TS. The backend on node.js, the front on react (why switched from angular.io to react is a topic for a separate article), and mobile applications on react-native.
Retrospective three years later
Since the introduction of node.js, our company has implemented more than 10 projects on this stack.
What are the benefits of migrating from .Net C # to node.js typescript?
- easily and with pleasure increase their competencies in related fields. For example, at the beginning of this year, a layout designer came to our company, who during the trial period managed to participate in the project, as a backend developer and as a developer of mobile applications. Considering that we have CI, CD, and code review on projects, I am pleased with the result of his work (CI passes, the number of comments on code review decreases from the request pool to the request pool);
- They enjoy the quick restart of the updated version in development mode using the nodemon tool. It is enough to save the change in the file, as the utility will immediately respond to this, collect and launch the updated version of the backend;
- write the maximum reusable code (one for backing, web and mobile);
- work in the usual OS and IDE;
- have more opportunities for sharing experiences and their own development since all around are fullstacks, but someone is cooler in one, and someone in another;
- do not resolve disputes between back-end and front-end;
- They plan minor improvements in functionality affecting the back and front based on one full-stack developer;
- choose an artist for the task, depending on the individual characteristics of the developers, regardless of whether it is a back-end or front-end. For example, a more accurate and meticulous developer more often typeset pixel-perfect, and a programmer with well-developed algorithmic thinking more often writes logic at the front and back;
- consider a wide range of candidates: it doesn’t matter, are you publishing on react, writing on angular or node.js, or maybe you are a react-native programmer? We are glad to everyone, especially those who have experience and understanding of the need to use typescript in projects;
- has a large selection when compiling a team for a new project;
- can speed up development by connecting new developers;
- Does not rack his brains on whom to replace a team leader or employee on a project when he goes on vacation;
- fits into the period indicated at the start of the project and labor costs in 90% of cases.
But there are also objective disadvantages, where without them:
- one great and another good developer who didn’t want to switch to node.js doesn’t work for us anymore;
- debugging Typescript code using breakpoint from under VS Code in conjunction with nodemon completely sucks. Breakpoints do not work, then they work, but not there. As a result, this leads to the fact that sooner or later the developer gives up and starts debugging the code with console.log inserts;
- Typescript – your expectations, these are your problems. Often beginners get into trouble when, for example, they declare an integer variable into which they receive a value from an external library or, say, input from a form and for a long time wonder how it turned out that they had a string value in the variable;
- if ring dependencies appear during compilation of ts in js (for example, the a.ts file imports b.ts, and b.ts imports a.ts), the error occurs at run time (the values of the created instances of the imported classes are undefined), and not at the stage compilation;
- npm infrastructure sometimes (in our experience 1-2 times a year) presents unpleasant surprises. When updating the version of a library connected via ^ in a dependent library (dependency dependency) crashes the entire application. To minimize such problems, a service file was created with a description of the entire package-lock.json dependency tree. But in cases where development is carried out on different operating systems, situations happen when this mechanism does not work without painstaking manual intervention.
Holistic cons of node.js and environments:
- you need high qualifications to write extensions in C ++. But it so happened that for three years we have never had a need for this. The performance of the JS-layer did not rest;
- the possibilities for implementing OOP and reflection are much poorer than in C #. But again, functional programming using middleware is a good alternative for a very wide range of tasks;
- The most facilitated VS Code in comparison with powerful VS;
- instead of powerful LINQ, built-in JS methods for working with arrays, such as filter, map, reduce, sort;
- in a full-fledged VS, better means of profiling an application under load.
I do not regret that I decided to transfer the company to node.js. The advantages on our projects (and this is the automation of internal business processes of any complexity, tender sites, p2p / b2b / b2p platforms, CRM systems, social WEB services, MVP of any business ideas) in my opinion outweigh the minuses. Project deadlines do not break. The recruitment of new employees is ongoing, and those who have passed the probationary period, their eyes are burning, and their hands are torn into battle!
In this case, of course, there are areas where node.js is not rolling right now. This is GameDev, Data Science, Machine Learning, AI. But it is understandable. If there was an ideal tool for everything, then there would be no others.