Migração de legado · Método

Conversor automático de Delphi, VB6 ou .NET para C# funciona? Por que não convertemos código

A promessa é tentadora: aponte a ferramenta para o código antigo e receba um projeto em C#. A conversão automática de código até entrega algo que compila. O problema é o que ela carrega junto — e é por isso que, na NasteSoft, a migração de software legado começa por entender o sistema, não por traduzi-lo.

Por NasteSoft Leitura de 12 min

A promessa do conversor

Quem tem um ERP em Delphi 7 ou um sistema comercial em Visual Basic 6 já ouviu a oferta, ou já procurou por ela: um conversor Delphi para C#, uma ferramenta para converter VB6 para C# ou VB.NET, um utilitário que "atualiza" o .NET Framework para o .NET moderno. Em poucas semanas, milhares de linhas traduzidas. Sem reescrever regra, sem reentrevistar ninguém.

A conta parece óbvia. Reescrever é caro e arriscado; traduzir é rápido. Se a sintaxe muda e a lógica fica, por que não automatizar?

Porque a lógica não fica sozinha. Junto com ela vêm a arquitetura, os vícios, os componentes, as dependências implícitas e as decisões que ninguém lembra de ter tomado. Um conversor traduz como o código está escrito, não o que o sistema faz. E é exatamente o "como está escrito" que tornou o sistema difícil de manter.

Um exemplo concreto: o botão que faz tudo

O trecho abaixo é ilustrativo — escrito para este artigo, não tirado de nenhum cliente —, mas qualquer pessoa que já abriu um sistema Delphi dos anos 2000 vai reconhecer o padrão. Imagine uma distribuidora com um ERP em Delphi 7. Na tela de pedidos, o botão "Calcular" faz isto:

// Delphi 7 — exemplo ilustrativo
procedure TFrmPedido.Button1Click(Sender: TObject);
var
  Total, Desc: Double;
begin
  Query1.Close;
  Query1.SQL.Text := 'SELECT SUM(QTD * PRECO) FROM ITENS WHERE PEDIDO = ' + Edit3.Text;
  Query1.Open;
  Total := Query1.Fields[0].AsFloat;

  if (ComboBox2.ItemIndex = 1) and (Total > 5000) then
    Desc := Total * 0.05
  else
    Desc := 0;

  if DM.TabClientesUF.AsString = 'PR' then
    Total := Total - Desc + (Total * 0.012)
  else
    Total := Total - Desc;

  Label7.Caption := FormatFloat('#,##0.00', Total);
  Query2.SQL.Text := 'UPDATE PEDIDOS SET TOTAL = ' + FloatToStr(Total) +
                     ' WHERE NUM = ' + Edit3.Text;
  Query2.ExecSQL;
end;

Em vinte linhas há uma consulta ao banco montada por concatenação, uma regra comercial (5% de desconto quando a opção de índice 1 do combo está selecionada e o pedido passa de 5.000), uma regra regional que depende de um registro posicionado num DataModule em outra unidade, formatação de tela e uma gravação no banco. Tudo dentro de um evento de clique.

Agora veja o que um conversor de sintaxe tipicamente produz em C# (também ilustrativo):

// C# gerado por conversão automática — exemplo ilustrativo
private void Button1_Click(object sender, EventArgs e)
{
    double Total, Desc;
    Query1.Close();
    Query1.SQL.Text = "SELECT SUM(QTD * PRECO) FROM ITENS WHERE PEDIDO = " + Edit3.Text;
    Query1.Open();
    Total = Query1.Fields[0].AsFloat;

    if ((ComboBox2.ItemIndex == 1) && (Total > 5000))
        Desc = Total * 0.05;
    else
        Desc = 0;

    if (DM.TabClientesUF.AsString == "PR")
        Total = Total - Desc + (Total * 0.012);
    else
        Total = Total - Desc;

    Label7.Caption = DelphiCompat.FormatFloat("#,##0.00", Total);
    Query2.SQL.Text = "UPDATE PEDIDOS SET TOTAL = " + DelphiCompat.FloatToStr(Total) +
                      " WHERE NUM = " + Edit3.Text;
    Query2.ExecSQL();
}

