Skip to content
Published on

Modern .NET 2026 — .NET 9 / .NET 10 LTS / Aspire / Blazor United / MAUI / Avalonia 11 / FastEndpoints Deep Dive

Authors

Prologue — "Another enterprise that isn't Java"

May 2026. A senior engineer at a Korean fintech said, half joking: "Lots of people say they don't use .NET. They just don't know their card issuer's backend runs on it." It's a joke, but it carries about 50% truth.

Conference stages still belong to TypeScript, Go, and Rust. But payments, banking, manufacturing, government, and a meaningful slice of game backends run on .NET. And .NET itself has been busy reshaping since the .NET 5 unification in 2020, shipping a fresh major every November.

What happened between 2024 and 2026:

  • .NET 9 (Nov 2024) — STS (Standard Term Support, 18 months). Focused on perf + CLR.
  • .NET 10 (Nov 2025, LTS) — Long Term Support 3 years. The new default.
  • ASP.NET Core 10 — Minimal API matures, OpenAPI built in.
  • Aspire (Nov 2024 GA) — Cloud-native orchestration + telemetry.
  • Blazor United — Server / WASM / SSR / streaming as one model.
  • .NET MAUI — Stabilized. Single C# for iOS/Android/Mac/Win.
  • Avalonia 11 — Truly cross-platform desktop, Linux included.
  • EF Core 9 — JSON, complex types, primitive collections.
  • C# 13 (2024) — params collections, lock object, escape sequences.
  • C# 14 (2025) — field accessor, partial members, null-conditional assignment.

This post lays out a map of .NET in 2026 in one read. Which version do you pick. ASP.NET Core 10 vs FastEndpoints. Do you really need Aspire. MAUI vs Avalonia 11. EF Core vs Dapper. CQRS patterns after MediatR went commercial. The real limits of NativeAOT. And who should pick .NET in the first place.

One thing up front: .NET is not dead. Far from it — in Korean and Japanese enterprise / finance / government systems it is, quietly, more entrenched than ever. It's a tool with a different grain from JVM / Go / Node, and there are domains where that grain fits perfectly.


1. The 2026 .NET map — in one table

AreaDe facto standard in 2026Note
Runtime.NET 10 LTS (2025-11).NET 9 EOL imminent (2026-05)
LanguageC# 13 / C# 14F# 9, VB stable maintained
Web backendASP.NET Core 10 + Minimal APIs+ FastEndpoints / Carter
DataEF Core 9 / Dapper / MartenWide choice
MessagingMassTransit / NServiceBus / WolverineMediatR going paid changed things
CQRSWolverine / Brighter / hand-rolledMediatR 8.x is paid
Cloud orchestration.NET AspireBicep + OTel built in
Web UIBlazor UnitedServer + WASM + SSR
Mobile / desktop.NET MAUIiOS/Android/Mac/Win
Cross-platform desktopAvalonia 11Linux included
GameUnity (.NET 8 based), Stride, MonoGameUnity has its own runtime
DeploymentContainer + NativeAOTself-contained shrinking
PackagesNuGet 6 + Central Package Managementglobal.json standardized
AnalysisRoslyn analyzers + StyleCop + Sonarnullable enforcement

The headline summary:

  • The runtime conversation is settled — .NET 10 LTS. .NET 9 is the short STS you pass through.
  • Web defaults to Minimal APIs, with FastEndpoints / Carter sitting just above as "more structured Minimal API".
  • Aspire became the .NET-native docker-compose + Kubernetes + OpenTelemetry combo.
  • UI split three ways. Web is Blazor United, mobile/desktop is MAUI, truly cross-platform (incl. Linux) is Avalonia 11.
  • CQRS scattered after MediatR went commercial — Wolverine / Brighter / hand-rolled.

2. .NET 10 LTS (2025-11) — The flagship release

.NET 10 went GA in November 2025 as Long Term Support, supported until November 2028. It follows .NET 6 (2021-11) and .NET 8 (2023-11) in the LTS line, with .NET 9 sitting between them as STS.

