dotnet-modernize
dotnet-modernize
Analyze existing .NET code for modernization opportunities. Identifies outdated target frameworks, deprecated packages, superseded API patterns, and missing modern best practices. Provides actionable recommendations for each finding.
Scope
- Outdated target framework detection
- Deprecated package identification
- Superseded API pattern flagging
- Actionable modernization recommendations
Out of scope
- Actual migration paths and polyfill strategies -- see [skill:dotnet-version-upgrade]
- Multi-targeting guidance -- see [skill:dotnet-multi-targeting]
Prerequisites: Run [skill:dotnet-version-detection] first to determine the current SDK, TFM, and language version. Run [skill:dotnet-project-analysis] to understand solution structure and dependencies.
Cross-references: [skill:dotnet-project-structure] for modern layout conventions, [skill:dotnet-add-analyzers] for analyzer-based detection of deprecated patterns, [skill:dotnet-scaffold-project] for the target state of a fully modernized project.
Modernization Checklist
Run through this checklist against the existing codebase. Each section identifies what to look for and what the modern replacement is.
1. Target Framework
Check <TargetFramework> in .csproj files (or Directory.Build.props):
| Current TFM | Status | Recommendation |
|---|---|---|
net8.0 |
LTS -- supported until Nov 2026 | Plan upgrade to net10.0 (LTS) |
net9.0 |
STS -- support ends May 2026 | Upgrade to net10.0 promptly |
net7.0 |
End of life | Upgrade immediately |
net6.0 |
End of life | Upgrade immediately |
net5.0 or lower |
End of life | Upgrade immediately |
netstandard2.0/2.1 |
Supported (library compat) | Keep if multi-targeting for broad reach |
netcoreapp3.1 |
End of life | Upgrade immediately |
.NET Framework 4.x |
Legacy | Evaluate migration feasibility |
To scan all projects:
# Find all TFMs in the solution
find . -name "*.csproj" -exec grep -h "TargetFramework" {} \; | sort -u
# Check Directory.Build.props
grep "TargetFramework" Directory.Build.props 2>/dev/null
```csharp
---
### 2. Deprecated and Superseded Packages
Scan `Directory.Packages.props` (or individual `.csproj` files) for packages that have been superseded:
| Deprecated Package | Replacement | Since |
|-------------------|-------------|-------|
| `Microsoft.Extensions.Http.Polly` | `Microsoft.Extensions.Http.Resilience` | .NET 8 |
| `Newtonsoft.Json` (new projects) | `System.Text.Json` | .NET Core 3.0+ |
| `Microsoft.AspNetCore.Mvc.NewtonsoftJson` | Built-in STJ | .NET Core 3.0+ |
| `Swashbuckle.AspNetCore` | Built-in OpenAPI (`Microsoft.AspNetCore.OpenApi`) for document generation; keep Swashbuckle if using Swagger UI, filters, or codegen | .NET 9 |
| `NSwag.AspNetCore` | Built-in OpenAPI for document generation; keep NSwag if using client generation or Swagger UI features | .NET 9 |
| `Microsoft.Extensions.Logging.Log4Net.AspNetCore` | Built-in logging + `Serilog` or `OpenTelemetry` | .NET Core 2.0+ |
| `Microsoft.AspNetCore.Authentication.JwtBearer` (explicit NuGet package) | Remove explicit PackageReference -- included in `Microsoft.AspNetCore.App` shared framework | .NET Core 3.0+ |
| `System.Data.SqlClient` | `Microsoft.Data.SqlClient` | .NET Core 3.0+ |
| `Microsoft.Azure.Storage.*` | `Azure.Storage.*` | 2020+ |
| `WindowsAzure.Storage` | `Azure.Storage.Blobs` / `Azure.Storage.Queues` | 2020+ |
| `Microsoft.Azure.ServiceBus` | `Azure.Messaging.ServiceBus` | 2020+ |
| `Microsoft.Azure.EventHubs` | `Azure.Messaging.EventHubs` | 2020+ |
| `EntityFramework` (EF6) | `Microsoft.EntityFrameworkCore` | .NET Core 1.0+ |
| `RestSharp` (older versions) | `HttpClient` + `System.Text.Json` | .NET Core+ |
| `AutoMapper` | Manual mapping or source-generated mappers | Preference |
To scan for deprecated packages:
```bash
# List all package references
grep -r "PackageReference" *.csproj Directory.Packages.props 2>/dev/null | grep -o 'Include="[^"]*"' | sort -u
```bash
---
### 3. Superseded Patterns
Identify common patterns that have modern replacements:
#### 3.1 Async/Await
| Old Pattern | Modern Pattern | Benefit |
|-------------|----------------|---------|
| `async void` (non-event handlers) | `async Task` | Exception handling, composability |
| `.Result` / `.Wait()` | `await` with proper async propagation | No deadlocks |
| `Task.Run` for I/O | Direct async I/O | Fewer thread switches |
| Manual `ConfigureAwait(false)` | Global policy or targeted use | Cleaner code |
#### 3.2 Dependency Injection
| Old Pattern | Modern Pattern | Benefit |
|-------------|----------------|---------|
| `new ServiceCollection()` in test | `WebApplicationFactory` | Realistic test environment |
| Manual service location | Constructor injection | Explicit dependencies |
| `IServiceProvider` casting | Generic `GetRequiredService<T>()` | Type safety |
#### 3.3 Configuration
| Old Pattern | Modern Pattern | Benefit |
|-------------|----------------|---------|
| `ConfigurationBuilder` manual setup | `HostBuilder`/`WebApplicationBuilder` | Convention over configuration |
| `appSettings.json` only | `IConfiguration` with env vars, Key Vault, AppConfig | 12-factor compliance |
| `ConfigurationManager` | `ConfigurationBuilder` + `AddJsonFile` | Flexibility |
#### 3.4 HTTP Clients
| Old Pattern | Modern Pattern | Benefit |
|-------------|----------------|---------|
| `new HttpClient()` per request | `IHttpClientFactory` | Socket exhaustion prevention |
| `HttpClient` singleton | `IHttpClientFactory` + typed clients | DNS rotation |
| Manual retry logic | `Microsoft.Extensions.Http.Resilience` | Polly integration |
#### 3.5 JSON Serialization
| Old Pattern | Modern Pattern | Benefit |
|-------------|----------------|---------|
| `Newtonsoft.Json` for new projects | `System.Text.Json` | Native AOT compatible, faster |
| `JsonConvert.SerializeObject` | `JsonSerializer.Serialize` | Consistent with framework |
| Custom converters without source gen | JSON source generators | Better startup performance |
#### 3.6 Logging
| Old Pattern | Modern Pattern | Benefit |
|-------------|----------------|---------|
| `Console.WriteLine` | `ILogger<T>` | Structured logging, filtering |
| Static logger access | Injected `ILogger<T>` | Testability, scoping |
| `Log4Net` / `NLog` in new projects | `Microsoft.Extensions.Logging` | Unified ecosystem |
#### 3.7 Data Access
| Old Pattern | Modern Pattern | Benefit |
|-------------|----------------|---------|
| Raw ADO.NET without `await` | `DbContext` + async EF Core | Productivity |
| `System.Data.SqlClient` | `Microsoft.Data.SqlClient` | Active development, bug fixes |
| `SqlConnection` per query | Connection pooling via DI | Performance |
---
## Output Format
Provide findings in a structured format:
```markdown
## Modernization Report
### Target Framework
- **Current:** `net6.0` (⚠️ End of life)
- **Recommendation:** Upgrade to `net8.0` or `net10.0`
- **Effort:** Medium -- requires testing
### Deprecated Packages
| Package | Current Version | Status | Replacement |
|---------|----------------|--------|-------------|
| `Newtonsoft.Json` | 13.0.3 | ⚠️ Consider STJ | `System.Text.Json` |
| `Microsoft.Extensions.Http.Polly` | 8.0.0 | ❌ Deprecated | `Microsoft.Extensions.Http.Resilience` |
### Superseded Patterns
| File | Line | Pattern | Modern Alternative |
|------|------|---------|-------------------|
| `Program.cs` | 42 | `async void` | `async Task` |
| `Startup.cs` | 15 | Manual `ConfigurationBuilder` | `WebApplicationBuilder` |
### Recommendations Priority
1. **Critical:** Upgrade EOL TFMs immediately
2. **High:** Replace deprecated packages before next release
3. **Medium:** Refactor superseded patterns incrementally
4. **Low:** Consider optional modernizations for consistency
```text
---
## References
- [.NET Release Lifecycle](https://dotnet.microsoft.com/en-us/platform/support/policy)
- [Obsolete APIs in .NET](https://learn.microsoft.com/en-us/dotnet/core/compatibility/obsolete-apis)
- [PackageDeprecation on NuGet](https://devblogs.microsoft.com/nuget/deprecating-packages/)