Compila. Provavelmente roda. E é o mesmo código. Query1 não é um objeto do .NET: é uma classe da biblioteca de compatibilidade que a ferramenta entrega para imitar o TQuery do Delphi — é comum que conversores tragam uma camada assim, porque sem ela o código traduzido não teria onde se apoiar. O SQL continua concatenado e aberto a injeção. O dinheiro continua em double, com os erros de arredondamento de ponto flutuante. A regra de desconto continua escondida atrás de ComboBox2.ItemIndex == 1, e ninguém sabe dizer, olhando o código, que o item 1 significa "pagamento à vista".

E há um detalhe mais sutil: o acréscimo de 1,2% é calculado sobre o total antes do desconto, não depois. Intencional ou bug de quinze anos atrás? O conversor não pergunta. Ele copia.

O que o conversor carrega junto

O exemplo acima é um caso pequeno de um problema estrutural. Numa migração de sistemas legados de verdade, o que vem junto na tradução automática costuma ser isto:

  • Arquitetura acoplada. Regra de negócio dentro de evento de tela, acesso a banco espalhado por centenas de formulários, estado global em DataModule ou variáveis públicas de módulo. Traduzir mantém tudo grudado — e é justamente esse acoplamento que impede testar, integrar e evoluir.
  • Componentes sem equivalente. Grades, geradores de relatório e controles de terceiros (em Delphi, suítes de grid e de relatório; em VB6, controles ActiveX/OCX e relatórios em Crystal Reports) não têm tradução direta. Ou o conversor emula, criando dependência de uma biblioteca que só ele mantém, ou deixa um buraco para o time preencher à mão.
  • Semântica que não traduz. No VB6, Variant e conversões implícitas fazem "10" * "2,5" funcionar de acordo com a configuração regional do Windows, e On Error Resume Next faz o código seguir em frente depois de um erro, linha após linha. Para reproduzir isso fielmente em C#, a ferramenta precisa envolver cada instrução num tratamento de erro — e o código fica ilegível — ou simplificar, e aí o comportamento muda sem ninguém perceber.
  • Configuração global invisível. No exemplo Delphi, FloatToStr numa máquina em português gera vírgula decimal, e o UPDATE quebraria — a menos que, em algum lugar da inicialização do programa, alguém tenha trocado o separador decimal global. Esse tipo de dependência não aparece no trecho convertido.
  • Nomes que não dizem nada. Button1, Edit3, Query2, Label7. O conversor preserva os nomes porque não tem como saber o que eles significam. O time herda um código onde nenhum identificador explica a intenção.
  • Falta de testes. O sistema original quase nunca tem testes automatizados. O convertido também não terá. Ou seja: não existe como provar que o C# faz o mesmo que o Delphi fazia, a não ser usando o sistema em produção.

"Compila" não é "funciona"

O relatório final de muitas conversões é um número: tantos por cento do código convertido sem erro de compilação. É uma métrica fácil de medir e quase irrelevante. Compilar prova que a sintaxe está correta. Não prova que o desconto continua sendo aplicado no caso certo, que o arredondamento bate com o fiscal, que a ordem dos eventos de tela continua disparando a validação antes da gravação.

Em sistemas desktop antigos, boa parte do comportamento vive em lugares que um tradutor linha a linha não enxerga bem: a ordem em que eventos como OnExit, OnChange e BeforePost disparam, campos calculados de TDataSet, triggers no banco, o efeito colateral de um formulário que fica aberto escondido. A diferença aparece em produção — no fechamento do mês, numa nota fiscal, num cliente específico.

A pergunta que importa

Não é "quanto do código foi convertido?", e sim "como sabemos que o sistema novo calcula igual ao antigo?". Se a resposta for "vamos ver quando os usuários usarem", o risco foi só adiado.

O custo escondido: manter um código que ninguém escreveu

O motivo pelo qual uma empresa quer sair de Delphi ou VB6 raramente é a linguagem em si. É que cada mudança demora, ninguém tem segurança para mexer e contratar quem conhece ficou difícil. A conversão automática resolve o último ponto pela metade — agora o código está em C#, e há muito mais gente que sabe C# — e piora os outros dois.

O desenvolvedor C# que recebe o projeto convertido não reconhece nada ali como C# idiomático. Não há camadas, não há injeção de dependência, não há separação entre tela e regra, há uma biblioteca de emulação que ele nunca viu. Ele não escreveu esse código, o autor original também não (foi uma ferramenta), e quem entendia o sistema antigo entendia em Delphi. É um legado novo, numa linguagem nova — com a diferença de que agora nem o histórico do código ajuda.

