DEV Community

Joseleno
Joseleno

Posted on

Seu código C# funciona? Parabéns. Agora vamos falar sobre produção.

Era uma quinta-feira, 17h40. Eu tinha acabado de fazer merge de uma feature que vinha trabalhando a semana toda. Build verde. Testes passando. Pipeline limpa. Fui pra casa tranquilo.

Sexta-feira, 9h12. Teams pipocando.

"A API de notas tá retornando 500."

O endpoint recebia a lista de alunos matriculados e montava um dicionário pra busca rápida por matrícula. O código era simples — bonito, até:

var alunos = await _repository.GetMatriculadosAsync();
var mapa = alunos.ToDictionary(a => a.Matricula);
Enter fullscreen mode Exit fullscreen mode

Duas linhas. Elegante. Funcionou por 4 meses em produção sem um soluço.

Até que a secretaria importou uma planilha com dois alunos usando a mesma matrícula. E o ToDictionary() fez o que ele sempre faz com chave duplicada: jogou uma ArgumentException na cara da aplicação. Sem aviso prévio. Sem warning no build. Quatro meses de silêncio e depois uma explosão.

Esse foi o dia que eu aprendi que código que funciona e código que sobrevive em produção são duas coisas completamente diferentes.


O que o compilador não te conta

O C# é uma linguagem generosa. Ela te dá um compilador forte, tipagem estática, Intellisense que praticamente escreve o código por você. E talvez por isso a gente baixe a guarda em alguns lugares onde não deveria.

Vou te mostrar três desses lugares. Não são bugs obscuros de runtime. São coisas que você provavelmente já escreveu — e que estão funcionando agora no seu código. Até pararem de funcionar.

O ToDictionary() e a confiança nos dados

O problema real não é o ToDictionary(). O problema é que a gente assume que os dados são limpos.

No ambiente de dev, são. No teste unitário com 5 registros criados à mão, são. Mas produção é um lugar hostil. Produção tem dados migrados de planilha Excel. Tem integração com sistema legado que manda registro duplicado. Tem secretaria que encontrou um jeito criativo de cadastrar o mesmo aluno duas vezes.

// Quatro meses funcionando. Quinto mês:
var alunos = await _repository.GetMatriculadosAsync();
var mapa = alunos.ToDictionary(a => a.Matricula);
// ArgumentException — e seu endpoint retorna 500
Enter fullscreen mode Exit fullscreen mode

A correção não é difícil. O que muda é a mentalidade:

// Se duplicata é possível e você quer o primeiro
var mapa = alunos
    .GroupBy(a => a.Matricula)
    .ToDictionary(g => g.Key, g => g.First());

// Se você precisa de todos os registros por chave
var lookup = alunos.ToLookup(a => a.Matricula);

// Se duplicata é um erro de dados que precisa ser tratado
var duplicados = alunos
    .GroupBy(a => a.Matricula)
    .Where(g => g.Count() > 1);

if (duplicados.Any())
    _logger.LogWarning("Matrículas duplicadas: {Matriculas}", 
        string.Join(", ", duplicados.Select(g => g.Key)));
Enter fullscreen mode Exit fullscreen mode

A diferença entre as abordagens não é técnica — é de postura. Você espera que os dados venham sujos ou torce pra que venham limpos?

O ToList() e a ilusão de independência

Essa eu demorei mais pra entender. Durante um bom tempo, eu usava .ToList() como uma espécie de escudo. Precisa passar uma lista pra outro método sem correr risco de alterar a original? .ToList(). Pronto. Protegido.

Só que não.

var alunos = await _repository.GetAprovadosAsync();
var paraExportar = alunos.ToList();

// Em algum lugar do método de exportação:
foreach (var a in paraExportar)
    a.Nome = a.Nome.ToUpper(); // "só pra formatar o relatório de notas"

// De volta no fluxo principal:
Console.WriteLine(alunos[0].Nome); // "MARIA SILVA" — a lista original mudou
Enter fullscreen mode Exit fullscreen mode

O ToList() cria uma lista nova, mas os objetos dentro dela são os mesmos. As referências apontam pro mesmo lugar na memória. Alterou por um lado, alterou pelo outro.

Isso é o tipo de bug que você passa horas procurando porque faz sentido que uma nova lista seja independente. Mas ela não é.

// Quando precisa de isolamento real com records:
var copiaSegura = alunos.Select(a => a with { }).ToList();

// Ou quando o tipo permite:
var copiaSegura = alunos.Select(a => new AlunoDto(a)).ToList();
Enter fullscreen mode Exit fullscreen mode

O Dictionary e a fé na existência das chaves

Essa é mais sutil. Não explode com frequência, mas quando explode é sempre num ambiente que você não controla — staging com dados incompletos, migração que esqueceu um registro, turma nova que ainda não tem todas as disciplinas cadastradas.

var notas = await _repository.GetNotasPorAlunoAsync(matricula);
var mapa = notas.ToDictionary(n => n.Disciplina, n => n.Valor);

var matematica = mapa["Matemática"];   // funciona
var filosofia = mapa["Filosofia"];     // KeyNotFoundException
Enter fullscreen mode Exit fullscreen mode

Você olha pro código e pensa: "mas essa disciplina sempre existiu". Sim. Até não existir.

// O caminho seguro que já virou reflexo pra mim:
if (mapa.TryGetValue("Filosofia", out var nota))
{
    // usa a nota
}
else
{
    _logger.LogWarning("Nota de Filosofia não encontrada para matrícula {Matricula}", matricula);
    nota = 0;
}

// Ou quando um fallback resolve:
var nota = mapa.GetValueOrDefault("Filosofia", 0);
Enter fullscreen mode Exit fullscreen mode

O que isso tem a ver com senioridade?

Nada disso é conhecimento avançado. Tá na documentação. Qualquer dev com 6 meses de C# consegue entender a explicação.

A diferença é que quem já tomou essas porradas em produção muda o jeito de escrever código. Não é que você fica paranóico — é que você desenvolve um instinto. Olha pro ToDictionary() e a primeira coisa que pensa é "e se vier duplicado?". Vê um ToList() sendo passado pra outro método e pergunta "esse método altera os objetos?". Encontra um acesso direto ao Dictionary e já sente o cheiro do KeyNotFoundException.

Não é sobre decorar regras. É sobre ter cicatrizes suficientes pra reconhecer o perigo antes dele acontecer.


Você já passou por algo parecido? Bug que ficou meses dormindo e acordou no pior momento possível? Conta aí nos comentários — essas histórias sempre ensinam mais que qualquer documentação.


Joseleno — Software Engineer & Founder @ CodeProcess

Top comments (0)