SF

Migration · .NET · Technical debt

Crossing five .NET versions without rewriting the application

From .NET 5 to .NET 10 on production projects: what an upgrade actually costs, and why waiting costs more.

Over three years I have shipped applications on .NET 5, 6, 8 and 10, and led several upgrades on projects already in production. The most consistent lesson is counter-intuitive: the migration is rarely the problem. The waiting is.

Moving a maintained application up one major version generally takes a few days. The same jump on an application left three versions behind becomes a project in its own right, because breaking changes accumulate and compound, and third-party libraries no longer follow the same trajectory.

The approach that worked best is progressive rather than head-on: raise the target framework first, let compiler warnings drive the work, and handle behavioural changes one at a time instead of rewriting. On a government permit management project, the move from .NET 6 to .NET 8 was done exactly that way across the whole solution, with a direct benefit in reduced build and package restore times.

Entity Framework Core deserves particular caution. It is the dependency where behavioural changes are quietest: a query that compiled and ran suddenly produces different SQL, or shifts from server-side to client-side evaluation. Without integration tests against a real database, that class of regression only shows up in production.

Which is, incidentally, the strongest argument for integration tests against a real database under Docker and Testcontainers: they do not only validate today's feature, they make the next upgrade survivable.

.NET 5 → 10 · EF Core · xUnit · Testcontainers · Docker

Related case study

Getting off MediatR and AutoMapper, in production

Read next

Knowing which customer triggered which errorAutomating hydraulic structure design inside AutoCAD