É comum, nesse ponto, a empresa concluir que precisa refatorar o resultado da conversão. Refatorar código convertido é, na prática, fazer a migração de verdade — só que partindo de um material pior que o original e já tendo gastado o orçamento da conversão.

Quando uma ferramenta automática ajuda

Nada disso quer dizer que ferramenta automática seja inútil. Uma boa ferramenta de migração de sistema legado é valiosa como instrumento de leitura, não como estratégia. Usamos automação, nossa e de terceiros, para:

  • Inventário. Listar formulários, units, módulos, componentes usados, consultas SQL embutidas (em Delphi, muitas ficam nos arquivos .dfm, na propriedade SQL dos componentes de consulta), referências e controles ActiveX declarados no projeto VB6.
  • Mapa de dependências. Quem chama quem, quais telas tocam quais tabelas, onde está o código morto que ninguém mais executa.
  • Pedaços isolados de cálculo. Uma função pura de cálculo — juros, rateio, dígito verificador — pode ser traduzida automaticamente como ponto de partida, desde que venha acompanhada de testes que comparem a saída nova com a antiga.
  • Atualização mecânica. No .NET Framework, há ferramentas da própria Microsoft que ajudam a converter arquivos de projeto e referências de pacote para o .NET moderno. Isso vale para bibliotecas e código sem interface; WebForms e WCF no lado do servidor não existem no .NET moderno, então essas partes precisam ser reescritas (em ASP.NET Core, Web API ou gRPC, ou com CoreWCF, que é mantido pela comunidade).

A diferença é o papel: a ferramenta produz insumo para quem vai entender e decidir, não o produto final.

E a IA? Ela converte?

É a pergunta que mais ouvimos hoje. Modelos de linguagem traduzem trechos de Delphi ou VB6 para C# muito melhor que os conversores baseados em regras: dão nomes melhores, usam decimal para dinheiro, parametrizam o SQL. Para um trecho isolado, o resultado costuma ser bom.

Mas o problema arquitetural continua igual. Se você pedir para a IA traduzir o Button1Click acima, ela vai devolver um Button1Click mais bonito — ainda com a regra dentro do evento, ainda dependendo de um registro posicionado em outro lugar. Traduzir tela por tela, com ou sem IA, reproduz a estrutura da tela. E a IA tem um risco a mais: ela corrige coisas por conta própria. Se o acréscimo sobre o total bruto era intencional e ela "arrumar" para o total líquido, o código fica mais limpo e o cálculo fica errado.

Por isso usamos IA onde ela é forte: ler. Descrever o que uma rotina faz, mapear dependências, listar candidatos a regra de negócio, rascunhar documentação, sugerir casos de teste. Isso transforma semanas de leitura em dias. A validação — "é isso mesmo que o sistema deve fazer?" — é feita por gente, com quem opera o processo. IA é acelerador da leitura, não conversor.

A alternativa: entender, documentar, reescrever

O método da NasteSoft é o oposto da conversão: primeiro entender como o sistema funciona de verdade, depois documentar, só então escrever código novo.

  1. Entender. Código, banco (tabelas, triggers, procedures), telas efetivamente usadas, relatórios, logs e conversa com quem opera. A pergunta não é "o que este código diz", é "o que este sistema faz pela empresa".
  2. Documentar. Cada regra vira uma ficha com identificador, onde foi encontrada e um exemplo numérico validado com o usuário. O "acréscimo sobre o bruto" do exemplo vira uma pergunta explícita, com resposta registrada. Detalhamos como fazemos isso em como documentar um sistema legado que ninguém documentou.
  3. Reescrever na arquitetura certa. A regra sai da tela e vira código de domínio testável; a tela vira só tela.

Para o mesmo cálculo do exemplo, o resultado fica mais ou menos assim (ilustrativo):

// C# reescrito — exemplo ilustrativo
// R-012: desconto de 5% para pagamento à vista acima de R$ 5.000
// R-013: acréscimo regional de 1,2% para clientes do PR, sobre o total bruto
//        (confirmado com o financeiro: é sobre o bruto, não sobre o líquido)
public sealed class CalculadoraTotalPedido
{
    public decimal Calcular(decimal subtotal, CondicaoPagamento condicao, string uf)
    {
        var desconto = condicao == CondicaoPagamento.AVista && subtotal > 5000m
            ? subtotal * 0.05m
            : 0m;
        var acrescimo = uf == "PR" ? subtotal * 0.012m : 0m;
        return subtotal - desconto + acrescimo;
    }
}

