Back to Blog
VB6 MigrationTypeScriptLegacy Modernization

Why We Skip .NET and Migrate VB6 Directly to TypeScript

April 20, 2026 | 18 min read

The Conventional Path and Why It Persists

When a legacy VB6 system needs to be replaced, consultants almost universally recommend the same two-step path: first migrate to VB.NET, then eventually move to .NET 8 or later. The pitch is irresistible. Same language syntax. Same Microsoft ecosystem. Familiar territory for the existing team. Low perceived risk.

We've migrated a dozen VB6 codebases. We've stopped recommending that path. Not because it can't work, but because in practice, for most organizations in 2026, it produces the worst possible outcome: a team that has spent 18 months migrating to a technology they'll need to migrate away from again in five years, with a codebase that's structurally identical to the VB6 original — now just running on a newer runtime.

This post is the argument we make to clients before we touch a line of code. Read it, push back on it, and then decide what the right path is for your system.

The Honest Case for .NET Migration

Before the contrarian argument, the steel-man. There are real reasons to choose .NET over TypeScript for a VB6 migration, and they matter.

If your codebase is deeply Windows-integrated, .NET is the right call. COM interop, Windows service dependencies, tight SQL Server stored procedure coupling, ActiveX controls — these have clean .NET equivalents with no equivalent in the JavaScript ecosystem. If you're running a VB6 application that is essentially a thick client to SQL Server, with business logic living in stored procedures, .NET will give you a straighter migration path to a working system.

If your team is .NET-native, the learning curve advantage is real. A team that writes C# every day and has never touched TypeScript or Node.js will produce lower-quality TypeScript than the .NET code they'd produce. Forcing a team to change languages mid-migration introduces risk that isn't purely technical — it's organizational.

If you need Windows desktop UI, WinForms and WPF have no TypeScript equivalent. If the end state is a Windows desktop application and not a web app, .NET is the answer.

The problem is that most VB6 applications don't fit these conditions. Most are business line applications: data entry forms backed by SQL Server, reporting, workflow management. The tight Windows dependency is incidental, not essential. And the team that built VB6 code — if they're still around at all — usually isn't an active .NET team. They're people who've been maintaining 20-year-old code in a language Microsoft stopped investing in a decade ago.

Why TypeScript Wins for Most VB6 Shops in 2026

The argument for TypeScript over VB.NET isn't that TypeScript is better in some abstract sense. It's that the practical advantages compound in ways that matter for the specific situation of a VB6 migration.

The learning curve is comparable. Modern VB.NET is not VB6. The syntax is similar on the surface, but the programming model is completely different. Dependency injection, async/await, nullable reference types, modern OOP patterns — these require just as much relearning as TypeScript does. The "familiar syntax" argument falls apart the moment your team tries to write idiomatic .NET 8 code instead of VB6-style imperative code in a .NET wrapper.

TypeScript, by contrast, has one genuine advantage for VB6 teams: the procedural style maps reasonably well. A VB6 Sub with local variables and sequential logic becomes a TypeScript function with typed parameters and a return type. The migration of individual logic units is often mechanical.

TypeScript runs everywhere you need to be. The web is where business applications are going. SvelteKit, Next.js, Remix — a TypeScript codebase can produce a web application without a separate frontend team, a separate language, or a WinForms-to-web translation step. Your VB6 form logic becomes a server-side handler. Your data access becomes a Prisma or Drizzle query. The output is a web application that works on any device without a VPN, without a Windows client install, without RDP.

A VB.NET codebase produces a .NET application. To get a web front-end, you're writing Blazor, or adding an ASP.NET layer, or maintaining two separate technology stacks. For organizations moving away from Windows-desktop-only deployments — which is most of them — this matters.

The tooling gap in 2026 is real and widening. AI-assisted development has become a first-class consideration in migration projects. GitHub Copilot, Cursor, and similar tools perform materially better on TypeScript than on VB.NET. The training data density in the JavaScript/TypeScript ecosystem dwarfs VB.NET. This matters practically: when a developer needs to write a new module, migrate a legacy function, or understand an unfamiliar pattern, the AI assistance available in a TypeScript codebase is significantly richer.

This is not a small thing. A migration project that takes 18 months in VB.NET might take 12 months in TypeScript specifically because AI-assisted code completion and refactoring is more reliable in that ecosystem.

