The Case for C# and .NET
It has been interesting as I’ve shifted out of .NET ecosystem which I’ve worked with on the server side (and some stints of desktop client/tooling work) since the beta versions just before the first release.
To understand why, it is instructive to take a look at GitHub’s State of the Octoverse report.
The Dependency Problem
This model has its benefits as it allows for innovation and creativity at a much faster pace. (In fact, one could argue that it has forced Microsoft to move faster and be more open with .NET Core.)
But the downside of that lack of governance has many, many deficiencies. One of which is an explosion of the dependency chain:
We all know the pain of managing
node_modules. Hundreds of dependencies and often hundreds of megabytes of space. In an ecosystem with strong governance in place, perhaps we would see some of these libraries rolled into a curated and well maintained core set of libraries that is less polluted and less sprawling.
There are even developers out there who “farm” NPM packages to pad their resumes (this library has 180k weekly downloads)!
This in and of itself may seem like a minor annoyance, but this leads to our next problem…
The Security Problem
- 2018’s ESLint attack.
- 2020’s lodash attack.
- The recent ua-parser-js hijack (more on this in this Reddit thread) which is insult on top of injury after CVE-2021-27292.
More surprisingly, according to GitHub, vulnerabilities can often go undetected for extended periods of time:
A vulnerability typically goes undetected for 218 weeks (just over four years) before being disclosed. From there, it typically takes 4.4 weeks for the community to identify and release a fix for the vulnerability, and then 10 weeks to alert on the availability of a security update.
Yikes. This is a legitimate problem that sucks productivity as you scramble to update your dependencies and then end up having to migrate whole codebases to newer, breaking versions of upstream dependencies.
The Performance Problem
While Node.js has an advantage in cold starts, the runtime performance of .NET is among the top 3 in these benchmarks and often 2x faster than Node.js.
From Bui’s 2019 benchmarks:
Filichkin’s 2021 update reveals more or less the same:
The Productivity Problem
Steve McConnell’s Code Complete talks extensively about how important good practices are:
The information contained in a program is denser than the information contained in most books. Whereas you might read and understand a page of a book in a minute or two, most programmers can’t read and understand a naked program listing at anything close to that rate. A program should give more organizational clues than a book, not fewer.
The smaller part of the job of programming is writing a program so that the computer can read it; the larger part is writing it so that other humans can read it.
And yet I find that in the hands of inexperienced developers, TypeScript adds to the mess (particularly indiscriminate use of operations like
The current trend of using arrow functions everywhere is absolutely killing me and I’m not the only one. It makes code unreadable when developers think that
function is apparently a dirty word.
It can actually hamper productivity when the type system is tacked on as an afterthought.
I think the answer to this question is actually quite simple:
- For new developers, it’s easier to learn one language and apply it to the front-end and back-end rather than learn two languages for front- versus back-end development.
- Hot reload is like crack.
Rather than have to teach new developers complex, battle-tested concepts like encapsulation, polymorphism, abstraction, and inheritance for managing complex software projects, let’s just focus on cutting code.
So Why/Not .NET?
I want teams to be productive and not have to waste time dealing with handling dependency vulnerabilities on the daily and funky type behavior caused by layering a type system on top of a language that doesn’t want to be typed. While a loosey-goosey type system is fantastic for building UIs, it’s terrible for building back-ends where strong contracts for system level interactions are desirable.
Prior to .NET Core, the main problem with .NET was that while it purported cross platform runtime compatibility, it depended on third party implementations such as mono in the early days. And of course, because the .NET Framework had underlying dependencies on Win32, it wasn’t truly portable. This had big problems in the modern era of Docker because non-Windows runtimes were treated as second class citizens.
However, as .NET has transitioned to .NET Core and
dotnet (Microsoft really needs to work on their branding), I sense that the tide has been slow to turn back to .NET. Part of this has been the poor branding and poor marketing of .NET. Part of this has been the baggage that Microsoft carries and sometimes surfaces with debacles like the recent one with
dotnet watch. Part of this is that Microsoft just isn’t the cool kid.
I bring this up only because it seems natural, then, to transition developers already comfortable with TypeScript to C#. .NET is also now truly cross platform for building server side applications. My recent CovidCureID project is a perfect example: written in C# on a Windows machine, built and deployed via a Linux runner on GitHub.
It seems that even as .NET itself has grown and improved leaps and bounds, its legacy and limitations prior to .NET Core seem to still hold it back from broader adoption. I even recently saw a job posting by Accenture that shunned “legacy” languages “such as Java and .NET” in favor of Go (Alex Yakunin’s companion article dives deeper into a direct comparison between C#/NET and Go). Why lump .NET with Java?!?