Documentation

adrs/029-dotnet-10-lts-migration.md

ADR-029: Migration to .NET 10 LTS

Status

Accepted

Context

We were on .NET 9.0, which is not a Long Term Support (LTS) release. .NET 10 is the LTS version released in November 2025. As an enterprise system, Dynaplex needs the stability, support, and longevity that LTS releases provide.

Key considerations:

  • .NET 9 support ends 6 months after .NET 10 release (May 2026)
  • .NET 10 LTS is supported for 3 years (until November 2028)
  • Performance improvements and new features in .NET 10
  • All dependencies have been updated to support .NET 10

Decision

We migrated to .NET 10 LTS to take advantage of the new features, performance improvements, and long-term support.

Completed: January 2026

Consequences

Positive

  • Long-term Support: 3 years of Microsoft support
  • Performance: Expected 10-20% performance improvements
  • New Features: Access to latest C# and runtime features
  • Security: Latest security updates and patches
  • Stability: LTS releases are more thoroughly tested
  • Ecosystem: Better third-party library support for LTS

Negative

  • Migration Effort: Time and resources for upgrade
  • Breaking Changes: Potential API changes requiring code updates
  • Testing: Full regression testing required
  • Dependency Updates: All packages need updating
  • Training: Team needs to learn new features

Neutral

  • Timing: Must coordinate with business cycles
  • Rollback Plan: Need strategy if issues arise
  • Feature Freeze: May need code freeze during migration

Proposed Implementation

Pre-Migration Preparation (Q3 2025)

// 1. Audit current dependencies
<Project>
  <PropertyGroup>
    <!-- Add analyzer to find deprecated APIs -->
    <EnableNETAnalyzers>true</EnableNETAnalyzers>
    <AnalysisLevel>latest</AnalysisLevel>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
  </PropertyGroup>
</Project>

// 2. Create compatibility report
dotnet list package --deprecated
dotnet list package --outdated
dotnet list package --vulnerable

Migration Strategy

<!-- Directory.Build.props - Centralized update -->
<Project>
  <PropertyGroup>
    <!-- Phase 1: Update to .NET 10 -->
    <TargetFramework>net10.0</TargetFramework>
    <LangVersion>14.0</LangVersion>
    
    <!-- Enable new features gradually -->
    <EnablePreviewFeatures>false</EnablePreviewFeatures>
    <EnableUnsafeBinaryFormatterSerialization>false</EnableUnsafeBinaryFormatterSerialization>
  </PropertyGroup>
</Project>

Expected New Features to Adopt

1. Performance Improvements

// Improved LINQ performance
var results = assets
    .Where(a => a.IsActive)
    .OrderBy(a => a.Name)
    .Take(100)
    .ToList();  // Expect 20% faster

// Better async performance
await Parallel.ForEachAsync(items, async (item, ct) =>
{
    await ProcessItemAsync(item, ct);
});  // Improved task scheduling

