VB6 to .NET migration: what it costs and when it pays
Matteo Migliore

Matteo Migliore is an entrepreneur and software architect with over 27 years of experience developing .NET-based solutions and evolving enterprise-grade application architectures.

He has led enterprise projects, trained hundreds of developers, and helped companies of all sizes simplify complexity by turning software into profit for their business.

A window manufacturer in the province of Treviso, 64 employees and 22 million euros in revenue, manages quotes, orders, bills of materials, warehouse and production with a program written in Visual Basic 6 in 2003. 27 people use it: technical office, sales, warehouse and two shop floor supervisors. It runs on six PCs and a Terminal server, with a SQL Server 2008 R2 database that Microsoft has not updated for years. The person who wrote it is the founding partner of a small software house, is now 68 and answers the phone when he feels like it. The owner has a single question: is the migration from VB6 to .NET worth it, or do we keep going like this as long as it holds?

Two answers circulate, both uncomfortable. The first says VB6 is dead and everything must be rewritten: said by those who sell rewrites. The second says it has worked for twenty years so don't touch it: said by those who don't want to spend or don't want to decide. The truth is in the middle and it can be calculated. The VB6 runtime keeps working on Windows 11, so the program does not stop by itself. What stops is everything else: the tool to modify it, the third-party components, the database underneath, the people who know it. The problem is not the expiry date of a language. It is how much it costs every year to stand still and how much it costs to move.

With the Veneto case, which is composed and not a real client, you will see how to measure the bill in a week. The result surprises those who expect me to sell a migration: for a horizon under eight years the cheapest road is to secure the VB6 with 18,900 euros of work, which pay back in ten months. Migration by modules to C# becomes the cheapest choice from almost the eighth year, and earlier only if there are many users or if a stoppage costs a lot. The break-even is at 7.8 years and changes with four numbers that I show you one by one.

You will find the seven signs that say the program is at risk, the inventory of what it really contains, the honest answer to the question about automatic conversion, the five roads with their costs, the bill at one, three, five and eight years, how to replace one module at a time with a COM bridge between the old and the new, the currency, rounding and null value problems that make the numbers go wrong, the troubles that blow up estimates, and the mistakes I have seen repeated in twenty-six years of projects.

An honest premise. I have been writing software since 1999, I built and sold a multi-tenant legal management system and I also do this kind of work, so I know which side my interest is on. The case is composed, the assumptions are written one by one, and the result includes the case where migrating does not pay, neither with me nor with anyone else. Advice that does not tell you when not to spend is worth little.

When do you need to migrate from VB6 to .NET?

A VB6 program that works is not a problem to solve tomorrow. The problem is the set of conditions around it, and there are seven signs. With three or more we are in the zone where the risk starts to cost more than the change.

1. Nobody can modify it any more with supported tools

The VB6 development environment has been out of support since 2008. The runtime, that is what is needed to run the program, is instead guaranteed by Microsoft along with Windows 11. It is a difference that gets forgotten: the program runs, but every change is made with a 1998 installer on a PC nobody dares reinstall. In the Veneto case the only PC with a working environment is a 2014 laptop in the owner's drawer.

2. One person knows the program, or nobody does

This is the most serious sign and the least discussed. Three hundred and ten forms, written over twenty years by four people, with logic in the form code and in stored procedures, are knowledge that lives in one head. If that head stops answering, the company does not lose a program: it loses the ability to change the way it sells.

3. The database underneath is out of support

SQL Server 2008 R2 has received no security updates for many years. If the program is attached to it with an old data access library, updating the database means checking every query. This constraint is hidden: nobody sees it until an audit or an insurer asks.

4. Third-party components cannot be found

A twenty-year-old VB6 program contains OCX components bought in 2003: grids, calendars, charts, barcode readers. Many vendors no longer exist, and the 32-bit versions are no longer easy to install on new machines. If a component breaks, there is nobody to ask.

5. The program talks to nothing new

The link with accounting, with the carrier, with the bank, with the large retail customer who asks for data in EDI format: each is a text file copied by hand or a nightly job. When a customer asks for real-time data, the program cannot provide it. This is where the cost of "doing nothing" turns into a lost order.

6. New machines do not want it

VB6 is 32-bit, and produces 32-bit programs. On a new PC with Windows 11 the program starts, but printers, code readers and new drivers do not always cooperate, and every PC replacement becomes a small project.

