Modernization
Enterprise AI
Modernizing .NET code with Roslyn and Droids
November 14, 2026 - 5 minute read
Modernization
Enterprise AI
November 14, 2026 - 5 minute read
.NET modernization with Roslyn uses the compiler platform's analyzers and code fixes for the changes a machine can make the same way every time, and a coding agent for the changes that need judgment. Roslyn rules and dotnet format handle syntax modernization, style, and known API replacements. Droids, the coding agents in Factory, take the build failures, unsupported APIs, and test repairs that remain, and write new analyzers when a pattern repeats.
The timing matters. As of November 2026, the .NET support policy lists .NET 10 as the active long-term support release through November 14, 2028. .NET 8 and .NET 9 both reached end of support on November 10, 2026, the day .NET 11 shipped as a standard-term release. Any estate still on .NET Framework, .NET 8, or older now has a supported target and a reason to move.
Roslyn is the .NET Compiler Platform. An analyzer inspects syntax trees and semantic models and reports a diagnostic with an ID. A code fix rewrites the code to resolve that diagnostic. The .NET SDK ships hundreds of these rules: IDE rules for code style, such as IDE0161 for file-scoped namespaces and IDE0090 for target-typed new, and CA rules for code quality and compatibility, such as CA1416 for calls to platform-specific APIs. The same analyzer and code fix APIs target Visual Basic, and dotnet format runs against Visual Basic projects, so VB.NET code follows the same loop.
dotnet format is the batch runner for those rules. The dotnet format reference documents three subcommands. whitespace applies formatting, style applies code style fixes, and analyzers applies fixes from analyzers, including third-party ones. Each accepts --diagnostics to limit the run to specific IDs and --severity to set the minimum level. --verify-no-changes makes any of them a check that exits non-zero when it would edit a file.
The project-level tools have changed. Microsoft now marks .NET Upgrade Assistant as officially deprecated and points users to its GitHub Copilot upgrade agent, which assesses a solution, plans the upgrade, applies changes, and validates the build. The try-convert tool for converting legacy project files to SDK-style was archived in May 2024. Moderne also publishes C# recipes for OpenRewrite under its proprietary license.
Factory has no built-in .NET converter or language mode. Droids run the SDK's own tools.
Each command below ships with the .NET SDK. Run them from the solution directory with the target rules enabled in .editorconfig, for example csharp_style_namespace_declarations = file_scoped:warning.
Commit the output of each dotnet format run on its own. A reviewer can confirm a style commit by rerunning the same command on the parent commit and comparing the result, which is far faster than reading thousands of mechanical edits.
Code fixes cover syntax and well-defined API replacements. A move to .NET 10 needs more than that.
Project files are the first layer. Legacy .csproj files list every source file and carry packages.config references, while SDK-style projects use globbing and PackageReference. Converting them is mechanical in principle, but each conversion needs a build to prove that nothing was dropped.
Runtime and framework changes are the second. ASP.NET applications depend on System.Web and HttpContext, which have no direct equivalent in ASP.NET Core. Microsoft's ASP.NET Framework to ASP.NET Core guide recommends incremental migration for most production apps for that reason. Code that relied on BinaryFormatter, whose implementation was removed in .NET 9, needs a new serialization choice. Windows-only APIs flagged by CA1416 need either a platform guard or a replacement.
Those decisions are the Droid's work. It reads the build output, groups the failures by cause, and fixes one class at a time, so a commit that replaces BinaryFormatter does not also touch authentication middleware. For each choice with more than one reasonable answer, such as System.Text.Json versus a contract-compatible serializer for data already stored on disk, it writes the tradeoff into the pull request for the owner to confirm. Where coverage is thin, it adds characterization tests against the old build first, recording the current outputs for representative inputs. The same tests then run unchanged against the migrated code with dotnet test, so a behavior change surfaces as a failing assertion.
Factory is a platform for running a software factory, where Droids take work from a signal to a reviewed pull request. The loop for a .NET project keeps Roslyn in charge of every change it can make and the build in charge of deciding what is left.
Formatting on every edit. Factory hooks run shell commands at lifecycle events. A PostToolUse hook on Edit, Create, and ApplyPatch can run dotnet format --verify-no-changes --include on the file Droid just changed. When the script exits with code 2, its stderr goes back to Droid as feedback, so a style or analyzer violation is fixed before the next edit rather than at review time.
{
"PostToolUse": [
{
"matcher": "Edit|Create|ApplyPatch",
"hooks": [
{
"type": "command",
"command": "\"$FACTORY_PROJECT_DIR\"/.factory/hooks/dotnet-format-check.sh",
"timeout": 120
}
]
}
]
}Project facts in AGENTS.md. The AGENTS.md file records the solution path, target framework, test command, and any project that must stay on .NET Framework during an incremental migration. Droid reads it before writing code.
The playbook as a skill. A skill in .factory/skills/dotnet-upgrade/SKILL.md lists the order of operations. Convert one project to SDK-style, retarget it, run the dotnet format passes, build, fix residue, and verify, with each step in its own commit. Skills load when a task matches, so every session follows the same sequence.
A repeated manual fix is a candidate for a new Roslyn rule. Suppose a codebase calls an internal LegacyLogger.Write(string) in four thousand places and the target is ILogger. The Droid writes a DiagnosticAnalyzer that reports each call, a CodeFixProvider that rewrites it, and tests that assert the diagnostic location and the fixed source. Microsoft's analyzer and code fix tutorial shows the test pattern, where VerifyCodeFixAsync compares marked-up input with the expected output. The Droid also adds cases that must not trigger the rule.
Once the analyzer ships as a NuGet package, dotnet format analyzers --diagnostics with its rule ID applies it everywhere deterministically. The agent wrote the rule once. The tool applies it thousands of times, and the rule keeps reporting any new call that appears later.
For many projects, Droid Exec runs Droid headless in CI. The pattern from Factory's lint autofix recipe carries over directly. A deterministic tool lists the targets, one droid exec run handles each target in isolation, and the tool re-verifies. For .NET, the target list is the set of projects whose build fails after the tool commits, and each run uses --auto medium so it can build and test locally.
A portfolio of solutions with shared libraries needs ordering, since libraries have to move before their consumers. Factory Missions plan that work as milestones with validation along the way, and list brownfield migrations as a primary use case.
As of November 2026, .NET 10 is the current long-term support release, supported until November 14, 2028. .NET 8 and .NET 9 reached end of support on November 10, 2026, the same day .NET 11 shipped as a standard-term release. Check the .NET support policy page before choosing a target.
Microsoft lists .NET Upgrade Assistant as officially deprecated and points users to its GitHub Copilot upgrade agent. The try-convert tool for SDK-style project conversion was archived in May 2024. Teams can still use the Roslyn analyzers and dotnet format that ship with the .NET SDK as deterministic tools in any workflow.
No. Factory runs the .NET SDK tools directly. Hooks run dotnet format and the build after edits, skills hold the migration playbook, Droid Exec fans work out per project, and Missions coordinate migrations across many solutions. Droids also write custom Roslyn analyzers and code fixes with tests.
dotnet format applies whitespace rules, code style rules, and analyzer code fixes configured in .editorconfig. The style and analyzers subcommands accept a list of diagnostic IDs and a minimum severity, and --verify-no-changes exits non-zero when any change would be made, which makes it usable as a CI gate.
Yes, for the deterministic layer. The .NET Compiler Platform SDK supports analyzers and code fixes for both C# and Visual Basic, and dotnet format runs against Visual Basic projects. The Droid follows the same loop on VB.NET code.
COBOL modernization works when every step is checked by something deterministic. Compile with GnuCOBOL, inventory with a real parser, capture golden files from batch inputs, and let Droids handle the residue that translation tools leave behind.
PHP modernization works best when Rector performs the mechanical upgrade, PHPStan and the test suite gate every step, and Factory Droids handle the residue and write new Rector rules. A practical setup with hooks, skills, and Droid Exec.
Python modernization works best when Ruff, django-upgrade, and LibCST codemods make the mechanical changes and Droids handle the residue. A practical setup with hooks, tested codemods, type checking, and a rerun gate that must report zero changes.
Start building