The SQL Server dependency is not a TypeScript problem. The most common objection to TypeScript migration is database compatibility. VB6 shops are almost always on SQL Server. The assumption is that .NET has better SQL Server support.

It doesn't — not anymore. Drizzle ORM, Prisma, and the Microsoft JDBC driver for Node all have production-grade SQL Server support. Drizzle in particular has become the go-to for teams that need type-safe SQL without giving up the direct query control they've relied on in stored procedures. Parameterized queries, transactions, connection pooling — all covered. The stored procedure migration is a separate concern, but it's the same work whether you're migrating to .NET or TypeScript.

The AI-Assisted Migration Argument

One of the more interesting developments in the VB6 migration space is the emergence of AI-assisted code translation. Several vendors sell tools that claim to convert VB6 to VB.NET or C# automatically. The conversion quality ranges from "surprisingly useful" to "generates valid code that does the wrong thing."

We use AI assistance in our migrations, but not for wholesale translation. The problem with automated VB6-to-.NET conversion is that it can reproduce syntax without reproducing intent. Global variables, implicit type conversions, and On Error Resume Next error handling in VB6 map to structures in VB.NET that compile but carry the same hidden assumptions that made the original code fragile.

TypeScript migration forces the issue. The type system is strict enough that you can't accidentally import implicit behavior. Where VB6 had an integer that was sometimes a status code, sometimes a record ID, and sometimes zero because it was never initialized, TypeScript forces you to model the distinction. The migration becomes a design review of the original system, not just a syntactic translation.

When clients ask about using AI tools to accelerate the migration, we support it — but with the caveat that the output of any AI translation should be treated as a first draft to be reviewed and tested, not as a deliverable. AI-assisted migration cuts the mechanical work. It doesn't cut the judgment work.

How We Execute VB6 Migrations: The 4M Method

We use the same methodology across every VB6 migration we run. We call it MIRROR → MODEL → MIGRATE → MONITOR.

MIRROR is the most important phase and the most frequently skipped. Before a line of code is touched, we spend two to six weeks documenting what the existing system actually does. Not what the documentation says it does. Not what the team thinks it does. What the running system demonstrably does when presented with real production data.

This means sitting with power users, running queries against production data to find the edge cases, reverse-engineering stored procedures to understand the business rules encoded in them, and mapping every workflow that crosses module boundaries.

VB6 systems always contain undocumented behavior. Date calculations that assume a specific fiscal year boundary. String truncation that happens to match the database column width. Report totals that are right because two bugs cancel each other out. The MIRROR phase finds these before they become regression bugs.

MODEL is where we build the TypeScript skeleton. Interfaces for every entity. A test suite that exercises the business rules documented in MIRROR. We write the tests to match the behavior of the current system, including the intentionally-weird behavior that turns out to be correct. The tests are red before we write any implementation.

MIGRATE is the incremental module-by-module implementation. We never do a big-bang cutover. Each module goes through a cycle: implement in TypeScript, pass the test suite, run in parallel alongside the VB6 equivalent, compare outputs for a defined period, then flip the flag. The team is never more than one module away from a rollback.

MONITOR is the 60-to-90 day parallel run after the full migration is complete. The old system continues to run in read-only mode. Every production transaction is logged. We track output divergences. After the parallel run, the old system is decommissioned.

This methodology applies whether we're migrating to TypeScript or .NET. The difference is that TypeScript's strict type system makes the MODEL phase richer: the interfaces you define in MODEL become the enforcement mechanism in MIGRATE.

The Codebase Arithmetic

One number we consistently see: VB6 codebases lose 60 to 70 percent of their line count in a TypeScript migration. This isn't because TypeScript is terser than VB6. It's because VB6 codebases accumulate dead code, duplicated logic, and defensive coding patterns that aren't needed when you have a real type system doing the work.

A 200,000-line VB6 application typically becomes a 60,000 to 80,000-line TypeScript application that does the same thing, tested, with better error handling and a web UI that doesn't require a Windows client deployment.

The reduction matters beyond aesthetics. A smaller codebase is cheaper to maintain, easier to onboard new developers into, and less likely to harbor undiscovered bugs in unused code paths.

Where does the dead code come from? Three sources. First, VB6 systems tend to have module-level variables that are set in one code path and read in another, but the code path that reads them was removed years ago. The variable is still declared, still set, but the value is never used. TypeScript's strict unused variable checking eliminates these on the first compile pass.