7. Daily work costs too many minutes

Fourteen minutes lost every day per person between waiting, retyping, phone calls to the colleague who knows where a piece of data is, and parallel spreadsheets. It is the sign measured least and weighing most, and we will see it with the figure.

In the Veneto case the real signs are six out of seven: the development environment, the single person, the database, the components that cannot be found, the patched-up integrations and the lost minutes. With a result like this the question is not whether to think about it, but what to do and when. For the general horizon of the cost of an old program, the guide to the cost of legacy software shows how to do the bill for the generic case.

What is really inside your VB6 business application?

Before choosing a road you need an inventory, because the price of any solution depends on what is inside, and those who decide usually describe only what they see. A VB6 program contains six kinds of things, and each is replaced in a different way.

  • Forms: the .frm files with their code. Forms are redesigned, the code is read to understand what it decides.
  • Modules and classes: the .bas and .cls files where the calculations usually live, but also global variables that forms pass to each other.
  • Third-party components: the .ocx controls and the .dll libraries registered in the system.
  • Data access: ADO or DAO, queries written inside the code, sometimes logic inside stored procedures.
  • Printouts: Crystal Reports of an old version, or reports from the VB6 Data Report Designer.
  • Rules: customer discounts, minimum orders, rounding, reserved stock. They are the real asset and they are written only in the code.

In the Veneto case the inventory, done by reading the project in three days, gives these numbers.

ItemQuantityWhat it means for the cost
.frm forms31074 used every day, 96 once a month, 140 never: the latter are not migrated
Modules and classes88Inside there are 41 global variables the forms use to talk to each other
OCX and DLL components145 third-party, 2 of them with a vanished vendor
Crystal reports9631 really used, 14 are documents for customers and must be redone with care
Procedures and views in the database142In 27 there is logic the code takes for granted
Integrations7Accounting, a customer's EDI, a cutting machine, carrier, bank, electronic invoices, mail
Automated tests0The behavior must be captured before touching it

The most important row is the one with the forms never used: 140 out of 310. Almost half of the program does not exist in the bill, because nobody has opened it for years. This alone lowers the quote of a rewrite more than any negotiation, and it is discovered only from the access logs, not by asking users.

Another row not to skip is the global variables. Forty-one variables that forms pass to each other are the hidden state of the program. Whoever rebuilds it must decide where each one lives, and each is a point where the new can behave differently from the old without anyone noticing.

Can VB6 be converted automatically to VB.NET or C#?

It can, with a result that is not the one you hope for. There are commercial conversion tools that read the VB6 project and produce a VB.NET project that compiles. Microsoft's old wizard, the one built into Visual Studio up to version 2008, no longer exists in recent versions. Third-party tools do better, but two problems remain that no program solves.

The first is that the code produced is VB6 written in .NET syntax. It brings with it a compatibility library, the same global variables, the same form-based structure with the code inside. It compiles, runs, and has all the defects of before. Whoever must maintain it finds a program no .NET developer recognizes as their own.

The second is more subtle: where VB6 and .NET behavior differ, the converter does not always know it. The VB6 Currency type is not the .NET decimal, a Variant with a null value does not behave like a nullable type, Format(2.5, "0") rounds one way and CInt(2.5) another. The program converts and the calculation changes by one cent on one case in a thousand. You find out at month end, with the accounting office.

What automatic conversion doesWhat it does not do
Takes the project out of VB6 and its development environmentDoes not remove the global variables and the logic inside the forms
Produces a possible 64-bit program, with components replacedDoes not replace by itself the third-party OCX controls with no future
Costs less than a rewrite, though not littleDoes not reduce the lost minutes: the interface is the same
Gives a starting point for testsDoes not write the tests and does not find the calculation differences

Conversion has a place, and it is a precise place: when the program must live a few years, must leave the VB6 environment and the interface is fine as it is. In the bill that follows we will call it road C, and you will see that no horizon makes it the cheapest. It remains the choice of those who want to remove a maintainability problem without touching daily use.

How much does migrating from VB6 to .NET cost compared with not doing it?