Key changes:

  • CLR / GC: DATAS (Dynamically Adapting To Application Sizes) GC is now default. 30-40% smaller memory footprint for low-memory services.
  • JIT: PGO (Profile-Guided Optimization) on by default. Devirtualization and inlining get more aggressive.
  • NativeAOT maturity: More framework libraries are AOT-safe. ASP.NET Core minimal API + parts of EF Core are now AOT-capable.
  • JSON: System.Text.Json formally supports polymorphism, snake_case naming, and IAsyncEnumerable streaming.
  • Threading: TimeProvider, PriorityQueue improvements. Async LINQ goes stable.
  • C# 14 shipped alongside: field accessor, partial properties/methods, null-conditional assignment.
  • NuGet 6 + Central Package Management: One Directory.Packages.props file controls the version of every package across the solution.
  • dotnet CLI unification: dotnet run app.cs (single-file run) is officially supported.

LTS doesn't just mean "longer patch window" — it means the ecosystem is most aligned. EF Core 10, ASP.NET Core 10, the Aspire stable channel, MassTransit, the MediatR forks — nearly every major library treats .NET 10 as a first-class target.

Upgrade guidance:

  • .NET 6 (LTS, EOL 2024-11): Move to .NET 10 immediately.
  • .NET 8 (LTS, EOL 2026-11): Move to .NET 10 at your own pace after a compat audit (EF Core, MediatR, etc.).
  • .NET 9 (STS, EOL 2026-05): Move fast — it's about to end.

3. .NET 9 (2024-11) — STS in context

.NET 9 is STS, meaning 18 months of support, which lands in May 2026 — right around when this post is written. .NET 9 served as "the lab notebook for the next LTS, .NET 10".

Features that landed in .NET 9 and got polished in .NET 10:

  • DATAS GC — opt-in in .NET 9, default in .NET 10.
  • System.Text.Json polymorphism — alpha in 9, stable in 10.
  • Built-in OpenAPI — .NET 9 dropped the Swashbuckle dependency in favor of Microsoft.AspNetCore.OpenApi; .NET 10 integrated the UI.
  • Kestrel HTTP/3 — production-ready in .NET 9.
  • NativeAOT expansion — more libraries supported in .NET 9.

Bottom line: teams on .NET 9 must move to .NET 10 by mid-2026. Going back to 8 (LTS) is pointless — .NET 8 itself ends in November 2026.

The LTS / STS rhythm is clear now:

  • LTS — even majors (8, 10, 12...), 3-year support, enterprise default.
  • STS — odd majors (7, 9, 11...), 18 months, faster experiment cycle.

Enterprises typically skip STS releases entirely. The canonical path is .NET 6 → 8 → 10.


4. C# 13 / C# 14 — params collections, field accessor

C# ships a new major every November alongside .NET. C# 13 (2024) and C# 14 (2025) landed with .NET 9 and .NET 10 respectively.

C# 13 highlights:

  • params collectionsparams Span<int>, params IEnumerable<int> and other collection types. No more "params int[] only".
  • dedicated lock typeSystem.Threading.Lock arrives, with monadic-style usage from lock(myLock). Using plain object as a lock now gets flagged by analyzers.
  • new escape sequence \e — ANSI escape (ESC 0x1B). Terminal color codes look cleaner.
  • partial properties — joining partial methods. Source-generator friendly.
  • preview field keyword — direct backing-field access via field inside a getter/setter.

C# 14 highlights:

  • field keyword officiallyfield provides an automatic backing field inside getters/setters. No more explicit backing-field declarations.
  • partial members expanded — constructors, operators, indexers can be partial.
  • null-conditional assignmentobj?.Prop = value skips the assignment when obj is null.
  • extended nameof — works on unbound type arguments too.
  • extension members evolved — extension properties in preview, alongside extension methods.

The field keyword in one shot:

// Old style — explicit backing field
private string _name = string.Empty;
public string Name
{
    get => _name;
    set => _name = value?.Trim() ?? throw new ArgumentNullException();
}

// C# 14 — field keyword
public string Name
{
    get;
    set => field = value?.Trim() ?? throw new ArgumentNullException();
} = string.Empty;