Second, VB6 applications often have error handling copy-pasted across procedures — the On Error GoTo ErrorHandler pattern repeated hundreds of times, each with its own label and cleanup code. TypeScript's exception handling and result types replace these with a handful of shared error utilities.

Third, VB6 form applications accumulate event handlers for controls that were removed from forms long ago. The handler still exists in the .frm file. The control doesn't. When you migrate to a web interface, these handlers simply don't have anything to map to.

The line count reduction is a signal, not a goal. We've seen migrations where the team tried to preserve the line count as a sign of thoroughness. That's the wrong orientation. The right orientation is: the new system must do exactly what the old system does, tested, and nothing more.

Objections Addressed

"Our team knows VB.NET, not TypeScript."

The most common objection. See above: modern VB.NET is not the same programming model as VB6. The learning curve argument for .NET migration rests on surface-level syntax similarity. If your team currently writes production VB.NET code, that's a different conversation — but most VB6 shops don't have active .NET developers. They have people who maintain VB6 code.

The investment in learning TypeScript is similar to the investment in learning idiomatic modern .NET. The payoff is different: TypeScript opens the web ecosystem, full-stack development, and better AI tooling. VB.NET does not.

"We'd lose our COM interop layer."

If you genuinely need COM interop, this is a real constraint. Evaluate how much of the COM dependency is essential versus incidental. In our experience, roughly half of all COM dependencies in VB6 systems are to Microsoft Office (Excel for reports, Word for document generation). These have TypeScript equivalents that are often better than the original Office automation code, which was slow, brittle, and dependent on Office being installed on the server.

The other half vary. ActiveX controls in the UI are gone regardless — the web is the replacement. Windows API calls for file system operations, registry access, or system integration are more complex. If these are load-bearing, .NET may genuinely be the better path.

"TypeScript is a frontend language."

This was true in 2012. Node.js has been in production at Fortune 500 companies for over a decade. SvelteKit and Next.js run server-side. TypeScript is the dominant language for full-stack web development. The frontend/backend distinction in TypeScript has been gone long enough that it's no longer a meaningful objection.

"SQL Server isn't well supported outside .NET."

See the tooling section above. Drizzle and Prisma both have production SQL Server support. Microsoft publishes and maintains a first-party Node.js SQL Server driver. If you're already on SQL Server and want to stay there, this is a solved problem.

"What if we need to add a Windows service later?"

You can run Node.js as a Windows service with PM2 or as a Windows scheduled task. You can also run it on Linux, in Docker, or on a managed cloud service — which gives you more deployment options than a .NET Windows service, not fewer.

When .NET Is the Right Answer

We're not dogmatic about this. .NET is the right answer when:

The codebase has deep Windows-specific dependencies that aren't worth abstracting. Heavy COM interop to non-Office components. WMI queries. Windows authentication integrated into Active Directory in ways that require the full .NET Kerberos stack.

The end state is a Windows desktop application. If you need a WinForms or WPF replacement, that's what .NET is for.

The team is actively writing .NET code today and has senior .NET expertise. Forcing a skilled .NET team to migrate to TypeScript introduces risk for no benefit.

The existing .NET ecosystem is load-bearing. If you have a .NET library that does expensive custom calculations and it's been in production for fifteen years, porting it is risk for no gain. Keep it. Wrap it. Call it from TypeScript if you need to via a lightweight API layer, or call it from a .NET service that coexists with the TypeScript system.

Everything else being equal, we recommend TypeScript. But equal is rare. The honest recommendation depends on what "equal" actually means for your specific system, team, and target state.

Ready to Start?

If you're running VB6 in production and starting to plan a migration, we'd be glad to do a free technical review. We'll walk through your current system, identify the dependencies that matter, and give you an honest assessment of whether TypeScript or .NET is the better path — not the path that's more convenient for us to sell.

The review is an hour. We'll ask about your current infrastructure, your team's skillset, the nature of the Windows dependencies in the codebase, and what the target deployment looks like. At the end of the call you'll have a concrete recommendation and enough information to evaluate it independently.

Get a free technical review →

If you want to read about how this methodology works in practice, see the legal case management case study or reach out directly.

Need This Kind of Engineering?

We build the systems we write about. Let's talk about your project.