Skip to main content
Test Double company logo
Services
Services Overview
Holistic software investment consulting
Software Delivery
Accelerate quality software development
Product Management
Launch modern product orgs
Legacy Modernization
Renovate legacy software systems
DevOps
Scale infrastructure smoothly
Upgrade Rails
Update Rails versions seamlessly
Technical Recruitment
Build tech & product teams
Technical Assessments
Uncover root causes & improvements
Case Studies
Solutions
Accelerate Quality Software
Software Delivery, DevOps, & Product Delivery
Maximize Software Investments
Product Performance, Product Scaling, & Technical Assessments
Future-Proof Innovative Software
Legacy Modernization, Product Transformation, Upgrade Rails, Technical Recruitment
About
About
What's a test double?
Approach
Meeting you where you are
Founder's Story
The origin of our mission
Culture
Culture & Careers
Double Agents decoded
Great Causes
Great code for great causes
EDI
Equity, diversity & inclusion
Insights
All Insights
Hot takes and tips for all things software
Leadership
Bold opinions and insights for tech leaders
Developer
Essential coding tutorials and tools
Product Manager
Practical advice for real-world challenges
Say Hello
Test Double logo
Menu
Services
BackGrid of dots icon
Services Overview
Holistic software investment consulting
Software Delivery
Accelerate quality software development
Product Management
Launch modern product orgs
Legacy Modernization
Renovate legacy software systems
Cycle icon
DevOps
Scale infrastructure smoothly
Upgrade Rails
Update Rails versions seamlessly
Technical Recruitment
Build tech & product teams
Technical Assessments
Uncover root causes & improvements
Case Studies
Solutions
Solutions
Accelerate Quality Software
Software Delivery, DevOps, & Product Delivery
Maximize Software Investments
Product Performance, Product Scaling, & Technical Assessments
Future-Proof Innovative Software
Legacy Modernization, Product Transformation, Upgrade Rails, Technical Recruitment
About
About
About
What's a test double?
Approach
Meeting you where you are
Founder's Story
The origin of our mission
Culture
Culture
Culture & Careers
Double Agents decoded
Great Causes
Great code for great causes
EDI
Equity, diversity & inclusion
Insights
Insights
All Insights
Hot takes and tips for all things software
Leadership
Bold opinions and insights for tech leaders
Developer
Essential coding tutorials and tools
Product Manager
Practical advice for real-world challenges
Say hello
Developers
Developers
Developers
Software tooling & tips

C# and .NET tools and libraries for the modern developer

C# has a reputation for being used in legacy projects and is not often talked about related to startups or other new business ventures. This article aims to break a few of the myths about .NET and C# and discuss how it has evolved to be a great fit for almost any kind of software.
Patrick Coakley
|
August 20, 2025
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Introduction

It’s been over 23 years since the first release of .NET and C# hit the scene. And in that time these technologies have been the backbone of many successful organizations. With that success, however, comes a certain reputation: C# is only used for enterprise software, it only runs on Windows, and it has to use Microsoft SQL Server.

Things have changed significantly over the last decade. C# should now be considered a major competitor as a multi-platform and high-performance programming language. It should also be noted that a lot of what I write will also apply to F# as it has functional equivalency with C#'s target platforms.

The journey to multi-platform

It is true that .NET started as, and was always intended to be, a Windows-specific application framework. That includes the ability to run on Internet Information Services (IIS) with Microsoft SQL Server (MS SQL) or as native desktop applications using Windows Forms (WinForms). Seen as a competitor to Java, C# runs on top of the Common Language Runtime (CLR), the equivalent of the Java Virtual Machine (JVM). The CLR and JVM have many similarities, including their use of garbage collection (GC) for memory management, runtime exceptions for error handling, an intermediate language to allow for easier platform abstraction, and Just-In-Time (JIT) compilation over Ahead-Of-Time (AOT) compilation as the default build and distribution process.

One key difference, though, was the fact that the JVM could run on almost any major operating system within a few years of Java's original 1996 release, including various flavors of UNIX (Solaris, IRIX, and HP-UX), Linux, Mac OS, and Windows. It is worth noting that the Common Language Infrastructure (CLI) is an Ecma standard of the various components that make up CLR, including the runtime. Thiswas released around the time of the initial release of the original version of.NET, so it has always technically been an open standard. 

Still, C# could only really run on Windows platforms. And Microsoft was not particularly interested in anything else at the time of the original release. Instead the primary focus was to onboard C and C++ developers onto C# as fast as possible. 