You don't hand-declare _name; the compiler generates it. The _camelCase backing-field naming debate goes away.


5. ASP.NET Core 10 — performance + DX

ASP.NET Core 10 shipped with .NET 10. As of May 2026, the canonical shape of a modern .NET web app:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenApi();
builder.Services.AddDbContext<AppDbContext>(opts =>
    opts.UseNpgsql(builder.Configuration.GetConnectionString("Default")));

var app = builder.Build();

app.MapOpenApi();              // /openapi/v1.json
app.MapScalarApiReference();   // Scalar UI integration

app.MapGet("/users/{id:guid}", async (Guid id, AppDbContext db) =>
{
    var user = await db.Users.FindAsync(id);
    return user is null ? Results.NotFound() : Results.Ok(user);
})
.WithName("GetUser")
.WithOpenApi();

app.MapPost("/users", async (CreateUserDto dto, AppDbContext db) =>
{
    var user = new User(dto.Name, dto.Email);
    db.Users.Add(user);
    await db.SaveChangesAsync();
    return Results.Created($"/users/{user.Id}", user);
});

app.Run();

What's different:

  • OpenAPI built in — Swashbuckle no longer required. Microsoft.AspNetCore.OpenApi is the default.
  • Scalar UI integration — A lighter, cleaner OpenAPI viewer than Swagger UI.
  • JSON streaming — IAsyncEnumerable naturally flows as chunked transfer encoding.
  • HTTP/3 default — Kestrel ships production-ready HTTP/3.
  • Compiled routing — Endpoint dispatch generated at compile time, not via reflection.
  • OpenTelemetry first-classAddOpenTelemetry() is the standard pattern.

Performance numbers (TechEmpower-style microbench):

  • ASP.NET Core 10 Minimal API + Kestrel: 700k+ req/s (Plaintext).
  • ASP.NET Core 10 + EF Core 9 + Postgres: 80-100k req/s (Fortunes).
  • Comparable Go (net/http): 600k req/s.
  • Comparable Node (Fastify): 200k req/s.

In 2026 ASP.NET Core remains one of the fastest mainstream web frameworks.


6. Aspire (Nov 2024 GA) — Cloud-native orchestration

Aspire went GA in November 2024 — .NET's cloud-native orchestration + local dev environment + telemetry integration toolkit. The name is grand, but the essence is simple.

What Aspire solves:

  • Define dependencies in .NET code instead of docker-compose up.
  • One solution that boots several microservices + Postgres + Redis + RabbitMQ.
  • OpenTelemetry auto-injected + dashboard built in.
  • For cloud deploy, automatic Bicep / Terraform / Kubernetes manifest generation.

The AppHost project:

var builder = DistributedApplication.CreateBuilder(args);

var postgres = builder.AddPostgres("db")
    .AddDatabase("appdb");

var redis = builder.AddRedis("cache");

var rabbitmq = builder.AddRabbitMQ("messaging");

var apiService = builder.AddProject<Projects.ApiService>("api")
    .WithReference(postgres)
    .WithReference(redis)
    .WithReference(rabbitmq);

builder.AddProject<Projects.WebFrontend>("web")
    .WithReference(apiService);

builder.Build().Run();

dotnet run on this code:

  1. Postgres / Redis / RabbitMQ containers spin up automatically.
  2. API service and Web Frontend start as .NET processes.
  3. Connection strings are injected as environment variables.
  4. OpenTelemetry data flows into the Aspire Dashboard.
  5. Distributed traces, logs, metrics — all on one screen.

Aspire Dashboard:

  • Distributed trace (waterfall view).
  • Structured logs.
  • Metrics (CPU, memory, request rate, latency).
  • Container / process status.
  • Environment-variable inspector.

Cloud deploy:

  • azd up deploys to Azure Container Apps in one command.
  • Other clouds: Aspire manifest converts to Kubernetes / Bicep / Terraform.

Docker compose vs Aspire:

Criteriondocker-composeAspire
Definition languageYAMLC#
DebuggingAttach to containerDirect .NET process debugging
TelemetrySeparate installBuilt in
Cloud deploySeparate conversionBuilt-in generator
Non-.NET servicesWorks wellWorks well (Postgres, Redis, OS images, etc.)

