CASE STUDY
Zero Migration Code Written by Hand:
How TechGrit Used a Self-Improving AI Skill to Migrate 50 Projects from .NET 4.8 to .NET 10 Across Three Repositories
18 JUN, 2026

50
Projects Migrated Across 3 Repos
346/346
Tests Passing on both .NET 4.8 and .NET 10
Zero
Production Regressions to .NET 4.8
The Challenge
An enterprise test automation software vendor needed to modernize a large .NET Framework codebase to .NET 10, while keeping production stable throughout. The platform had grown organically across multiple repositories, with deep dependencies on legacy technologies that .NET 10 does not support. The business stakes were clear: ship a dual-targeting build that runs on both net48 and net10.0-windows, prove it in three repositories before scaling to the full platform, and do it without breaking a single production build. The net48 target is intentionally transitional: it lets the client keep shipping on the current framework while the net10 build is validated in production. Once the net10.0-windows build is confirmed stable, a dedicated Claude skill pass will drop net48, audit every conditional compilation block, strip the net48 paths, and collapse to a single net10 target, with zero functional behavior changes as a hard requirement.
The Scale of the Problem
The full platform spans a desktop IDE with approximately 200 projects. The migration order was non-negotiable: dependencies ran bottom-up. Libs had to move first because Dex depended on it. Dex had to complete before Rox could be unblocked. And all three had to succeed before the IDE migration began. The four repositories contained:
-
Old-style .csproj files using ToolsVersion 12 through 14, incompatible with the SDK-style format required for multi-targeting
-
Paket for dependency management, with no path to Central Package Management without a full conversion
-
.NET Framework 4.8 as the sole target, with no .NET 10 path defined anywhere in the build system
-
Five legacy technologies that .NET 10 does not support: EF6, .NET Remoting, OWIN, NetworkComms, and SOAP service references
-
A WiX v3 installer in the Dex repository that needed to produce two separate MSIs, one per framework
Our Approach
TechGrit approached this as a compound engineering problem: the migration itself, the AI tooling strategy, and the sequencing plan all had to be solved simultaneously. The approach rested on four deliberate moves.
Move 1: Build a Migration Skill, Not a Migration Script
The first decision was the most consequential. Rather than writing one-off migration scripts, TechGrit built on an existing dotnet-migration-skill, a structured AI playbook checked into the repository alongside the code, that Claude follows phase by phase. The skill was not run cold on the live codebase. Before invoking it, a human engineer audited the full Paket dependency graph, mapped which packages were net48-only and would need net10 replacements, and sketched the Directory.Packages.props structure. Only after that groundwork was done did the skill execute the mechanical bulk of the conversion. The skill ran three full iterations on Dex. Iteration 1 produced the structural skeleton but left gaps: missing conditional ItemGroup blocks and no preprocessor directives in source files referencing legacy APIs. Iteration 2 addressed the structural gaps and surfaced that EF6, .NET Remoting, and OWIN had no .NET 10 equivalent, requiring full replacement implementations rather than stubs. Iteration 3 produced correct implementations for all three subsystems, with the wire-protocol spec, EF Core migration pattern, and Kestrel route inventory provided as explicit input. Every gap found in each pass was folded back into the skill permanently. The pattern throughout was deliberate: prepare the ground manually, direct Claude with increasing precision, review the output critically, identify gaps, and repeat until the bar was met. Three repositories in, the skill had accumulated 50+ rules. By the time the full IDE migration begins, it starts from a near-final state on every new project.
Move 2: Sequence by Dependency, Not by Convenience
The migration order was determined entirely by the dependency graph, not by ease. Libs migrated first because Dex depended on it. Dex completed before Rox was unblocked. Rox completed before any work on the full IDE platform could begin. Within each repository, projects were ordered by batch dependency, so that each batch could be parallelized internally but every batch completed before the next began. This sequencing meant that the migration skill was applied to increasingly complex codebases in the right order, and that learnings from each stage directly benefited the next.
Move 3: Treat Technology Replacements as First-Class Engineering Work
The migration skill automates the mechanical conversion. It does not write technology replacements. Five legacy technologies required full replacement implementations: EF6 to EF Core, .NET Remoting to Named Pipes, OWIN and NetworkComms to Kestrel, SOAP service references to HttpClient, and WiX v3 to WiX v6. Each replacement was engineered as a first-class deliverable with the same approach: enumerate the API surface, implement at exact parity with the legacy behavior, validate on both targets, and document the pattern for reuse. An audit skill was built alongside the migration skill to verify that zero stub implementations remained before any repository was declared complete.
Move 4: Build a Dedicated Runtime Testing Phase Into Every Repository
The pilot revealed a finding that reshaped the full plan: compiler success and runtime correctness are independent. Nine runtime behavioral differences were found across Libs and Dex alone, covering SQLite locking, WPF window activation, process launching, ContextMenu teardown, MSI service installation, and configuration file compatibility. None were visible at build time. Each was diagnosed, fixed, and written back into the skill as a proactive check for all future repositories. A mandatory runtime testing phase was added to every subsequent batch to catch the same class of failure before it reached production.
Why Manual Migration Was Not a Viable Option
A conventional manual migration of this scale, touching 50+ projects across three repositories with five technology replacements, would have consumed months of senior engineering time with no guarantee of consistency. Each engineer would make different decisions about how to handle the same pattern. Technology replacements would be implemented differently across repositories. And every change would carry the risk of introducing a net48 regression that broke production.
The Additional Constraint: Runtime Behavior Is Invisible to the Compiler
“The challenge was not just migrating the code. It was proving that an AI-assisted approach could handle five simultaneous technology replacements across a production codebase, at scale, without a single regression.”
A build that compiles cleanly on both targets does not mean the application runs correctly on .NET 10. The .NET runtime changed default behaviors across dozens of APIs between Framework and .NET 10: file I/O, process launching, WPF window activation, SQLite locking, ContextMenu lifecycle, and more. Every one of these differences would only surface at runtime, after the build was green, requiring a dedicated testing phase that had to be budgeted separately from the migration work.
Technical Deep Dive
The Migration Skill Architecture
The dotnet-migration-skill is a structured AI playbook stored in .claude/skills/ alongside the source code. It is organized into phases, each independently invocable, with a master orchestrator that reads migration-config.json, enforces global rules, and coordinates phase execution in sequence.
Phase | Executing |
|---|---|
Phase 0 | Assessment |
Phases 1 to 2 | SDK and CPM |
Phase 3 | Multi-target |
Phase 4 | Technology Replacement |
Technology Replacements: What Was Built
Each replacement followed the same principle: same interface, new transport. Application code above the replacement layer was left entirely unmodified.
Legacy Technology | To | Replacement | Implementation Detail |
|---|---|---|---|
EF6 (EntityFramework 6.x) | → | EF Core 9 (Sqlite) | IDbSet<T> compatibility shim kept the Domain layer unmodified. WAL mode and busy_timeout required to prevent SQLite locking errors on .NET 10. |
.NET Remoting (IpcChannel / binary) | → | Named Pipes + DispatchProxy | NamedPipeServerStream / NamedPipeClientStream with System.Text.Json serialization and DispatchProxy client proxies. Line-based framing, UTF-8 encoding, 2000ms timeout. |
OWIN + NetworkComms (10+ REST routes) | → | ASP.NET Core / Kestrel | All REST routes reimplemented at exact parity with net48 OWIN counterparts using ASP.NET Core with Kestrel hosting. |
SOAP Service References | → | HttpClient (manual SOAP 1.1) | Legacy SOAP proxies replaced with manual SOAP 1.1 envelope construction via System.Xml.Linq and HttpClient. No CoreWCF dependency introduced. |
WiX v3 Installer | → | WiX v6 (dual MSI) | Two separate MSIs produced, one per framework. MajorUpgrade schedule set to afterInstallExecute. ProductCode auto-generation. Recursive runtimes/ directory harvest for net10 self-contained MSI. |
Critical Architecture Discovery: The Dual IPC System
During the analysis of the full IDE platform, TechGrit discovered that the client already maintained two parallel IPC systems: a legacy System.Runtime.Remoting path for net48 automation scenarios, and a modern Core.Ipc path using StreamJsonRpc over Named Pipes for .NET Core scenarios. This discovery materially reduced the risk profile of the IDE migration. The net10.0-windows host can use Core.Ipc immediately. The legacy Remoting code can be wrapped in #if NET48 guards. No new IPC protocol needs to be built from scratch, which was the highest-risk item in the original pre-pilot estimate.
Runtime Issues Discovered and Resolved
Nine runtime behavioral differences were identified and fixed across Libs and Dex. All were invisible at build time. All patterns were written back into the migration skill as proactive checks for subsequent repositories.
The Skill Improvement Loop
The compounding value of the skill approach became measurable across the three repositories. Dex ran three full skill iterations and added approximately 35 rules. Libs ran two iterations and added approximately 15 rules. Rox ran one iteration and required minimal intervention, with mechanical work comprising approximately 80% of total effort compared to 30% for Dex. The pattern is explicit: each repository makes the next one faster and more predictable.
Outcomes & Impact
For the Client:
-
Three repositories completed in the Phase 1 pilot window: Libs in 1 week, Dex in 2 weeks, Rox in 1 week.
-
All solutions build and pass tests on both net48 and net10.0-windows with zero errors and zero warnings.
-
Five legacy technology replacements completed with full parity implementations: EF6, .NET Remoting, OWIN, SOAP, and WiX v3.
-
The migration unblocked the full IDE platform migration, which is the commercial objective the pilot was designed to validate.
-
A data-backed, batch-by-batch migration plan for the IDE platform was produced as a pilot deliverable, replacing the scenario-based estimates of the original proposal with measured data.
For the Platform and Operations:
-
A self-improving AI migration skill with 50+ rules is now the primary tooling asset for the IDE migration. It starts from a near-final state on every new project.
-
An audit skill was built alongside the migration skill, providing automated verification that zero stub implementations remain before any repository is marked complete.
-
12 proven patterns from the pilot are documented and proactively applied to all subsequent repositories, eliminating the need to rediscover the same runtime behavioral differences.
-
The pre-pilot risk register had 7 items. After the pilot, 7 risks were retired or reduced, and 6 new risks were surfaced and documented with specific mitigations.
-
The dual IPC discovery reduced the highest-risk item in the IDE migration from a full Remoting replacement build (the hardest work in Dex) to a wrapping exercise using infrastructure that already exists.
Five lessons from this engagement that will read as useful to a peer engineer running a large-scale .NET migration at another firm:
1. A Self-Improving Skill Beats a One-Off Script by an Order of Magnitude
The decisive architectural choice of this engagement was building a structured playbook that compounds knowledge across projects rather than writing migration scripts that solve one problem and are discarded. After three repositories, the skill had 50+ rules covering not just the mechanical conversion but the runtime behavioral differences, technology replacement patterns, and installer edge cases that no pre-migration analysis could have predicted. Any engineering team running a multi-repository migration should invest in the tooling first. The compounding return starts on the second repository and accelerates from there.
2. Build-Green Does Not Mean Runtime-Correct; Budget Testing as a Separate Phase
This is the finding that most teams discover too late. Nine runtime behavioral differences were found across two repositories. None were visible at compile time. All required diagnosis and fix after the build was already green. The .NET runtime changed default behaviors for dozens of APIs between Framework and .NET 10. Process launching, WPF window activation, SQLite journal mode, ContextMenu teardown, MSI service installation: each of these has a different default in .NET 10 and each will produce a silent failure or a crash that the compiler cannot detect. Runtime testing must be planned and budgeted as a distinct phase for every batch, not treated as a consequence of the migration that will sort itself out.
3. Technology Replacements Are Engineering Projects, Not Migration Tasks
EF6 to EF Core is not a mechanical conversion. Neither is .NET Remoting to Named Pipes, or OWIN to Kestrel. Each replacement requires understanding the full API surface of the legacy technology, designing an implementation that preserves the behavioral contract, and validating it against the same tests the legacy implementation passed. On Dex, technology replacements consumed approximately 40% of total effort. Planning a .NET migration that does not explicitly budget technology replacements as first-class engineering work will produce either stub implementations that fail at runtime or significant overruns that were not in the original estimate.
4. Sequence by Dependency Graph, Not by Team Preference
The migration order was determined entirely by what depended on what, not by what was easiest or what the team was most familiar with. Libs migrated first because Dex needed it. Dex before Rox. Three repositories before the IDE. This sequencing meant that skills and patterns accumulated in the right order, and that the hardest repository in each phase (Dex, then the IDE Core Engine) was approached with the full weight of prior learnings. Teams that migrate the easy repositories first defer the hardest problems and arrive at them with less runway, less context, and a skill that has not been tested on the most complex material.
5. Read the Existing Architecture Before Designing a Replacement
The dual IPC discovery during the IDE analysis is the clearest example of this lesson. The original estimate assumed that the .NET Remoting replacement in the IDE Core would need to be built from scratch, as it was in Dex. A careful architectural review revealed that the client already had a production Named Pipes IPC system for .NET Core automation scenarios. That discovery reduced the highest-risk item in the full migration from a greenfield build to a wrapping exercise. The time spent on architectural analysis before the migration begins is not overhead. It is risk management.
Key Learnings
Ready to modernize your .NET codebase without betting production stability on it?
Most teams either go fully manual, accepting months of senior engineering time with no guarantee of consistency, or fully automated, accepting the risk of stub implementations and silent runtime regressions. TechGrit took a third path: an AI skill that handles the mechanical volume, directed and verified by engineers at every phase, that gets sharper with every repository it touches. Three repositories in, zero production regressions, and a tooling asset ready for the next two hundred projects.