Evaluating Node.js as a Client-Side Technology
Published at 14:49 on 24 January 2026
Executive Summary
- I can see it perhaps making sense in some corporate situations,
- I am not personally in such a situation, therefore, it does not make much sense for me, and
- The Javascript module system is (or should I say systems are) absolute garbage.
How I Got Here
Prompted by data like this, I decided Node.js and frameworks based upon it were worth a closer look, since I am starting a web-based software project for a nonprofit I am associated with.
First, I want to choose something that someone else can easily step in and maintain, and that means choosing something in common use. Second, at least some of what is in common use is likely so for a reason.
A word or two is first necessary on that chart. It’s what pops up highest when I ask Duck Duck Go about the most popular web frameworks. I did once find out the source; it was a poll taken at a major conference of web developers in 2024. So it is actually based on actual real data, which is more than one can say about most lists of top web frameworks. But Node.js is not a web framework; it is an implementation of a programming language, Javascript. Why this is so becomes clear if you scroll down the right sidebar and look at the question asked, which was about “web frameworks and web technologies [emphasis added],” and not strictly frameworks.
But I digress. There are frameworks based on that language, however, which come up repeatedly (and are the top rankers) in that chart.
I’ve dabbled in client-side Javascript over the years, simply because one cannot avoid doing so. It is just not possible to do all that needs to be done purely in declarative HTML. But the key term here is “dabbled.” I have deliberately avoided the sort of Javascript overuse that harms the user experience for so many on the Web, but it is still relatively easy to run into situations for which the easiest and simplest solution is a little bit of client-side scripting.
So I install the latest version of Node.js, and because I have personal experience with how Javascript’s lack of static typing makes it easy to write and difficult to find coding errors, I install Typescript as well.
Module and Import Hell
Right off the bat, I run into obstacles of the sort I seldom do with a new programming language. Typescript blows up, big time, spewing error after error for a small snippet of code that has absolutely nothing wrong with it so far as I can see. After some time, and some sleuthing on the Web, I pinpoint the cause as having my import resolution options incorrectly configured.
Which right off the bat shows that said subsystem is hot garbage in the Javascript world. Just for openers, there’s at least three kinds of modules. Most all programming languages get by fine with just one, but not Javascript. Modules and imports are super-simple in most programming languages. There should be no need to configure things. Imports should just work out of the box, like they have for the vast majority of the world’s programming languages at least for the past fifty years or so.
If you have to write a lengthy web page to explain how your programming language’s imports and modules work, long enough to require a special summary section, and that summary itself takes up three screenfuls, you have a problem.
I finally figured it out and got my test script to compile, but I basically blew a day doing so, and it’s still largely a crap shoot for me how to properly import a given module, because the rules are so complex and I have yet to fully internalize them.
To reiterate: this aspect of Javascript is hot garbage. No other honest assessment is possible. Javascript does modules and imports worse than any other programming language I know of.
Random “WTF Javascript” Stuff
Javascript has more than its share of strange quirks. Most of these are related to it being a weakly-typed language. Probably the worst thing is that Javascript has two sets of equality and inequality comparison operators, which differ in the details of the type coercion they apply to their operands (most languages get along just fine with one). More than one web site is dedicated to pointing this all out.
While this does make for a quirky and sometimes annoying experience, it doesn’t rise to the level of malicious complexity, bordering on total unpredictability, that the module and import system does. Moreover, a lot of it simply comes with the territory when one has a dynamically-typed language. For most of the “WTF Javascript” examples I have seen, if I feed the analogous expression to Python (which is famed for being a “clean” and logical language), I get a similar result. So I could have coped with this if it was all there was.
As such, I almost didn’t include this aspect of the programming language in this article. But I figured I’d mention it here because if I didn’t, people would keep commenting and telling me about it.
Generating HTML with JSX
Why did I persevere? In part because of JSX (and its Typescript analogue TSX). It’s really quite the clever innovation for generating HTML. Not quite so clever as it first appears, but it’s still pretty nice. It’s definitely the easiest and most logical way (and one of the most powerful) ways of letting the user generate custom markup tags that I have run across.
Eventually, however, the conclusion was inescapable: there is just too much mismatch between Javascript and what I want to do for a 100% Javascript framework to make much sense for me.
Event-Based Programming
Javascript gives the programmer a single thread and an event-based programming model with a dispatch loop. It’s evidently very efficient; benchmarks give Node.js a decided edge over alternatives such as Python.
I think, however, that in most cases this is a “so what?” moment. Benchmarks give C and C++ a decided performance edge over languages that do automatic memory management. Yet most programs are no longer written in C/C++, because the mental overhead of dealing with manual memory management is a drag on programmer productivity, and the overhead of finding and dealing with memory mismanagement bugs is an even bigger drag yet.
My contention is forcing the programmer to manually manage threading and context switching is approximately as much a drag on programmer productivity as is forcing him or her to manually manage memory allocation. And just like the latter has the automated solution of garbage collection to relieve the programmer of cognitive load, the former has the solution of preemptive multithreading.
Yes, it’s not as fast. Again, so what? Processors are faster than they have ever been. Raw speed is not nearly so important as it once was.
And how much faster is it? The figure thrown around on the Net is that Javascript comes out 40–70% faster than Python for back-end web software. Frankly, that is a very modest improvement, considering that Python is one of the slowest languages out there. What happens when you compare Node.js platforms to those coded in compiled languages? Let’s just stay it starts looking a lot less impressive for Node.js.
It also must be mentioned that while Python’s ability to multithread has historically sucked, it is about to get a whole lot better.
Culture, or Tradition Being Mightier than Innovation
One of the most overlooked aspects of any framework is the culture and traditions that have evolved around it.
Java really isn’t that bad a language, when you consider its core features. Sure, it seems dated now, but that design represented state-of-the-art consensus in the 1990s. That Java is still around after all those years shows its design got at least some things right. The problem with Java is all the dysfunctional culture and traditions that are present in that language’s community.
Well, part of the traditions of Javascript is where it started: in the browser, as a relatively simple scripting language that was thrown together in a hurry. That use case grew, eventually growing to encompass server-side code, and the language acquired more features as a result. There was never a great deal of careful planning in the process; features were just added in a mostly ad hoc manner, as needed. Sometimes this resulted in features that didn’t grow well (this is the source of much of the module and imports mess).
Because of this history, there is this hidden implicit assumption in the Javascript world that web pages will have a lot of Javascript in them. Javascript frameworks, even server-side ones, tend to make this the path of least resistance for doing anything. There is, to put it mildly, insufficient appreciation of the pitfalls of doing this in the Javascript community, both client-side and server-side.
Javascript frameworks make it easy to write web pages with lots of Javascript in them, pages that perform poorly on mobile devices and when networks are slow. They make it difficult to write anything else.
The Real Argument for Javascript
It’s that there is currently, and has historically been, no alternative for client-side scripting. Web Assembly is in the process of ending that, but it has not yet done so. Plus sheer momentum means that Javascript on the client side won’t be going away any time soon.
So there are a lot of workplaces that have teams coding in Javascript for the front end. Probably too many of them, given the current overuse of client-side scripting, but I digress. Javascript is already in use, and wouldn’t it be nice to be able to move people between the front-end and back-end teams as demand for labour requires? Or maybe get rid of the front-end and back-end distinction and just hire for full-stack positions? If all the code was in the same programming language, that would be a whole lot easier.
And, fortunately enough, it just so happens that there are server-side implementations of Javascript, and while they can’t match the performance of compiled languages, they actually perform very respectably for an interpreter. Major win!
And that explains the current popularity of server-side Javascript.
But That Is Not My Situation
I am developing a service that I want to work as well as possible on mobile devices with subpar Internet connections. That means, wherever possible, using plain HTML, eschewing the Ajax pattern (which I consider mostly an antipattern). I don’t need deep Javascript knowledge to do that.
I am not trying to bully people into installing a custom app (which then proceeds to make me money for me by spying on the user) by having my web site be slow and unusable on mobile devices, because I am not a slimy capitalist. I don’t personally have the time to write both and app and a website, so just writing a mobile-friendly website serves my best interest as much it does the user’s. Win/win!
I already know Python well, and Python is squarely within a virtuous cycle of programmers with healthy design patterns attracting other programmers with similarly healthy patterns. The Python standard library is more comprehensive than the one for Node.js. The overall quality of open-source, third-party Python libraries in my experience exceeds that in the Node.js world. Python’s modules and import logic are sane, not a shambolic mess. Python allows event-based programming but doesn’t force it on me.
While Python isn’t the No. 1 back-end web language, it is No. 2 or 3, and over all, for all uses (not just the web), ranks No. 1. As such, there are plenty of Python web frameworks out there. While they are not as performant as Node.js frameworks, they aren’t that far behind, and should do a fair bit of catching up soon.
And so far as recoding if I want better performance, I have written web sites using C# and ASP.NET (currently sitting at the very top of the benchmarks, and no, you don’t need Windows to run it), and I found it an easier technology to adapt to than Javascript (just for openers, there is but a single module and import system, and it behaves predictably). Even old-school Java Server Pages, when built with a simple and predictable build tool like Ant, was less painful. Why should I choose an option that has more pain and less performance?
Node.js just doesn’t seem to make much sense for me.