For teams running .NET microservices, Aspire became the de facto default. In polyglot environments with many non-.NET services, docker-compose still feels natural.


7. Blazor United — Server + WASM + SSR + streaming

Eight years after the 2018 WebAssembly hype, in 2026 Blazor settled into a single model where one component can pick from several render modes. Microsoft's docs call this Blazor United.

Four render modes:

  1. Static SSR — Server renders once. Fastest, no interactivity.
  2. Streaming SSR — Streams async data results via chunked transfer.
  3. Server interactive — SignalR-based, event handling on the server.
  4. WebAssembly interactive — Client WASM, offline-capable.

A single page can mix modes per component.

@page "/dashboard"
@rendermode InteractiveAuto

<h1>Dashboard</h1>
<StaticSummary />
<InteractiveChart />

InteractiveAuto boots fast in Server mode (SignalR) on first visit, then flips to WASM after the WASM payload finishes downloading. A pattern that hides the first-interaction latency.

Blazor strengths:

  • Full-stack C#. SPA without JavaScript (or with it minimized).
  • Integrates with ASP.NET Core — same auth, DI, middleware.
  • SignalR is stable. Bi-directional communication feels natural.
  • WASM AOT builds steadily shrinking initial payload.

Blazor weaknesses:

  • Initial WASM bundle still 1-3MB. Heavy even after compression.
  • Smaller ecosystem than React / Vue.
  • WASM performance on mobile browsers still trails desktop.
  • Designer / component marketplace is thin.

Who uses it: Internal tools, B2B admin, full-stack .NET teams' SPAs. Public marketing or SEO-critical pages stay on Static SSR.


8. .NET MAUI — Cross-platform native

.NET MAUI (Multi-platform App UI) succeeded Xamarin.Forms in 2022 and stabilized between 2024 and 2026. iOS / Android / macOS / Windows from a single C# codebase.

MAUI structure:

  • Single project, single codebase.
  • XAML or C# markup for UI.
  • Per-platform handlers render native controls.
  • Hot reload supported.

MAUI Hybrid + Blazor Hybrid:

A BlazorWebView inside MAUI lets you reuse Razor components as a native app. Blazor UI built for the web ends up nearly as-is in the mobile app.

<BlazorWebView HostPage="wwwroot/index.html">
    <BlazorWebView.RootComponents>
        <RootComponent Selector="#app" ComponentType="{x:Type local:Main}" />
    </BlazorWebView.RootComponents>
</BlazorWebView>

This pattern is attractive when a full-stack .NET team also needs a mobile app. Build/deploy is simpler than React Native, and integration with the .NET backend is more natural than Flutter.

MAUI weaknesses:

  • Doesn't 100% track iOS / Android native SDK evolution.
  • Linux desktop isn't officially supported (community GTK backend exists).
  • Complex animations / perf-critical surfaces still favor native.

2026 recommendation: Internal mobile / Windows desktop. For public app-store consumer apps, compare with Flutter, SwiftUI/Compose, React Native first.


9. Avalonia 11 — A truly cross-platform UI

Avalonia is the community-maintained, .NET Foundation-operated open-source cross-platform UI framework. 11.0 shipped in November 2023, with 11.1 / 11.2 / 11.3 following through 2024-2025.

vs MAUI:

Criterion.NET MAUIAvalonia 11
GovernanceMicrosoftCommunity + commercial (AvaloniaUI company)
PlatformsiOS/Android/Mac/WiniOS/Android/Mac/Win/Linux/WebAssembly
RenderingNative controls per platformOwn renderer (Skia-based)
UI consistencyDiffers per platformIdentical on every platform
Design patternXAML + handlersXAML + own control tree
Hot reloadSupportedSupported
Desktop focusWeakStrong

