Time de tecnologia revisando código legado em quadro branco durante migração para .NET 10

Os maiores desafios da migração para .NET 10 saindo de Web Forms 4.8

TL;DR

  • Migração para .NET 10 é principalmente discovery, não coding — descrever o comportamento real do sistema antes de converter uma linha.
  • Código legado parece simples, mas é denso em regra de negócio — uma classe de 80 linhas pode concentrar cinco anos de exceções de cálculo fiscal.
  • Acoplamento invisível entre páginas Web Forms, eventos de runtime, sessão e SQL inline é a fonte primária de regressão pós-migração.
  • Reescrita sem mapeamento preserva código e perde regra — converter sintaxe preserva a árvore sintática, não a árvore de decisões.
  • O ROI da migração aparece na fase de discovery, não na de execução — cortar essa fase para acelerar cronograma é o erro mais caro.

Por que a migração para .NET 10 quebra mesmo quando “compila”

A promessa típica de uma migração Web Forms 4.8 para .NET 10 é reconquistar ciclos: runtime moderno, tipagem forte ponta a ponta, SPA Angular no front. A realidade que aparece nas primeiras sprints é menos vendável: o sistema legado não é um monolito bem desenhado com uma camada de UI a ser substituída. É um grafo de efeitos colaterais construído ao longo de uma década, com decisões que existem em comentários de código, planilhas internas, mensagens de e-mail antigas e conversas com usuários que nunca viram um PR.

Os desafios da migração para .NET 10 começam quando o time decide descobrir o que o sistema realmente faz. Esse mapeamento é o que diferencia uma migração que sobrevive em produção de uma migração que vai para o ar, abre 73 chamados na primeira semana e volta para o monolito com a desculpa de “ainda não está maduro”.

A primeira armadilha é tratar o código-fonte como verdade única sobre o sistema. O código mostra o que foi digitado, não o que foi combinado. Exceções que nunca chegaram ao repositório — porque foram negociadas por telefone, registradas em memorando, ou simplesmente lembradas pelo analista mais antigo — continuam existindo no comportamento do sistema mesmo quando o código que as suporta foi apagado. Migração que começa pela sintaxe, sem passar por discovery de comportamento, entrega um sistema que compila, roda e produz resultado errado nos mesmos casos em que o legado produzia resultado certo.

A segunda armadilha é subestimar o tempo de mapeamento. Em sistema legado com 8 a 15 anos de operação, o discovery realista consome entre 25% e 40% do cronograma total. Esse número assusta sponsor acostumado a planilha de reescrita em três sprints. A defesa é histórica: cada projeto pulou essa fase, cada projeto estourou cronograma, cada projeto entregou defeitos que poderiam ter sido evitados. A pergunta correta não é “quanto tempo levamos para codificar” e sim “quanto tempo levamos para descobrir o que precisa continuar funcionando”.

A dificuldade de descobrir o que o sistema realmente faz

A etapa mais ingrata da migração para .NET 10 é a que não produz commit. Não há branch, não há diff, não há demo. Só horas de leitura de código antigo, entrevistas com usuários, leitura de log de produção e construção de mapa mental. O que aparece durante esse processo é o conjunto de dificuldades que definem a viabilidade da migração:

  • Regras espalhadas — uma mesma regra de negócio aparece em cinco lugares diferentes: na view, no evento do botão, no stored procedure, no job de validação noturna, no relatório. Sincronizar essas cópias durante a migração é a parte invisível que mata cronograma.
  • Código duplicado — duas cópias de “cálculo de ICMS” não são refactor preguiçoso; são duas histórias diferentes que em algum momento precisaram divergir. Migrar uma e não a outra é regressão silenciosa.
  • Métodos com múltiplas responsabilidades — método de 400 linhas que faz validação, persistência, notificação e log não é código mal escrito; é o registro de um requisito que cresceu sem refactor. Migrar exige desmontar e remontar respeitando o efeito agregado.
  • Consultas SQL dentro da interface — SELECT em code-behind de página Web Forms é legado real, e em alguns sistemas representa o caminho quente de relatório que roda 8.000 vezes por dia. Substituir por EF Core sem medir plano de execução é a forma mais rápida de transformar tela rápida em tela travada.
  • Processos acionados por eventos indiretos — Application_Start, Session_End, eventos do Global.asax, HttpModule registrado em web.config, triggers no banco, jobs no agendador do Windows. O sistema se comporta diferente em 23:00, em 03:00 e no momento em que o IIS é reciclado.
  • Variáveis de sessão — Session["usuarioLogado"] é a forma mais comum de estado espalhado. Migração para .NET 10 que ignora o ciclo de vida da sessão troca estado “funciona” por estado “às vezes funciona”.
  • Tabelas utilizadas por vários módulos — uma mesma tabela Cliente é tocada por 11 módulos diferentes, com colunas calculadas em tempo de leitura que só fazem sentido em três deles. Migração de schema é a parte onde o “banco compartilhado” vira problema sério.
  • Rotinas sem documentação — existe, roda em produção, ninguém sabe exatamente o que faz e ninguém quer arriscar desligar. A regra é documentar antes de migrar, jamais descobrir em produção.
  • Exceções conhecidas apenas pelos usuários — “se você clicar no botão X antes de salvar Y, a tela pisca e o campo Z preenche sozinho” é feature, não bug. Está documentada na cabeça de quem usa há 7 anos. Discovery sem entrevista com usuário-chave perde essa categoria inteira.
  • Dependências externas não registradas — a chamada SOAP para a receita, o FTP que baixa arquivo de fornecedor, o WebService de terceiro que fechou em 2019 mas o sistema finge que ainda existe. Cada uma vira incógnita na migração; algumas viram bloqueador.