To answer you need five roads compared, with the same assumptions. The model is in euros and can be redone with your numbers. The assumptions of the case are these.

  • Average cost of a development day: 420 euros. 220 working days a year and 32 euros an hour as the cost of an internal user.
  • Users: 27. Each minute lost per day for each of them is worth 27 × 220 ÷ 60 × 32 = 3,168 euros a year.
  • A one-week stoppage of the program costs 180,000 euros between blocked production, delivery delays and penalties. It is the most uncertain estimate, and we discuss it shortly.
  • Each road has an annual running cost: lost minutes, probability of stoppage and days of support.

A. Do nothing

Fourteen minutes lost per user per day are worth 44,352 euros a year. The probability of a serious stoppage in a year, with a development environment nobody can put back together, is 15%: 27,000 euros. The days of support from a consultant who every time has to rebuild the context are 18, that is 7,560 euros. Total: 78,912 euros a year, without spending a euro on a project.

B. Secure the VB6

Forty-five days, 18,900 euros. A rebuildable development environment is made in a documented virtual machine, the sources are put under version control (for those starting from a shared folder, the guide to source control with Visual Studio and Team Foundation Server explains the concepts), the two OCX controls from vanished vendors are replaced, the database is moved to a supported version of SQL Server checking the queries, tests are written on the 30 rules that move money and a backup is made that really restores. Afterwards, lost minutes drop from 14 to 12 and the probability of stoppage from 15% to 6%. The annual cost becomes 54,696 euros.

C. Automatic conversion to VB.NET

One hundred and sixty days plus 6,000 euros of tooling: 73,200 euros. You get a VB.NET program that runs in 64-bit, without the VB6 environment. The interface is the same, so lost minutes stay at 12. The probability of stoppage drops to 3%. Annual cost: 48,456 euros.

D. Migration by modules to C#

Five hundred and twenty days, 218,400 euros, plus 12,600 of double maintenance: 231,000 euros over twenty-two months. One module at a time moves to C# with a COM bridge and the same database, and each module goes live on its own. In the first two years the running cost is that of road B, because securing is part of the work. Afterwards, lost minutes drop to 4, stoppage to 2% and days of support to 4: 17,952 euros a year.

E. Complete rewrite

Six hundred and forty days, 268,800 euros. The old one stays in use until the new one is ready, and the risk is that it never arrives at covering everything. At steady state it costs the same as D, but it gets there with more spending and a single big release.

RoadInitial spendAnnual cost at steady stateWhat changes for the user
A. Do nothing078,912Nothing
B. Secure it18,90054,696Little: fewer stoppages and 2 minutes recovered
C. Automatic conversion73,20048,456Nothing visible
D. Migration by modules231,00017,952New forms one module at a time, 10 minutes recovered
E. Complete rewrite268,80017,952Everything new at once, after two years

The model is simple on purpose: the shape matters more than precision to the cent. For those thinking of starting from scratch with custom software, the bill with the quote items is in how much custom management software costs.

After how many years does it pay to migrate VB6 to .NET?

The cost of a road is not its initial spend: it is what you have paid after a certain number of years. Putting together initial spend and annual cost, the cumulative cost of the five roads is this.

HorizonA. NothingB. SecureC. ConversionD. Modules in C#E. Rewrite
1 year78,91273,596121,656285,696323,496
3 years236,736182,988218,568358,344396,144
5 years394,560292,380315,480394,248432,048
8 years631,296456,468460,848448,104485,904

The break-evens are four numbers.

  • Securing versus doing nothing: pays back in 0.8 years, about ten months. It is the only intervention whose convenience depends on nothing.
  • Migrating versus doing nothing: pays back in 5.0 years.
  • Migrating versus securing: pays back in 7.8 years. If the program must serve less than eight years, you secure it and that is all. If it must serve ten, you migrate.
  • Rewriting versus migrating by modules: never breaks even. The rewrite costs 37,800 euros more and leads to the same cost at steady state.

The chart shows the five lines: the curves of D and E start much higher but grow more slowly.

Cumulative cost over ten years of five choices for a VB6 business application with twenty-seven users: do nothing, secure it, automatic conversion, migrate by modules to C# and rewrite everything

Automatic conversion never wins: at two and a half years it overtakes "do nothing", but it stays above securing until almost nine years, and by then migration by modules already costs less. It is the road that spends a lot and changes little, and this is why I rarely recommend it.

What moves the break-even

The number 7.8 is not a law, it is the result of those assumptions. Changing one at a time, here is what happens to the break-even between securing and migrating.