The advent of the Mono Project, a fully open-sourced implementation of .NET, has been crucial in making multi-platform C# a reality. Originally started as a Linux equivalent of .NET, it has morphed into something that has been critical in porting C# applications to desktop and mobile platforms. Interestingly enough, Mono’s development with the same core developers had persisted through multiple company creations, acquisitions, and layoffs, and eventually made its way into Microsoft when it purchased Xamarin in 2016. 

As time went on, and the first signs of what we consider to be cloud computing emerged, technology organizations had already started to standardize around deploying to Linux over commercial UNIX offerings and Windows. This was due to it being "good enough" and free with the ability to customize it for ease of deployments. 

Microsoft had both openly and internally been fighting against the spread of Linux. But by the mid 2010s it made an unprecedented move to open-source .NET with a newly re-written version called .NET Core. This would include ASP.NET support on Mac, Linux, and Windows, allowing Windows developers to work on their machines and deploy to Linux without ever having to use another operating system. .NET Core officially displaced .NET Framework as the standard .NET with the release of .NET 5, finally bridging the gap between the old and new release cycles.

Where is C# being used today?

Web development

While C# started out as an application-level programming language for desktop and the web, it has evolved into a truly general-purpose programming language that can run in a number of different spaces. That being said, web development with C# has evolved significantly since the original ASP.NET was released.

While you can still write web applications using some of the more traditional methods—including the classic MVC style of project and standard controller-based REST APIs—there are now a few more options that let you write full-stack as well as cloud-native applications in C#, including Docker support out of the box when you create a new project.

Razor Pages are a more streamlined approach to something similar to MVC, and is a great choice for putting together applications that are more about content than interactivity, such as internal business software or a CMS. 

Blazor, which takes the Razor syntax and puts it together into a C# SPA framework influenced by React and Angular, lets you write interactive web applications using components and shared code between the front-end and back-end.

There are even cloud-native project templates, including gRPC microservices and worker services, meant to be used for long-running tasks that aren't necessarily going to be tied to a particular web service.

The latest ASP.NET project, .NET Aspire, takes a lot of the standard technologies you would need to deploy a distributed web application and packages them together in one easy to use starter that integrates many of the core pieces you need—including caches, databases, messaging services, and storage systems. Aspire marks the first time Microsoft has really developed and pushed an opinionated way to write web applications in .NET, and it works with existing software by integrating all of the various types of ASP.NET projects together, not replacing them.

Native applications

While the web has historically been the primary target of .NET, a close second would be creating desktop applications using WinForms and WPF.

While both of these are still available to use, including a modern, cross-platform replacement for WPF with the open-source Avalonia project, .NET MAUI allows for .NET developers to create multi-platform desktop and mobile applications in a single framework.

In addition, there is also nanoFramework and Meadow for Internet of Things (IoT) projects. These frameworks have different targets, with the former being more focused on smaller, more constrained microcontrollers, while the latter works on beefier microcontrollers and even single-board-computers like the Raspberry Pi.

Game development

Finally, one of the most prominent use-cases today is in game development. Unity and Godot, two fairly popular game engines, both fully support C#.

There are also frameworks like MonoGame, which is actually written in C# and offers developers a code-first approach to game development.

One of the most interesting aspects to come from this is that these projects can pull in any number of existing NuGet packages to augment their project in ways outside of what the game engine itself can provide. It also lets you potentially write all of your core game logic, authentication servers, and multi-player networking code all in one language and project. Game developers can also leverage engine-agnostic libraries for things like entity-component-system frameworks or multiplayer servers without having to be tied to a single engine.

This breadth makes C# a very powerful choice when it comes to choosing a programming language for a new project. It not only compares favorably with the competition, it also provides a wide dynamic range that lets you work with a high-level language while giving you the tools to dig into the low-level details for extra performance or portability concerns.

What does modern .NET look like?

Nowadays, when you look at the job market for C# developers you will often see the same listings for working on legacy .NET Framework projects targeting Windows, IIS, and MS SQL.

A lot of organizations have not and may not in the near future move on to .NET 5+, but tools like the Upgrade Assistant or LLMs can help reduce the burden and costs associated with a large migration. In fact, since .NET leverages Solutions and Projects as ways to compartmentalize code, a standard way to tackle this problem is to work your way one-by-one through each project and isolate legacy code that can't be easily upgraded. Instead of re-writing your entire codebase from scratch, a much more pragmatic approach is to maintain your engineering organization's knowledge of C# and gain most of the benefits languages have had over it in the past.

If you haven't touched C# in a long time, the language has evolved significantly since about 2016, bringing in a number of great features to make it more expressive and type-safe than ever. Some of the newer language features include pattern matching, nullable types, using declarations for resource disposal, async streams, and collection expressions, to name just a few. The NuGet ecosystem is also incredibly mature, with over 400,000 different packages that cover a wide range of use cases, with many of the more popular ones being used in business-critical software for large Fortune 500 companies.

Consider a real-world example: building a cross-platform CLI tool that has fast startup times and zero dependencies. Traditionally, when making a CLI tool that you want to use on multiple platforms, you'd either need to use a scripting language like Python or Perl, or you would reach for something like Go or Rust to be able to distribute a single static binary. However, modern .NET makes C# equally viable for this scenario. NativeAOT compilation in .NET allows you to accomplish the goal of having cross-platform self-contained binaries with faster startup times than a standard JIT-compiled C# application. There are a lot of knobs to fiddle with in the project settings to fine-tune the size and performance of your application, so there is a good amount of flexibility to get your desired result.

When I started to develop my Godot version manager called gdvm, my primary goal was to take advantage of NativeAOT to see what it was capable of. I was able to create a fully-crossplatform application that clocks in at roughly 10MB. While this is not as small as something Rust or Go would output, it is still very reasonable in comparison and saves the user from needing to have the .NET runtime installed on their computer. Using NativeAOT gives gdvm a solid ~44ms startup time. This puts it in a nice place compared to Python and Ruby, which are roughly 1.7x-2x slower. 

That being said, in comparison to asdf and mise, other version management tools written in Go and Rust, respectively, gdvm is still in the double-digits versus their sub-10ms startup. There is ongoing work to improve NativeAOT in future .NET releases. And there are changes in my application architecture and dependencies that would help get that number lower, as I am currently focused on having a smaller binary size over a faster startup. Still, I think this is an impressive feat considering what the situation was for .NET applications only 5 years ago, and it’s only going to get better.

Beyond NativeAOT, modern C# features can also significantly improve the software development experience. Some of these include:

  • record structs for immutable value types
  • extensive use of pattern matching with switch expressions for cleaner platform compatibility and versioning comparisons
  • collection expressions with concise syntax comparable to JavaScript and Python
  • primary constructors to eliminate boilerplate and create a cleaner entry point for dependency injection

 I also enjoy using LINQ, which enables you to express complex queries in a type-safe manner using a clean, fluent API, turning what would be nested loops and temporary variables into readable, chainable operations.

Conclusion

If you are trying to pick a tech stack for a new project, definitely give .NET a look. It has come a long way from where it started, and  the open source efforts have helped shape it into a way to deliver software to multiple platforms all in the same languages and frameworks without sacrificing speed or choice of libraries. It is battle-tested, with over two decades of production software, cross-platform, running multiple desktop, mobile, embedded, and web environments,  and is constantly evolving and improving.

‍

Related Insights

🔗
How modern frontend teams approach automated testing
🔗
Breaking up (with) your test suite
🔗
How to stop hating your tests

Explore our insights

See all insights
Leadership
Leadership
Leadership
Turning observability into a team strength without a big overhaul

By addressing observability pain points one at a time, we built systems and practices that support rapid troubleshooting and collaboration.

by
Gabriel Côté-Carrier
Developers
Developers
Developers
Why I actually enjoy PR reviews (and you should, too)

PR reviews don't have to be painful. Discover practical, evidence-based approaches that turn code reviews into team-building opportunities while maintaining quality and reducing development friction.

by
Robert Komaromi
Developers
Developers
Developers
Build with HTMX: Simplify development with a return to fundamentals

Modern web development embraced complexity with frameworks like React, but at what cost? HTMX is a lightweight, progressively enhanced alternative, embraces web fundamentals, and reduces dependency overhead. Through a side-by-side comparison of identical applications built with React and HTMX, this screencast and blog explores the benefits of a hypermedia-first approach and when it might be the right choice for your projects.

by
Dave Mosher
Letter art spelling out NEAT

Join the conversation

Technology is a means to an end: answers to very human questions. That’s why we created a community for developers and product managers.

Explore the community
Test Double Executive Leadership Team

Learn about our team

Like what we have to say about building great software and great teams?

Get to know us
Test Double company logo
Improving the way the world builds software.
What we do
Services OverviewSoftware DeliveryProduct ManagementLegacy ModernizationDevOpsUpgrade RailsTechnical RecruitmentTechnical Assessments
Who WE ARE
About UsCulture & CareersGreat CausesEDIOur TeamContact UsNews & AwardsN.E.A.T.
Resources
Case StudiesAll InsightsLeadership InsightsDeveloper InsightsProduct InsightsPairing & Office Hours
NEWSLETTER
Sign up hear about our latest innovations.
Your email has been added!
Oops! Something went wrong while submitting the form.
Standard Ruby badge
614.349.4279hello@testdouble.com
Privacy Policy
© 2020 Test Double. All Rights Reserved.