Most organisations running .NET Framework applications are not running bad software. They are running software that works, carries years of business rules nobody wrote down, and is increasingly expensive to host, hire for and secure. The temptation is a rewrite. The better answer, almost always, is a deliberate migration.

We have modernised systems across every generation of .NET, from Framework 2.0 to .NET 10. This is the path we follow.

Step 1: Know what you are actually migrating

Before changing a line of code, build an inventory:

  • Project types. Class libraries usually move easily. ASP.NET Web Forms, WCF services and Windows Forms or WPF clients each need their own strategy.
  • Platform dependencies. Search for anything Windows-only: the registry, WMI, MSMQ, Windows event logs, DPAPI, COM interop, file paths with drive letters and Windows authentication assumptions.
  • Third-party packages. Check each for a modern .NET version. Abandoned packages are often the real critical path.
  • Behaviour. Identify the business-critical flows and capture how they behave today with characterisation tests. These tests are your safety net for everything that follows.

Step 2: Isolate Windows dependencies behind adapters

This is the step that makes the rest safe. Introduce a thin platform adapter layer: interfaces for configuration, messaging, logging, secrets and system information, with the current Windows implementations behind them.

Then replace each implementation with a cross-platform equivalent:

Windows dependency Cross-platform direction
MSMQ A managed broker such as Azure Service Bus, RabbitMQ or Kafka
Windows event log Structured logging through ILogger and OpenTelemetry
DPAPI-protected secrets ASP.NET Core Data Protection with keys in a managed vault, or the cloud secret manager
WMI system queries Platform-neutral health and metrics endpoints
Registry and web.config settings IConfiguration with environment variables, ConfigMaps and secret stores
Server-side WCF CoreWCF for compatibility, or gRPC and REST for new contracts

Because the application now talks to interfaces, each replacement is small, testable and reversible.

Step 3: Move to modern .NET incrementally

With dependencies isolated, retarget libraries first, then services, then the host. .NET 10 is a long-term support release, which makes it the sensible target for anything that will live for years.

For large ASP.NET applications, avoid a big-bang switch. Put a reverse proxy in front of the existing application and move routes one at a time to a new ASP.NET Core application, a strangler-fig migration. Microsoft’s System.Web adapters help shared code run on both sides while the migration is in progress.

Data access deserves its own attention. Moving from Entity Framework 6 to EF Core, or adopting PostgreSQL alongside SQL Server through Npgsql, changes query behaviour in subtle ways. Compare results, not just compilation.

Step 4: Containerise and deploy where it makes sense

Once the application runs on Linux, it can be containerised and deployed to AKS, GKE, EKS or a self-managed Kubernetes cluster:

  • Build small, non-root images and scan them in the pipeline.
  • Move configuration to ConfigMaps and secrets to a secret manager, not image layers.
  • Add OpenTelemetry tracing, metrics and logs from day one, so the new system is more observable than the old one ever was.
  • Use Kustomize or Helm so each environment is described as code.

Step 5: Decompose only where it pays

Microservices are a tool, not a goal. After the platform migration, look for boundaries where independent deployment or scaling earns its operational cost: a high-traffic API, a slow batch process, a module owned by a separate team. Use domain-driven design to find those seams, and patterns such as the outbox and sagas to keep data consistent across them. Much of the system may be better as a well-structured modular monolith.

Step 6: Prove it

Run old and new side by side where you can. Compare outputs for the same inputs, monitor error rates and latency, and keep the characterisation tests running in the pipeline. The goal is a migration that business users do not notice, except that things get faster.

What you gain

  • Lower hosting cost, because Linux containers are cheaper to run than Windows servers and scale more precisely.
  • Better security, because modern .NET receives current security features and the platform can be patched and scanned like any other workload.
  • Easier hiring, because engineers want to work on current technology.
  • Business rules, finally documented, because the characterisation tests describe what the system really does.

Getting help

We lead modernisation programmes end to end, from inventory and architecture to migration and handover. See our microservice migration and cloud modernisation service, our solution architecture practice, or contact us to talk through your system.

  • .NET 10
  • Legacy modernisation
  • Kubernetes
  • Microservices
  • Cloud migration

Keep reading

More from Insights

Have a system to build, modernise or secure?

Tell us where things stand today. The first conversation is free, and you will leave it with an honest view of the work involved.