If this changes...The break-even between securing and migrating becomes
Users drop from 27 to 1211.4 years
Users rise from 27 to 505.6 years
A stoppage costs 400,000 euros instead of 180,0006.7 years
A stoppage costs nothing9.2 years
Migration costs 400 days instead of 5206.4 years
Migration costs 680 days9.6 years
After migration 8 lost minutes remain instead of 410.8 years
Even after securing, the risk of stoppage stays at 12%6.5 years

The observation that matters is that two numbers weigh more than all the rest: the minutes that are really recovered and the cost of a stoppage. A program used by twelve people is not migrated for convenience, it is secured. A program used by fifty people, in a company where a stoppage halts production, is migrated even at a short horizon. And if the lost minutes after migration do not really drop, the whole bill falls apart: the number to defend in the project is this one, not the delivery date.

If you want a wider comparison between replacing and cleaning up code, the guide on rewriting or refactoring legacy software puts the two choices on the same plane. And if your case calls for an intervention on the system as a whole, the path is on the page about legacy software modernization.

How do you migrate VB6 to .NET without stopping the company?

The method is called strangler fig: you build the new around the old, one module at a time, until the old is no longer needed and is switched off. The guide to the strangler fig pattern explains why and when it works. For a VB6 desktop program the obstacle is not a network proxy, as in web portals, but the fact that the old form and the new one must coexist on the same computer and share calculations and data.

The technical solution is COM, the same technology VB6 uses to talk to its components. The new C# code is compiled as a library that VB6 sees as any other component. VB6 calls it with CreateObject, without knowing that underneath there is .NET 10. The architecture is this.

The COM bridge between the VB6 application and the new C# modules: COM library and shared services, a single SQL Server database, behaviour tests and rules register

1. The new COM library, 32-bit

VB6 is 32-bit, so the new library must be compiled as 32-bit even if the rest of the world is 64. It is a detail that costs a day to those who do not know it. The project is declared like this.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net10.0-windows</TargetFramework>
    <PlatformTarget>x86</PlatformTarget>
    <EnableComHosting>true</EnableComHosting>
    <EnableRegFreeCom>true</EnableRegFreeCom>
  </PropertyGroup>
</Project>

The file produced has a .comhost.dll extension and is registered once with regsvr32, or the registration-free manifest is used so as not to touch the PC's registry. Inside the library live the calculation rules, written once and used by everyone.

using System.Runtime.InteropServices;

namespace Serramenti.Servizi;

[ComVisible(true)]
[Guid("5B1B7C3E-2D44-4C0A-9A66-0B7F2E5E8A11")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IPrezzi
{
    // Currency di VB6 arriva come decimal: nessuna perdita di precisione.
    decimal PrezzoRiga(string codiceArticolo, decimal quantita, string codiceCliente);
}

[ComVisible(true)]
[Guid("A7D0F6C2-91B3-4E8D-B2C4-6F4E3D1A9C07")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("Serramenti.Prezzi")]
public sealed class Prezzi : IPrezzi
{
    public decimal PrezzoRiga(string codiceArticolo, decimal quantita, string codiceCliente)
    {
        var listino = Listini.Corrente(codiceArticolo);
        var sconto = ScontiCliente.Percentuale(codiceCliente, codiceArticolo);
        var riga = listino * quantita * (1 - sconto);
        // Il VB6 arrotondava con la regola del banchiere: lo stato di fatto si conserva.
        return Math.Round(riga, 2, MidpointRounding.ToEven);
    }
}

On the VB6 side the change is one line. Where before there was the calculation written in the form, now there is the call.

' Prima: il calcolo stava nella maschera, in 140 righe.
' Dopo: la maschera chiede il prezzo al servizio condiviso.
Dim prezzi As Object
Set prezzi = CreateObject("Serramenti.Prezzi")
txtImporto.Text = Format(prezzi.PrezzoRiga(txtArticolo.Text, CDec(txtQta.Text), cboCliente.Value), "#,##0.00")

The step has two effects. The calculation leaves VB6, so it is written and tested by a person working with modern tools. And the new C# form that will arrive in a few months will use the exact same function, so the old and the new can no longer diverge on prices.

2. The database stays one

The database is not copied and no synchronization is done. Old and new read and write the same tables, which is the reason the move can be done one module at a time. The price is a rigid rule: for each table there is a single writer, either the old or the new. When a module moves, it moves with its tables, and from then on the old one only reads them.

3. The first module is a lookup

You start with a module that shows data and does not write any, for example article search or the customer card. It tests the bridge with little risk and gives users a visible result. If it goes wrong, you go back by changing one menu line.

4. The new interface

New forms are written in WinForms or WPF. For a business application with many grids and many fields, WinForms on .NET 10 is a sensible choice and quick to learn for those coming from VB6. If the company wants a more polished interface, WPF triggers show how to make an interface reactive without filling it with code. For the decision between keeping, porting and rewriting a Windows Forms program, I wrote a separate comparison in Windows Forms in 2026.

5. The order of the modules

You start with the low-risk modules that show a visible value, not with the most important ones. A good order, in the Veneto case, is this.

MonthModuleWhat you get
1-3Securing, COM library, tests on the 30 critical rulesThe program is unchanged, but now a module can be moved
4-6Master data and article searchSearches go from 6 seconds to under one
7-10Quotes and pricesA single price calculation, written once and proven
11-14Orders and bills of materialsThe most complex area, run in parallel for two weeks with two pilot users
15-18Warehouse and productionThe real-time link with the cutting machine
19-21Printouts, remaining integrations, accountingThe last reason to keep the old one is removed
22VB6 switch-offThe risk it carried is removed

For a program of this size twenty-two months are needed, and every quarter something changes for the users. The difference from the rewrite is not the duration, it is that from the fourth month each module pays off on its own, and that if the project stops at the twentieth month the company still has twenty months of value.

How do you carry over calculations and data without changing a cent?

The most concrete danger of a VB6 migration is not that the program does not start. It is that it starts and a total is off by one cent. The accounting office finds out at month end and from then on does not trust the new one. There are four differences I have seen do damage, and they must be written in a test before touching anything.

Currency is not decimal, but it comes close

The VB6 Currency type is a 64-bit integer divided by ten thousand: four exact decimals, without floating point errors. Double instead gets it wrong, and a program that adds up amounts in Double accumulates errors that the old one hid with a final rounding. In C# the right choice is always decimal. The risk is twofold: where the old one used Double and rounded, the new must do the same thing, even if it is "wrong".

Rounding is not just one

In VB6 Round and CInt round to the nearest even, banker's rule: 2.5 becomes 2 and 3.5 becomes 4. Format instead rounds half up: Format(2.5, "0") gives 3. A program written over twenty years uses the two things in different places, and the total of an invoice depends on which of the two was used. In C# Math.Round uses banker's rule by default, and to get half up you pass MidpointRounding.AwayFromZero. The choice is made line by line, looking at what the old one did.

Null is not empty

A database column that is NULL arrives in a VB6 Variant as a null value, which propagated in a calculation gives null, while in many places the program treated it as zero with IIf(IsNull(x), 0, x). In C# a nullable column does not become zero by itself. Every point where the old one did this implicit conversion is a rule that someone must have decided and nobody wrote down.

Dates, strings and the time zone that did not exist

The VB6 Date type has no time zone and does not distinguish date from time. The fixed-length strings of the old file format are padded with spaces. The conversion character set of text files is ANSI, not Unicode. They are tiny differences and each is an integration file that the cutting machine stops reading.

The cure is always the same: you write a test on the behavior of the old one and rerun it on the new one. A test on the price of a line, with the three roundings lined up, is this.

using Xunit;

public class PrezziTest
{
    // I valori attesi sono quelli che il programma VB6 ha prodotto in fattura,
    // non quelli che sembrano giusti a una persona.
    [Theory]
    [InlineData("A-1042", 2.5, "C-0007", 187.50)]
    [InlineData("A-1042", 3.5, "C-0007", 262.50)]
    [InlineData("P-0310", 12, "C-0112", 1043.28)]
    [InlineData("P-0310", 0.5, "C-0112", 43.47)]
    public void IlNuovoPrezzoCoincideConQuelloDelVecchio(
        string articolo, decimal quantita, string cliente, decimal atteso)
    {
        var prezzi = new Serramenti.Servizi.Prezzi();

        Assert.Equal(atteso, prezzi.PrezzoRiga(articolo, quantita, cliente));
    }
}

The expected values are taken from the archive: two hundred lines of real invoices from the last twelve months, with article, quantity, customer and amount. The edge cases are chosen, those with cascading discounts, fractional quantities and customers with a special price list. The price test in the Veneto case had 214 lines, and on the first run six failed. They were the six rules nobody had ever written down.

What are the problems that blow up estimates?

Every migration estimate has a margin of error, and almost all of it falls on six problems that can be seen in advance. In the Veneto case they are worth about 90 days out of 520.

1. OCX components without a vendor

Two of the five third-party components have a vanished vendor: the grid with frozen columns and the planning calendar. They are not carried over, they are replaced, and the grid is inside 60 forms. A replacement grid does not behave the same and every difference is a bit of training. Cost: 20 days.

2. Printouts and customer documents

Ninety-six reports, 31 of them used. Fourteen are documents the customer receives, with their layout, their logo and their legal wording. They are redone with the care of an interface, and compared on screen with the original. Cost: 22 days.

3. Logic hidden in stored procedures

In 27 procedures out of 142 there is a rule the code takes for granted: a credit block, a discount, a lot assignment. Nobody mentions it, and the day the old one disappears, the rule disappears with it. Cost: 18 days of reading and testing.

4. File integrations

Seven links, three of them with a fixed-format text file that a cutting machine reads every fifteen seconds. The format cannot be changed, every character counts, and the new one must produce it identical. Cost: 15 days.

5. Hardware and drivers

The barcode reader in the warehouse, the label printers and a badge reader connect with 32-bit drivers written for VB6. The new one uses them in a 32-bit process or replaces them. Cost: 8 days, or a purchase.

6. The global variables

Forty-one variables that forms pass to each other. Each must be read, assigned to a module and turned into an explicit parameter. It is tedious work full of surprises, because the same variable is written in ten places. Cost: 7 days.

The sum is 90 days, 17% of the total. It is the reason no serious quote gives a fixed price before the inventory, and why a quote that arrives two hours after a phone call is a figure put there to win the tender.

How do you decide in a week?

The path I use with a client is always the same, and it takes five working days.

Day 1: the horizon in years

One question to the owner: for how many years do you plan to use this program? Not "as long as it works", but a number. Ten years and three years are two different projects. If the answer is three, everything else gets simpler: you secure it and that is all.

Day 2: counting the minutes

You sit for an hour next to six users and note every time they wait, work around, retype or phone for a piece of data the program does not give. You time it. The number that comes out, in minutes per user per day, is worth 3,168 euros a year for each minute with 27 users. It is the most underestimated number of the project, and it is usually the highest.

Day 3: the inventory

The goal is the table seen above. You count forms, modules, components, reports and procedures, you check which forms are used from the access logs (not from what people say) and you list integrations and global variables. It takes three days if you start from zero and one if the logs already exist.

Day 4: the rules and the stoppage

You extract the business rules from the code and the procedures, write them in plain language with a numeric example for each, and have the people who work with them read them. Then you ask an uncomfortable question: what happens if the program stops for a week? You write the answer in euros, with the production manager, not with the IT person.

Day 5: the bill and the decision

With horizon, minutes, inventory, rules and cost of stoppage you fill in the comparison of the five roads. If the break-even between securing and migrating falls after the horizon, you secure. If it falls before, you migrate, and you choose the first module.

At the end you have five things you did not have before: a horizon, a figure for lost minutes, a list of rules, a value for the stoppage and an inventory with numbers. With these the discussion is no longer "VB6 yes or no", but "which road reduces cost and risk in the coming years".

What mistakes are repeated in VB6 migration?

In twenty-six years of projects the same mistakes come back. I list eight, with how to avoid them.

  • Starting from the most important module. You start from a lookup, which tests the bridge with little risk.
  • Writing the new without tests on the old. If you do not know what the old one does, you do not know whether the new one does it. The expected values are taken from real invoices.
  • Copying the database "to be safe". Two copies of the same data diverge within a week. One writer per table, always.
  • Migrating forms nobody uses. They are 45% of the forms and zero of the value.
  • Relying only on automatic conversion. It gives a program that compiles and that nobody knows how to maintain, and leaves the lost minutes intact.
  • Changing the interface and the calculations together. If something does not add up you do not know whether the form or the rule is at fault. First you move the calculation, then you redesign the form.
  • Forgetting the hardware. Code readers, label printers and scales are the first place where the new one gets stuck.
  • Not measuring the gain. If you do not measure the lost minutes before and after, you cannot prove the project worked, neither to yourself nor to whoever pays.

Another mistake, less technical: deciding from the title of an article. This one too, included, shows a composed case. Your program has another curve, and the only way to know is to put your numbers in the table.

What would the Veneto company do today?

Back to the window manufacturer. The owner had a question and a programmer who answers the phone when he feels like it. With the bill in hand he would have seen that "doing nothing" costs 78,912 euros a year, that securing pays back in ten months, and that migration by modules pays back against securing in 7.8 years.

He would have decided in two steps. Right away, 45 days: rebuildable development environment, sources under version control, replacement of the two OCX controls without a vendor, database moved to a supported version, tests on the 30 rules that move money, backup really restored. It costs 18,900 euros, pays back in a year and removes the most urgent risk. In parallel, the COM bridge and the first module, article search and master data, because they are the slowest forms and the ones sales open the most. With that module in production, the decision on the rest is taken with minutes recovered that are measured and not estimated.

The complete migration, 231,000 euros over twenty-two months, becomes the right choice if the company plans to keep the program for ten years, if users rise toward fifty, if a stoppage costs more than 400,000 euros or if the programmer stops sooner than expected. It is not the right choice if the reason is "VB6 is old": that reason, by itself, is not a number.

In any case he would have decided with a number and not with a salesperson's charm, and he would still have the right road open in two years. This is the difference, and it is worth more than the outcome.

If you have a VB6 business application and want to know which zone your company falls in, the path starts from the page on legacy software modernization, which also includes the cases where the answer is to touch nothing. For those starting from other languages, the bill is in migration from Delphi to .NET and in migration from ASP.NET WebForms to Core, and if the starting point is an Access database the guide is replacing Access with a management system. For the technical details of data access there is also the guide to migrating databases from VB6 to .NET. And if you need someone to look at the numbers with you, there is software consulting.

In five lines. Count how many of the seven signs apply to your program and for how many years nobody has known it. Measure the minutes lost every day per user: they are worth 3,168 euros a year each with twenty-seven users. Secure first, before anything else: rebuildable environment, sources under version control, a backup that restores. Extract the rules from the code and the procedures and write the tests on the old one before talking to any vendor. Migrate one module at a time with a COM bridge where the number of users, the cost of a stoppage and the horizon justify it.

Frequently asked questions

For a business application with 27 users and 310 forms, migrating by modules to C# costs about 231,000 euros (520 days, twenty-two months). A full rewrite costs 268,800 euros, automatic conversion 73,200 and securing VB6 18,900.

It can with third-party tools, but the result is VB6 written in .NET syntax: same global variables, same logic inside the forms, same calculation differences on Currency, rounding and nulls. It costs 73,200 euros in the case described and never wins at any horizon.

Under eight years it is better to secure it: 18,900 euros that pay back in ten months. The break-even between securing and migrating is at 7.8 years in the case described and drops to 5.6 years with fifty users.

With a COM bridge: a 32-bit C# library that VB6 calls with CreateObject, one shared database with a single writer per table and one module at a time, starting from a read-only screen. In the case described it takes twenty-two months.

Four: Currency against decimal, rounding (Round and CInt use banker's rounding, Format rounds half up), Null values treated as zero and dates without a time zone. You write tests on the behaviour of the old and run them again on the new.

Six: OCX components with no vendor, reports and customer documents, logic hidden in stored procedures, fixed-format file integrations, 32-bit hardware drivers and global variables. In the case described they are worth about 90 days out of 520.

Leave your details in the form below

Matteo Migliore

Matteo Migliore is an entrepreneur and software architect with over 27 years of experience developing .NET-based solutions and evolving enterprise-grade application architectures.

Throughout his career, he has worked with organizations such as Cotonella, Il Sole 24 Ore, FIAT and NATO, leading teams in developing scalable platforms and modernizing complex legacy ecosystems.

He has trained hundreds of developers and supported companies of all sizes in turning software into a competitive advantage, reducing technical debt and achieving measurable business results.

Stai leggendo perché vuoi smettere di rattoppare software fragile.Scopri il metodo per progettare sistemi che reggono nel tempo.