When Avalonia shines:

  • Teams that genuinely need Linux desktop. (MAUI can't.)
  • Pixel-identical UI across all platforms.
  • Desktop-centric apps (IDEs, graphics tools, audio/video software).

Real-world examples:

  • JetBrains Rider experimented with Avalonia for some UI (not a full adoption).
  • WasabiWallet, Camelot, AvaloniaEdit and other open-source .NET desktop tools.
  • Many enterprise internal tools migrating from WPF to Avalonia — WPF is Windows-only, so reaching Mac/Linux clients drives the move.

Weaknesses:

  • Mobile works but doesn't feel as native. Concede that lane to MAUI.
  • Smaller community control marketplace than WPF.

2026 recommendation: WPF's natural successor. First pick for cross-platform desktop .NET apps that include Linux/Mac.


10. Entity Framework Core 9

EF Core 9 shipped with .NET 9 in November 2024, and EF Core 10 with .NET 10 in November 2025. As of May 2026 the de facto standard is EF Core 10.

Changes over the last 2-3 years:

  • JSON columns officially supported — Postgres jsonb, SQL Server JSON, SQLite JSON. LINQ queries reach inside JSON.
  • Complex types — Owned types but lighter. Modeling value objects feels natural.
  • Primitive collections — Map a List<int> directly to a column.
  • Bulk operationsExecuteUpdateAsync, ExecuteDeleteAsync for N+1-free batch updates.
  • Query splitting default — Prevents JOIN explosion.

Typical code:

public class AppDbContext : DbContext
{
    public DbSet<Order> Orders => Set<Order>();

    protected override void OnModelCreating(ModelBuilder mb)
    {
        mb.Entity<Order>(b =>
        {
            b.ToTable("orders");
            b.ComplexProperty(o => o.ShippingAddress);
            b.OwnsMany(o => o.Items);
            b.Property(o => o.Metadata)
                .HasColumnType("jsonb");
        });
    }
}

// Bulk delete - one SQL statement
await db.Orders
    .Where(o => o.Status == OrderStatus.Cancelled && o.CreatedAt < DateTime.UtcNow.AddYears(-1))
    .ExecuteDeleteAsync();

EF Core vs Dapper vs Marten:

ToolModelStrengthsWeaknesses
EF CoreORM / change trackingRich LINQ, migrations, relationshipsComplex queries hurt SQL readability
DapperMicro-ORMFast, SQL hand-writtenEverything manual
MartenPostgres + event sourcingDocument DB + event storePostgres only
LinqToDBSQL-shape LINQPerformance + LINQ expressivenessLearning curve

The 2026 default is EF Core + Dapper mixed. EF Core for CRUD and domain mutations, Dapper for heavy report / analytics queries.


11. MediatR / MassTransit / NServiceBus — CQRS and messaging

In late 2024 the .NET CQRS scene shook: MediatR moved to a commercial license from 8.x. The same author's AutoMapper introduced a paid edition around the same time. The decision sparked sharp community debate.

Current options:

ToolModelLicenseNote
MediatR 8+In-process mediatorCommercialStable. Costs money.
WolverineIn-process + messagingOSS (MIT)Jeremy D. Miller. CQRS + saga integrated.
BrighterCommand processorOSS (BSD)Messaging-integrated.
Hand-rolledDI + interfacen/aSmall projects do fine.

Wolverine example:

// Handler — discovered by method signature, no interface needed
public class CreateOrderHandler
{
    public async Task<OrderCreated> Handle(CreateOrder command, AppDbContext db)
    {
        var order = new Order(command.CustomerId, command.Items);
        db.Orders.Add(order);
        await db.SaveChangesAsync();
        return new OrderCreated(order.Id);
    }
}

// Call site
var result = await bus.InvokeAsync<OrderCreated>(new CreateOrder(...));

Method-signature discovery without interfaces is the Wolverine signature. MediatR's IRequestHandler boilerplate evaporates.

Messaging (out-of-process):

ToolTransportsLicenseStrengths
MassTransitRabbitMQ, Azure Service Bus, Kafka, etc.OSS (Apache) + some ProMost widely used
NServiceBusVariousCommercialEnterprise. Strong saga story.
WolverineRabbitMQ, Kafka, Azure Service BusOSS (MIT)CQRS + messaging integrated
RebusRabbitMQ, Azure Service Bus, etc.OSSLightweight

MassTransit saga pattern:

public class OrderSaga : MassTransitStateMachine<OrderState>
{
    public State Submitted { get; private set; }
    public State Paid { get; private set; }
    public State Shipped { get; private set; }

    public Event<OrderSubmitted> OnSubmitted { get; private set; }
    public Event<PaymentReceived> OnPaid { get; private set; }

    public OrderSaga()
    {
        InstanceState(x => x.CurrentState);

        Initially(
            When(OnSubmitted)
                .Then(c => c.Saga.OrderId = c.Message.OrderId)
                .TransitionTo(Submitted));

        During(Submitted,
            When(OnPaid)
                .TransitionTo(Paid));
    }
}

2026 recommendations:

  • Simple in-process mediator: Hand-roll or Wolverine.
  • CQRS + messaging together: Wolverine.
  • Enterprise + commercial support: NServiceBus.
  • Already on MediatR + MassTransit: Cost out MediatR 8+ and decide whether to switch.

12. FastEndpoints / Carter / Minimal APIs

ASP.NET Core 10 routing has three flavors now.

1. Minimal APIs (default):

app.MapGet("/orders/{id:guid}", async (Guid id, AppDbContext db) =>
{
    var order = await db.Orders.FindAsync(id);
    return order is null ? Results.NotFound() : Results.Ok(order);
});

Pros: lightweight, fast startup. Cons: Program.cs balloons in a large API.

2. Controllers (classic):

[ApiController]
[Route("orders")]
public class OrdersController : ControllerBase
{
    [HttpGet("{id:guid}")]
    public async Task<IActionResult> Get(Guid id) { ... }
}

Pros: familiar, class-level organization. Cons: lots of boilerplate.

3. FastEndpoints / Carter (structured minimal):

FastEndpoints forces the "endpoint per class" pattern.

public class GetOrderEndpoint : Endpoint<GetOrderRequest, OrderResponse>
{
    public AppDbContext Db { get; set; } = null!;

    public override void Configure()
    {
        Get("/orders/{id:guid}");
        AllowAnonymous();
    }

    public override async Task HandleAsync(GetOrderRequest req, CancellationToken ct)
    {
        var order = await Db.Orders.FindAsync([req.Id], ct);
        if (order is null) { await SendNotFoundAsync(ct); return; }
        await SendAsync(new OrderResponse(order), cancellation: ct);
    }
}

Carter is the spiritual successor to Nancy with module-level routing.

public class OrdersModule : ICarterModule
{
    public void AddRoutes(IEndpointRouteBuilder app)
    {
        app.MapGet("/orders/{id:guid}", async (Guid id, AppDbContext db) =>
        {
            var order = await db.Orders.FindAsync(id);
            return order is null ? Results.NotFound() : Results.Ok(order);
        });
    }
}

Comparison:

CriterionMinimal APIControllersFastEndpointsCarter
BoilerplateVery lowHighLowVery low
OrganizationWeakStrongStrong (per-class)Medium (per-module)
PerformanceFastestSlightly slowerFastest (on Minimal)Fastest (on Minimal)
OpenAPIAutoAutoAuto + richAuto
Learning curveLowLowMediumLow
Large-API friendlyWeakStrongStrongMedium

Recommendation: Below 50 endpoints, Minimal API. Above that, FastEndpoints or Controllers. Carter if you want lightweight module splits.


13. NativeAOT — Small single-file executables

NativeAOT went GA in .NET 7 and matured through .NET 9 and 10. The core idea: ahead-of-time compile to a single native executable instead of relying on the JIT.

Strengths:

  • Startup time: Typically 50-100ms (vs JIT's 200-500ms). Good for cold-start-sensitive workloads (serverless, CLI tools).
  • Memory: 30-40% smaller. No JIT metadata.
  • Single executable: 10-30MB stand-alone binary. No CoreCLR / framework bundled separately.
  • Trimmed binary: Dead code removed.

Limitations:

  • Reflection restricted: Reflection-heavy libraries don't work. Serializers, ORMs, IoC containers must all use source generators.
  • No dynamic code gen: Reflection.Emit doesn't function.
  • No JIT compilation: Loading a new assembly at runtime is impossible.

AOT-friendly libraries:

  • System.Text.Json (with source generator).
  • Minimal APIs / FastEndpoints.
  • Parts of EF Core (8+ progressively).
  • Dapper.
  • Refit (HTTP client).
  • ASP.NET Core RequestDelegateGenerator.

AOT-unfriendly:

  • Some IoC containers (Autofac, Castle Windsor, etc.).
  • Newtonsoft.Json (reflection-dependent).
  • Some MediatR patterns.
  • AutoMapper.

Example — AOT minimal API:

<PropertyGroup>
    <PublishAot>true</PublishAot>
    <InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
dotnet publish -c Release -r linux-x64
# Single native executable -> bin/Release/net10.0/linux-x64/publish/MyApi
# Size: typically 15-25 MB

When to use:

  • AWS Lambda, Cloud Functions and other serverless.
  • CLI tools (alternative to dotnet-tool).
  • Microservices in containers with cold-start sensitivity.
  • Anywhere startup time is business-critical.

When not to use:

  • Legacy code with many reflection-dependent libraries.
  • Development phase where build time / debug ergonomics matter.
  • Monoliths with legacy EF Core / complex IoC containers.

14. Roslyn analyzers / NuGet / dotnet CLI

Roslyn analyzers:

.NET analyzers catch quality / security / performance issues at compile time. The 2026 default set:

  • Built-in .NET analyzers (CA, IDE rules).
  • StyleCop.Analyzers (code style).
  • SonarAnalyzer.CSharp (static analysis).
  • Meziantou.Analyzer (community best practices).
  • Roslynator (refactoring + analysis).
  • SecurityCodeScan (security vulnerabilities).

.editorconfig controls the rules:

[*.cs]
dotnet_diagnostic.CA1822.severity = warning   # mark members as static
dotnet_diagnostic.IDE0005.severity = error    # unused using
dotnet_analyzer_diagnostic.category-Style.severity = suggestion

Nullable enforcement:

<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>

These two lines mark the biggest gap between a modern .NET project and a legacy one. Null safety is enforced by the compiler.

NuGet 6 + Central Package Management:

A single Directory.Packages.props governs every package version in the solution:

<Project>
  <PropertyGroup>
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>
  <ItemGroup>
    <PackageVersion Include="MediatR" Version="8.4.0" />
    <PackageVersion Include="EntityFrameworkCore.Sqlite" Version="10.0.0" />
    <PackageVersion Include="Serilog.AspNetCore" Version="8.0.3" />
  </ItemGroup>
</Project>

Each csproj only writes <PackageReference Include="MediatR" /> without a version. Version conflicts dissolve.

dotnet CLI unified:

# The 2026 baseline workflow
dotnet new aspire-starter -n MyApp        # Create an Aspire solution
dotnet add package Serilog.AspNetCore     # Add a package (CPM writes to Directory.Packages.props)
dotnet ef migrations add InitialCreate    # EF Core migration
dotnet aspire run                         # Run the Aspire app
dotnet test --collect:"XPlat Code Coverage" # Tests + coverage
dotnet format                             # Auto-format
dotnet workload install android ios       # MAUI workloads
dotnet run app.cs                         # Single-file run (.NET 10)

global.json:

Place it at the repo root to pin the SDK version per solution.

{
  "sdk": {
    "version": "10.0.100",
    "rollForward": "latestFeature"
  }
}

That one file removes a whole class of subtle bugs caused by teammates running different SDKs.


15. Korea / Japan — Toss, Nintendo, SoftBank

.NET seems invisible in Korean / Japanese tech press, but it is widely deployed in enterprise / finance / game backends.

Korea:

  • Financial backends: Shinhan, KB Kookmin Card, Hyundai Card — parts of large financial backends ride ASP.NET / .NET 8+. Especially card / debt / wealth-management systems.
  • Public sector: Parts of the government administrative network, parts of the National Tax Service, parts of Korea Post run on .NET. (Security / audit considerations and long-running Microsoft licensing relationships drive this.)
  • Toss: Mostly JVM / Kotlin / Node.js, but reports of internal admin / tooling on .NET. Rarely surfaces at public conferences.
  • NCSOFT, Nexon, Pearl Abyss: Game clients are Unity / Unreal, but some backends / internal tooling are .NET. Unity uses its own .NET runtime but the same C# codebase.
  • Samsung SDS, LG CNS: SI projects still pick .NET often.

Japan:

  • Nintendo: .NET in some internal tooling / backends. Game engines are proprietary, but admin / management / online services pieces use it.
  • SoftBank: .NET in mobile-division backends. Telecom infrastructure management tools have public talks mentioning .NET.
  • Rakuten: Java / Python dominate, but ads / analytics systems include .NET.
  • NTT Data / Fujitsu: Japanese SI giants run many .NET-based government / financial projects.
  • Cybozu: kintone services and internal tooling have reported .NET usage.
  • Mercari: Largely Go backends, but payment / settlement uses some .NET.

Summary: in both Korea and Japan, .NET is a de facto standard option in enterprise / finance / telecom / government domains. Less visible on conference stages, but plenty present by revenue and system scale.


16. Who should pick .NET

Pick .NET when:

  • Enterprise / finance / government domains. Natural integration with the Microsoft ecosystem (SQL Server, Azure AD, Office). Long-lived LTS and audit friendliness.
  • Full-stack C# teams. Blazor + ASP.NET Core + MAUI unify web/mobile/desktop in one language.
  • High-throughput web backends. ASP.NET Core 10 + Minimal APIs approach Go-level throughput.
  • Windows desktop / .NET-integrated tools. Choose between WPF, WinForms, MAUI, Avalonia.
  • Unity game dev. One C# language unifies client and server.
  • AWS Lambda / Cloud Functions workloads (NativeAOT). Strong cold-start.

Avoid .NET when:

  • PaaS / serverless-heavy environments favoring JS / Python. Vercel / Cloudflare Workers and similar.
  • Data science / ML-first teams. The Python ecosystem dominates.
  • Mobile-first (social / consumer apps). SwiftUI / Compose / React Native / Flutter feel more natural.
  • Organizations with deep JVM library investment. Kafka, Spark, Hadoop, Flink as first-class — JVM is the natural home.
  • Extreme systems programming. Rust / C++ / Go fit better.

Decision tree (simplified):

Q1. Domain?
├── Enterprise / finance / government / insurance / Windows integration
│   └── .NET 10 + ASP.NET Core 10 + EF Core 10
├── Public consumer web / SEO-first
│   └── Consider Next.js / Remix / SvelteKit first
├── Game (Unity)
│   └── C# (.NET-compatible runtime)
└── Mobile app (consumer)
    └── Native or Flutter / RN first

Q2. Team language?
├── Strong C# -> .NET
├── Strong Java/Kotlin -> Spring / Quarkus
├── Strong TS/JS -> Node / Deno
└── Strong Python -> FastAPI / Django

Q3. Deployment?
├── Azure / on-prem Windows -> .NET feels natural
├── AWS / GCP / K8s -> .NET works, lean on OpenTelemetry and Aspire
└── Vercel / Cloudflare / Edge -> .NET is a poor fit

One-line recommendations (as of May 2026):

  • New enterprise / finance / telecom / government systems: .NET 10 LTS + ASP.NET Core 10 + EF Core 10 + Aspire.
  • Internal tools / admin full-stack: Blazor United.
  • Mobile as an extra burden for the .NET team: MAUI + Blazor Hybrid.
  • True cross-platform desktop: Avalonia 11.
  • Small API: Minimal APIs. Above 50 endpoints: FastEndpoints.
  • CQRS: Wolverine, messaging: MassTransit.
  • Single executable / cold-start first: NativeAOT.

The most important meta-recommendation: there are domains where not picking .NET is the rational choice. But before you assume you're in one of them, it's worth measuring how far modern .NET has come for yourself. The .NET of 2026 is nearly a different tool from the .NET of 2018.


References