.NET applications rarely become difficult to migrate because of the target framework alone.
The real challenge is usually the application ecosystem around it:
For teams running .NET 8 or .NET 9 applications, the support lifecycle creates a concrete planning requirement.
Microsoft currently lists both .NET 8 and .NET 9 as supported until November 10, 2026. .NET 8 is a Long Term Support release, while .NET 9 is a Standard Term Support release. .NET 10 is the current LTS target for teams planning a longer support window.
That means migration should not be treated as a last-minute framework upgrade.
A production migration is an application lifecycle project.
Why Migration Planning Should Start Early
A framework migration normally affects more than one project file.
Consider a typical solution:
MyCompany.sln
|
+-- Web
+-- API
+-- Application
+-- Domain
+-- Infrastructure
+-- Tests
+-- Worker
+-- Shared
Changing:
<TargetFramework>net8.0</TargetFramework>
to:
<TargetFramework>net10.0</TargetFramework>
may be technically simple.
The difficult part is determining whether all the surrounding components are compatible.
For example:
.NET Runtime
|
+-- ASP.NET Core
+-- EF Core
+-- Authentication
+-- OpenTelemetry
+-- Database Provider
+-- Cloud SDK
+-- NuGet Packages
+-- Container Image
+-- CI/CD
A migration is complete only when the application and its deployment environment have been validated.
Understand the Support Timeline
The first step is to inventory the current runtime.
| Runtime | Release Type | Support End |
|---|
| .NET 8 | LTS | November 10, 2026 |
| .NET 9 | STS | November 10, 2026 |
| .NET 10 | LTS | November 14, 2028 |
Microsoft's official lifecycle documentation should be treated as the authoritative source when planning an upgrade because support dates can affect security patch availability and organizational compliance requirements.
The practical implication is straightforward:
If an application must remain on a supported runtime after November 10, 2026, the migration should be completed and validated before that date.
Choose the Target Framework
For teams moving from .NET 8 or .NET 9, .NET 10 is an obvious target when the goal is a longer support window.
The decision should nevertheless consider:
Application compatibility
Vendor support
Cloud platform support
Required third-party libraries
Internal release schedules
Testing capacity
Deployment constraints
Avoid choosing a target solely because it is the newest runtime.
The target must be supported across the entire application ecosystem.
Inventory the Existing Application
Before changing anything, create an inventory.
Start with the project files:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
</Project>
Identify:
For a solution with multiple projects, do not assume every project targets the same framework.
You may find:
Web -> net8.0
Worker -> net8.0
Legacy -> net6.0
Tests -> net8.0
Tooling -> net9.0
The migration plan needs to account for all of them.
Check the Installed SDKs
Use:
dotnet --list-sdks
and:
dotnet --list-runtimes
Then check the active SDK:
dotnet --version
This is especially important when developers have multiple SDKs installed.
A local machine may build the application successfully using an SDK that is not available in CI.
Pin the SDK Used by the Repository
A global.json file can make SDK selection explicit.
For example:
{
"sdk": {
"version": "10.0.100",
"rollForward": "latestPatch"
}
}
The exact SDK version should match the version approved by your build environment.
The important principle is consistency:
Developer
|
v
Same SDK
|
v
CI
|
v
Same SDK
|
v
Release Build
Without consistent SDK selection, migration failures can become environment-specific.
Update the Target Framework
Once the inventory is complete, update the project:
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
For multi-targeted projects:
<PropertyGroup>
<TargetFrameworks>net8.0;net10.0</TargetFrameworks>
</PropertyGroup>
Multi-targeting can be useful during a transition because it allows the application or library to validate compatibility incrementally.
However, do not keep an old target indefinitely simply because multi-targeting makes the migration easier.
The eventual objective should be clear.
Update NuGet Packages
Framework upgrades often expose package compatibility issues.
Start by listing outdated packages:
dotnet list package --outdated
Depending on the SDK version and tooling available in your environment, the exact command output can vary.
Review each package rather than blindly upgrading everything.
Important packages may include:
Microsoft.AspNetCore.*
Microsoft.EntityFrameworkCore.*
Microsoft.Extensions.*
Azure.*
OpenTelemetry.*
Authentication libraries
Database providers
Serialization libraries
Testing frameworks
A major package upgrade can introduce behavioral changes unrelated to the framework migration.
Keep changes understandable.
Check Package Compatibility
For each important dependency, verify:
Current version
|
v
Supported target framework
|
v
Target version
|
v
Breaking changes
A package that compiles under .NET 10 is not necessarily behaviorally compatible with your application.
Pay particular attention to:
Authentication
Database providers
Serialization
Logging
Observability
Native libraries
Cloud SDKs
Upgrade ASP.NET Core Applications Carefully
ASP.NET Core applications often depend on framework behavior across the entire HTTP pipeline.
Review:
Middleware
Routing
Authentication
Authorization
Dependency Injection
Configuration
Logging
Exception handling
Endpoints
Static files
For example:
var builder =
WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Do not assume that a successful compilation proves the pipeline is unchanged.
Run integration tests that exercise authentication, authorization, routing, and error handling.
Review Entity Framework Core
If the application uses EF Core, treat the framework and EF Core upgrade as related but separately testable changes.
Review:
DbContext configuration
Migrations
LINQ queries
Database provider
Transactions
Concurrency
Query behavior
Generated SQL
Run the existing migration tests against a realistic database environment.
For important queries, compare behavior before and after the migration.
A query that returns the same records can still have different performance characteristics if the provider or generated SQL changes.
Review Authentication and Authorization
Authentication should receive explicit regression testing.
Test:
Login
Token validation
Cookie authentication
Refresh tokens
Role claims
Policy authorization
Expired credentials
Invalid credentials
Unauthorized requests
Forbidden requests
A migration should never be considered successful simply because the home page loads.
Security-sensitive functionality needs dedicated tests.
Update Container Images
Containerized applications have another dependency:
FROM mcr.microsoft.com/dotnet/aspnet:8.0
When moving to .NET 10, the base image should be reviewed:
FROM mcr.microsoft.com/dotnet/aspnet:10.0
The build image also needs to change:
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
Then rebuild the image from scratch.
For example:
docker build --no-cache -t myapp:upgrade-test .
Do not assume that updating the project file automatically updates the runtime inside the production container.
Review Self-Contained and NativeAOT Deployments
Some applications are deployed differently from framework-dependent ASP.NET Core applications.
Check whether the application uses:
Framework-dependent
Self-contained
Single-file
ReadyToRun
NativeAOT
Each deployment model can have different compatibility considerations.
If native dependencies exist, test them explicitly.
For example:
Application
|
+-- Native library
+-- Database driver
+-- OS dependency
A framework upgrade can expose compatibility issues that do not appear in a simple managed-code test.
Update CI/CD Pipelines
A common migration failure occurs when the application targets the new runtime but the pipeline still uses an older SDK.
For example:
- task: UseDotNet@2
inputs:
packageType: sdk
version: '10.0.x'
The exact syntax depends on your CI/CD platform.
Verify all pipeline stages:
Build
|
v
Unit Tests
|
v
Integration Tests
|
v
Security Scan
|
v
Container Build
|
v
Deployment
Every stage should use a supported and intentional SDK/runtime configuration.
Test Production Configuration
A migration should be tested with production-like configuration.
That includes:
Do not put real production secrets into a test environment merely to make it realistic.
Use controlled test credentials and representative configuration.
Performance Testing
Do not assume a runtime migration automatically improves performance.
Measure the application before and after.
Useful metrics include:
| Metric | Why Measure It |
|---|
| Requests/sec | Throughput |
| p50 latency | Typical response |
| p95 latency | Tail behavior |
| p99 latency | High tail behavior |
| CPU | Resource consumption |
| Memory | Working set |
| Allocations | GC pressure |
| GC pauses | Runtime behavior |
| Database latency | Downstream impact |
The comparison should use the same workload and infrastructure.
A migration should not be declared faster because a developer machine produced a lower response time during one manual test.
Regression Testing
At minimum, test:
Unit Tests
Integration Tests
API Tests
UI Tests
Database Tests
Authentication Tests
Authorization Tests
Background Jobs
Messaging
Scheduled Tasks
Health Checks
For public APIs, also test response contracts.
For example:
{
"id": 1001,
"name": "Customer"
}
Verify that important consumers still receive the expected structure.
Common Migration Mistakes
Changing Only TargetFramework
Changing:
<TargetFramework>net8.0</TargetFramework>
to:
<TargetFramework>net10.0</TargetFramework>
is only the beginning.
Upgrading Every Package at Once
This creates a large change surface.
Separate framework migration from unrelated dependency upgrades where practical.
Ignoring Containers
The application may compile for .NET 10 while production still uses a .NET 8 runtime image.
Forgetting CI
A developer machine can have the required SDK while the build agent does not.
Skipping Integration Tests
Compilation does not validate runtime behavior.
Migrating Just Before the Deadline
A migration needs time for unexpected dependencies, vendor issues, testing failures, and rollout problems.
A Safer Migration Strategy
A staged approach reduces risk.
Phase 1: Assessment
Document:
Current runtime
SDK
Packages
Container images
Cloud services
Native dependencies
CI/CD
Phase 2: Compatibility
Upgrade critical dependencies and identify blockers.
Phase 3: Development Migration
Change the target framework and fix compilation issues.
Phase 4: Automated Testing
Run unit, integration, security, and API tests.
Phase 5: Performance Validation
Compare the new runtime against the existing production baseline.
Phase 6: Staging
Deploy the application to a production-like environment.
Phase 7: Canary or Controlled Rollout
Start with a limited percentage of traffic where the platform supports it.
Phase 8: Full Deployment
Expand the rollout after monitoring the required health signals.
Example Migration Checklist
[ ] Confirm current .NET version
[ ] Confirm support lifecycle
[ ] Choose target runtime
[ ] Inventory projects
[ ] Inventory NuGet packages
[ ] Review native dependencies
[ ] Update global.json
[ ] Update TargetFramework
[ ] Update packages
[ ] Update Docker images
[ ] Update CI/CD SDK
[ ] Run unit tests
[ ] Run integration tests
[ ] Run security tests
[ ] Run performance tests
[ ] Validate authentication
[ ] Validate database operations
[ ] Deploy to staging
[ ] Validate observability
[ ] Perform controlled production rollout
[ ] Remove obsolete runtime dependencies
Handling Applications That Cannot Migrate in Time
Some applications cannot be upgraded immediately.
Possible reasons include:
Unsupported third-party libraries
Vendor dependencies
Legacy native components
Internal compliance requirements
Large monolithic architectures
Limited testing coverage
Do not simply leave the application unchanged without documenting the risk.
Instead, create an explicit exception plan.
Document:
Application
Current runtime
Reason migration is blocked
Security impact
Owner
Temporary controls
Target migration date
The exact remediation strategy depends on organizational requirements.
Migration vs Rewrite
A runtime migration does not normally require rewriting the application.
A good first objective is:
Existing application
|
v
Supported target runtime
rather than:
Existing application
|
v
Completely new architecture
Combining a runtime upgrade with a major architectural rewrite dramatically increases the change surface.
Separate those projects unless there is a compelling reason to combine them.
Troubleshooting Migration Problems
Build Fails After Target Framework Change
Start with the first compiler error.
Check:
Do not fix dozens of downstream errors before addressing the first meaningful failure.
Application Builds but Fails at Runtime
Look at:
Startup configuration
Dependency injection
Authentication
Database provider
Native dependencies
Configuration binding
Run integration tests before deploying.
Container Starts but Health Checks Fail
Verify:
Runtime image
Port
Environment variables
Certificates
Database connectivity
Health-check endpoint
Performance Changes Unexpectedly
Compare the same workload under both runtimes.
Check:
CPU
Memory
GC
Database
Network
Threading
Serialization
Do not assume the runtime itself is responsible until profiling supports that conclusion.
Frequently Asked Questions
Do .NET 8 and .NET 9 stop working after November 10, 2026?
No. End of support does not normally mean that applications suddenly stop executing.
It means the Microsoft support lifecycle ends, including the normal servicing and security-update expectations associated with a supported release. Microsoft lists November 10, 2026 as the support end date for both .NET 8 and .NET 9.
Should a .NET 8 application move to .NET 10?
For teams that need a longer supported lifecycle, .NET 10 is a logical target because it is an LTS release. The application should still be evaluated for package, platform, and operational compatibility before migration.
Is upgrading the TargetFramework enough?
No.
You should also review packages, SDK selection, container images, CI/CD, deployment configuration, native dependencies, and runtime behavior.
Should I upgrade NuGet packages at the same time?
Some packages may need to be upgraded for compatibility.
However, avoid unnecessary dependency changes when possible because they increase the migration's change surface.
How long should a migration take?
There is no universal duration.
A small application with strong automated tests may require relatively little effort, while a large system with legacy dependencies can require significantly more planning.
Measure the actual application rather than estimating solely from the number of projects.
Conclusion
The approaching .NET 8 and .NET 9 support deadline should be treated as a planning milestone rather than a last-minute upgrade task.
Microsoft currently lists both releases as supported through November 10, 2026. Teams that need continued support beyond that point should establish a migration plan early, select an appropriate supported target, and validate the complete application stack.
A successful migration is more than changing:
<TargetFramework>net8.0</TargetFramework>
to:
<TargetFramework>net10.0</TargetFramework>
The complete path is:
Inventory
|
v
Compatibility Review
|
v
Framework Upgrade
|
v
Dependency Upgrade
|
v
Testing
|
v
Performance Validation
|
v
Staging
|
v
Controlled Production Rollout
The most important principle is:
Treat a framework migration as a production change, not a project-file change.
Starting early gives teams time to discover incompatible dependencies, validate application behavior, update deployment infrastructure, and roll out the new runtime without turning a support deadline into an emergency release.