2. New Language Features (C# 14)

// Primary constructors for all classes (not just records)
public class AssetService(ILogger<AssetService> logger, CatalogDb db)
{
    public async Task<Asset> GetAssetAsync(int id)
    {
        logger.LogInformation("Getting asset {Id}", id);
        return await db.Assets.FindAsync(id);
    }
}

// Enhanced pattern matching
var description = asset switch
{
    { Status: "active", Location.Type: "warehouse" } => "Available in warehouse",
    { Status: "maintenance" } => "Under maintenance",
    _ => "Check status"
};

// Collection expressions improvements
List<Asset> assets = [asset1, asset2, ..existingAssets, asset3];

3. Runtime Improvements

// Native AOT improvements
[JsonSerializable(typeof(Asset))]
[JsonSerializable(typeof(List<Asset>))]
public partial class CatalogJsonContext : JsonSerializerContext
{
    // Better trimming and size reduction
}

// Improved garbage collection
<PropertyGroup>
  <ServerGarbageCollection>true</ServerGarbageCollection>
  <GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
</PropertyGroup>

4. New APIs and Libraries

// Potential new APIs (speculative)
// Improved HTTP/3 support
builder.WebHost.ConfigureKestrel(options =>
{
    options.ConfigureEndpointDefaults(listenOptions =>
    {
        listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
        listenOptions.UseHttps();
    });
});

// Better cloud-native features
builder.Services.AddAzureClients(clients =>
{
    clients.AddServiceBusClient(configuration)
        .WithHealthChecks()
        .WithMetrics();
});

Testing Strategy

// 1. Create .NET 10 test environment
FROM mcr.microsoft.com/dotnet/sdk:10.0-preview AS build
WORKDIR /src
COPY . .
RUN dotnet test --configuration Release

// 2. Compatibility tests
[Test]
[TestCase("net9.0")]
[TestCase("net10.0")]
public async Task EnsureBackwardCompatibility(string framework)
{
    // Test serialization compatibility
    var json = SerializeWithFramework(asset, framework);
    var deserialized = DeserializeWithFramework<Asset>(json, "net10.0");
    
    deserialized.Should().BeEquivalentTo(asset);
}

// 3. Performance benchmarks
[Benchmark]
[SimpleJob(RuntimeMoniker.Net90)]
[SimpleJob(RuntimeMoniker.Net100)]
public async Task AssetQueryPerformance()
{
    return await _db.Assets
        .Where(a => a.IsActive)
        .Include(a => a.Type)
        .ToListAsync();
}

Dependency Update Plan

{
  "dependencies": {
    "Microsoft.EntityFrameworkCore": "9.0 -> 10.0",
    "FluentValidation": "11.x -> 12.x",
    "Serilog": "3.x -> 4.x",
    "Microsoft.Extensions.Hosting": "9.0 -> 10.0",
    "System.Text.Json": "9.0 -> 10.0"
  },
  "action_required": {
    "Check for breaking changes in each major version",
    "Update deprecated API usage",
    "Test third-party integrations"
  }
}

Rollback Strategy

# Deployment with rollback capability
deployment:
  strategy: blue-green
  stages:
    - name: Deploy .NET 10
      steps:
        - Deploy to staging slot
        - Run smoke tests
        - Run performance tests
        - If tests pass: Swap slots
        - If tests fail: Keep current version
  
  rollback:
    - Keep .NET 9 binaries available
    - Database migrations must be backward compatible
    - Feature flags for .NET 10-specific features

Migration Checklist

  • Review .NET 10 breaking changes document
  • Update all NuGet packages to compatible versions
  • Update Directory.Build.props with new framework
  • Fix any compilation errors
  • Update CI/CD pipelines with .NET 10 SDK
  • Run full test suite
  • Performance testing and comparison
  • Security scanning with new framework
  • Update Docker base images
  • Update documentation
  • Train team on new features

Communication Plan

## Timeline Communication

### 3 Months Before (August 2025)
- Announce migration plan to stakeholders
- Begin dependency assessment

### 1 Month Before (October 2025)
- Test with release candidates
- Identify potential issues

### Migration Week (January 2026)
- Daily status updates
- Clear go/no-go criteria

### Post-Migration (February 2026)
- Performance reports
- Issue tracking
- Team training sessions

Monitoring and Success Criteria

Performance Metrics

  • API response time: Expect 10-20% improvement
  • Memory usage: Should remain same or decrease
  • Startup time: Target < 3 seconds
  • Throughput: Increase by 15%

Stability Metrics

  • Error rate: Must not increase
  • Availability: Maintain 99.9% SLA
  • Failed requests: < 0.1%

Code Quality

  • All tests passing
  • No new security vulnerabilities
  • Code coverage maintained
  • ADR-008: .NET 9.0 with Latest C# Features (superseded by this ADR)
  • ADR-019: OpenTelemetry Integration (monitoring migration)
  • ADR-025: System.Text.Json Standardization (JSON improvements)