Esse inventário não é completo. Sistemas diferentes têm dificuldades diferentes. Mas a forma de descobrir é a mesma: ler código com lupa, entrevistar usuários com perguntas específicas, ler log de produção com olho de arqueólogo, e mapear o que o sistema faz antes de decidir como reescrevê-lo.

Código simples não significa regra simples

A segunda categoria de desafios da migração para .NET 10 é conceitual e por isso mais traiçoeira. Migrar código legado exige reescrever, e reescrever exige entender. Mas o entendimento não mora na complexidade do código. Mora na regra que o código implementa — e regra simples pode estar escondida em código simples.

Uma classe pequena pode conter uma regra empresarial importante. Construtor de 12 linhas, duas propriedades, um método. Atrás dessas 12 linhas existe um percentual de desconto negociado com cliente VIP em 2017, com vigência até 2024, com extensão automática se o cliente não cancelar por escrito, com exceção para contratos acima de R$ 500 mil. Nada disso está no código. Está no contrato, no e-mail, no memorando. Migrar o código sem migrar a regra produz classe que compila, executa e dá desconto errado para o cliente mais importante da carteira.

Um método longo pode representar anos de ajustes para atender legislação, clientes ou operações específicas. Método de 600 linhas em sistema fiscal não é falta de refactor; é memorial de cada ajuste feito para passar em uma auditoria, cada patch de fim de ano para mudança de alíquota, cada workaround para integração com sistema da SEFAZ. O método é a história de uma equipe que escreveu regras de negócio no único lugar onde o sistema permitia escrever. Migrar exige ler essa história toda, não particionar em métodos menores.

Converter a sintaxe sem compreender o comportamento pode preservar o código e perder a regra de negócio. O try-convert da Microsoft converte Web Forms em Razor, converte HttpContext em IHttpContextAccessor, converte Session em ISession. Não converte a regra que existia entre esses elementos. O resultado é um sistema que roda, com testes que passam, e que na primeira semana de produção abre 200 chamados porque ninguém lembrou de avisar o sistema que o cliente X tem tratamento especial.

// Web Forms legado — código pequeno, regra grande
public decimal CalcularDesconto(Cliente cliente, Pedido pedido)
{
    if (cliente.Tipo == TipoCliente.VIP && pedido.Valor > 500000m)
    {
        return pedido.Valor * 0.18m; // contrato 2017-A, com extensão automática
    }
    if (cliente.Tipo == TipoCliente.VIP && cliente.AnoInicio < 2018)
    {
        return pedido.Valor * 0.12m; // regra antiga, mantida por exceção comercial
    }
    if (cliente.Cidade == "São Paulo" && pedido.DataEmissao.Year == 2020)
    {
        return pedido.Valor * 0.07m; // campanha emergencial COVID, desativada em 2021
    }
    return 0;
}

O método parece legível. A regra está em cada if, e cada if é uma decisão escrita em algum momento por alguém respondendo a um problema específico. Migrar esse método sem ler as 14 linhas como história de uma equipe é transformar exceção em bug. O método novo em .NET 10 precisa carregar a mesma história, ou documentar onde cada caso foi tratado.

A saída: tratar o sistema como objeto de pesquisa

A defesa contra esses desafios da migração para .NET 10 não é ferramenta, framework ou LoRA de IA. É método. A abordagem que consistentemente sobrevive em produção segue três passos não negociáveis.

Primeiro, discovery antes de escrita. Mapear comportamento antes de tocar em código. Produzir documentos formais com a lista de regras, exceções e integrações, validados com usuários-chave e com o time de operação. Esse documento vira o critério de aceite da migração: se o sistema novo não produz o mesmo comportamento que o documento descreve, ele não está pronto.

Segundo, contratos explícitos durante a transição. Cada módulo migrado publica um contrato OpenAPI ou um arquivo de interface com o conjunto de entradas, saídas e efeitos colaterais garantidos. Esses contratos viram base de teste automatizado. Migração sem contrato vira migração por inspeção visual, que é a forma menos confiável de validar comportamento.

Terceiro, paralisia planejada com tráfego real. Cada módulo novo roda em produção, com tráfego real, lado a lado com o legado, durante 4 a 8 semanas. Diferenças de comportamento aparecem, são corrigidas, e o desligamento do legado só acontece depois de zero divergência por pelo menos 21 dias consecutivos.

Sequência mínima de uma migração Web Forms 4.8 → .NET 10 com discovery real

Posts Similares

2 Comentários

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *