Migration from VB6, .NET Framework, and legacy systems with method, priorities, and risk reduction
This category explains how to tackle legacy systems and modernization progressively: less trauma, fewer replatforming slogans, more prioritization, more stabilization, and stronger economic control of change.
Legacy is not a dirty word: it is an economic problem with gradual solutions
Legacy software is not old software.
It is software that costs more than it produces: slow to change, hard to understand, expensive to test, risky to release.
A system written yesterday with a chaotic architecture is already legacy.
A system written twenty years ago with a clean and documented architecture is not.
The economic problem of legacy is real and measurable: every new feature takes twice as long compared to an equivalent modern system, every release generates anxiety because nobody knows for certain what might break, the development team loses people because senior developers no longer want to work on it and junior developers cannot find their way.
Modernization is not rewriting.
Rewriting is almost always the wrong answer: it takes years, freezes new features, introduces new bugs, and almost never maintains functional parity with the original system.
Modernization is a gradual, surgical, and measurable process that reduces the cost of change one step at a time.
This category exists to bring method to this process: not slogans about cloud-native or microservices, but concrete tools to reduce risk and increase the pace of evolution of real systems.
Strangler Fig and other patterns for modernizing without stopping the business
The Strangler Fig is the most effective modernization pattern for systems in production: instead of rewriting everything, you build the new system around the old one, shift traffic gradually, and eliminate the legacy code as the new system becomes ready.
In practice this means building a proxy or facade that routes requests: those the new system can handle go to the new, the rest continue going to the legacy.
Migration happens feature by feature, release by release, without ever stopping production.
Other useful patterns: Branch by Abstraction for replacing internal components without changing external interfaces; Parallel Run for running the old and new systems in parallel and comparing results before the final cutover; Anti-Corruption Layer for isolating the new domain from the data models of the legacy system.
The common thread of all these patterns is incrementality: no big-bang releases, no moment when the entire system is broken at the same time, always a safe path back if something does not go as expected.
Migration from VB6 and .NET Framework to modern .NET: the real path
VB6 and .NET Framework (1.x-4.8) are the two most common legacy technologies found in the Microsoft ecosystem.
Migration strategies differ.
From VB6: there is no automatic migration. VB6 code does not convert reliably to C# or VB.NET: the language semantics, the COM model, ActiveX controls, and the typical VB6 patterns have no direct equivalents. The strategy is to first isolate the business logic (often buried in forms), rewrite it in .NET with tests, and then build the new UI on top of that logic. COM interop allows using existing VB6 components during the transition.
From .NET Framework to modern .NET: dotnet upgrade-assistant automates the mechanical part. The real work is on NuGet packages that do not support .NET 8/10, on removed APIs (System.Web is the most impactful for MVC web apps), and on multiple target frameworks if the library must temporarily support both.
The most underestimated factor in both cases is documenting implicit behaviors: the legacy system has behaviors that nobody has ever written down, that exist only in the code and in people's memory.
Before migrating, these must be documented as acceptance tests.
They are the tests that certify the migration preserved the real behavior.
How to measure modernization progress
Modernization without metrics becomes an endless mission where you never know whether you are moving forward or going in circles.
The metrics I use to track progress are practical and tied to the business.
The percentage of code covered by automated tests measures the reduction in regression risk.
The average time from feature specification to deployment measures the reduction in cost of change.
The number of production incidents per release measures stability.
The onboarding time for a new developer measures how understandable the system is.
These metrics do not all improve at the same time.
You choose the one with the greatest impact on the current cost and work on that one, measuring before and after each intervention.
The second tool is the technical debt map: a document that categorizes areas of the system by cost of modification and business impact.
High-cost, high-impact areas are modernized first.
Low-impact areas are left for last or ignored if the cost exceeds the benefit.
Modernization is an investment, not an expense.
It must be justified, planned, and measured like any other technology investment.
Analyses, cases, and articles on legacy, migration, and software modernization
1 articles foundWhen modernization becomes unavoidable
Modernization becomes unavoidable when every change costs too much, every release feels dangerous, and the software holds the company back instead of supporting it. At that point you need method, progressive migration, and technical decisions that reduce risk without stopping the business.
Key technologies for leaving legacy behind
What is VB.NET
Discover what VB.NET is: the Visual Basic programming language for developing applications with .NET Framework and .NET Core.
Why VB6 Migration
Discover why it's essential to migrate from Visual Basic 6: security, performance and maintainability for your business applications.
What is Visual Basic
Discover what Visual Basic is: the event-driven programming language for developing Windows applications quickly and intuitively.
Frequently asked questions
Modernization is worthwhile when the annual maintenance costs of the legacy system (bugs hard to fix, scarce developers, inability to use new libraries) exceed the estimated cost of porting. A simple indicator: if modifying a feature takes weeks instead of days, if onboarding a new developer takes months, or if the application cannot run in the cloud, modernization is economically justifiable.
The most common migration is from .NET Framework 4.x to .NET 8. The path is: dependency analysis with .NET Upgrade Assistant, replacement of incompatible libraries (e.g., System.Web with ASP.NET Core), project migration to SDK-style format, and progressive testing. For large applications the Strangler Fig technique is used: the new system is built alongside the old one, migrating module by module.
VB6 has no direct migration path to .NET. Options are: full rewrite in C# (costly but clean), conversion to VB.NET as a first step followed by migration to C#, or wrapping the existing VB6 application via COM interop in a .NET layer that calls it. The choice depends on complexity, budget, and availability of documentation on the business logic.
Technical debt is managed with a deliberate strategy, not a total rewrite. You identify the highest-cost areas (those causing the most bugs or most slowing down changes), estimate the reduction cost, and negotiate a fixed portion of team capacity dedicated to technical improvement (typically 20-30%). Without this explicit quota, debt grows faster than new features.
Sources and references
Martin Fowler, Strangler Fig Application
Fowler's Strangler Fig Pattern is the most practical and least risky architectural pattern for modernizing legacy systems without a big-bang rewrite. I cite it as a foundational reference because it describes gradually replacing old system functionality with new, keeping both active during the transition. It is the strategy I recommend in almost all modernization projects.