[Fact]
public void Pedido_a_vista_acima_de_5000_no_PR()
{
    var total = new CalculadoraTotalPedido().Calcular(6000m, CondicaoPagamento.AVista, "PR");
    Assert.Equal(5772.00m, total); // 6.000 − 300 + 72, igual ao sistema antigo
}

A regra tem nome, tem rastreabilidade até a ficha da documentação, tem teste que prova que bate com o sistema antigo e pode ser chamada por uma tela web, um aplicativo ou uma integração. É isso que se compra numa migração — não a troca de linguagem.

E nada disso acontece numa virada única. O sistema novo assume uma parte por vez, rodando ao lado do legado (o padrão de estrangulamento), com cada etapa reversível. Para cada tecnologia há particularidades: veja como tratamos a migração de sistemas Delphi, a migração de sistemas VB6 e a migração do .NET Framework para o .NET moderno. A NasteSoft e seus parceiros já migraram bancos de dados e aplicações em Delphi, VB6 e .NET — e em nenhum desses casos o caminho foi traduzir linha a linha.

Conclusão

O conversor automático responde à pergunta errada. Ele resolve "como colocar este código em outra linguagem", quando o problema da empresa é "como ter um sistema que dá para manter, testar e evoluir". Código traduzido compila e carrega junto tudo o que tornava o original difícil.

A migração de software legado que funciona começa antes do código: entendendo o sistema, documentando as regras e só então reescrevendo, por partes. Leva mais pensamento no começo e muito menos dor depois — e deixa com a empresa uma documentação que continua valendo mesmo que a tecnologia mude de novo.

Recebeu uma proposta de conversão automática?

Mande o cenário. Fazemos uma leitura honesta do que a conversão resolveria, do que ela deixaria para trás e de qual seria o caminho alternativo.

FAQ

Perguntas frequentes.

Existe conversor automático de Delphi para C# que funcione?

Existem ferramentas que traduzem a sintaxe, e muitas entregam código que compila. O que nenhuma faz é mudar a arquitetura: a regra de negócio continua dentro do evento do botão, o SQL continua montado na tela e os componentes antigos continuam lá, agora imitados por uma biblioteca de compatibilidade.

O resultado roda, mas é um sistema legado novo, numa linguagem nova, que ninguém do time escreveu.

Converter VB6 para C# automaticamente é mais rápido que reescrever?

No primeiro mês, sim. Depois, quase sempre não. O tempo economizado na tradução volta como depuração de diferenças de comportamento (Variant, On Error Resume Next, conversões implícitas que dependem da configuração regional) e como manutenção cara de um código que o time não entende.

A conta precisa considerar os anos de manutenção depois da virada, não só a semana da conversão.

Então ferramenta de migração de sistema legado não serve para nada?

Serve, como ferramenta de leitura e não como estratégia. Ferramentas automáticas são ótimas para inventariar formulários, componentes e consultas SQL, montar o mapa de dependências e extrair pedaços isolados de cálculo como ponto de partida.

O que não funciona é usar a saída do conversor como o sistema novo.

A IA consegue converter um sistema Delphi ou VB6 inteiro?

A IA traduz trechos com qualidade bem melhor que os conversores de regra fixa, e acelera muito a leitura do código antigo. Mas, se você pedir para ela traduzir tela por tela, ela reproduz a mesma arquitetura acoplada, só que com nomes melhores.

Usamos IA para ler, descrever e rascunhar; a decisão de arquitetura e a validação das regras com quem opera continuam sendo trabalho humano.

Como a NasteSoft migra um sistema legado sem conversor?

Primeiro entendemos como o sistema funciona de verdade, a partir do código, do banco, das telas em uso e de entrevistas com quem opera. Depois documentamos as regras de negócio com exemplos validados.

Só então escrevemos o sistema novo, na arquitetura certa, uma parte por vez e rodando ao lado do legado, com cada etapa reversível. A documentação fica com o cliente.

Vamos conversar

Tem um sistema em Delphi, VB6 ou .NET Framework?

Conte o cenário — tecnologia, tamanho, quem mantém e o que mais dói. Respondemos com uma leitura honesta do caminho, sem promessa de conversão mágica.

WhatsApp +55 41 99511-2870 · e-mail rafael.naste@nastesoft.com.br

WhatsApp