Você já sabe escrever código que funciona. Nesta fase, aprenderá a escrever código bom — e a diferença entre os dois é maior do que parece. Um programa que funciona hoje pode ser um pesadelo de manter amanhã se for mal escrito; um programa bem escrito é fácil de entender, de modificar e de corrigir, mesmo meses depois, mesmo por outra pessoa. Esta aula reúne os hábitos que distinguem o bom programador — não regras rígidas, mas princípios de bom senso que, cultivados desde cedo, transformam a qualidade do que você produz. Há uma verdade libertadora por trás de todos eles, e vale enunciá-la logo: código é lido muito mais vezes do que é escrito. Você escreve uma função uma vez, mas você (e outros) a lerão dezenas de vezes ao longo da vida dela. Otimizar para a leitura, portanto, é otimizar para a maior parte do trabalho. Esse é o princípio que unifica tudo o que veremos.
Nomes que revelam a intenção
O hábito mais impactante e mais barato de adotar é dar bons nomes. Já tocamos nisso ao falar de variáveis e métodos, mas vale elevá-lo ao seu devido lugar de importância: nomes claros são a diferença entre código que se lê como uma explicação e código que se lê como um enigma. Um nome deve revelar a intenção — dizer o que a coisa é ou faz, sem exigir que quem lê adivinhe. Compare:
// Nomes obscuros: o que este código faz?
int d = 30;
decimal p = CalcP(l, d);
// Nomes reveladores: o código se explica sozinho.
int diasDeAtraso = 30;
decimal multa = CalcularMulta(valorEmprestimo, diasDeAtraso);
As duas versões fazem a mesma coisa, mas a segunda comunica. diasDeAtraso diz o que aquele 30 significa; CalcularMulta diz o que o método produz. Você não precisa de comentários para explicar código bem nomeado — ele se explica. A regra prática: um nome deve ser tão descritivo quanto necessário para que alguém entenda seu propósito sem contexto adicional. Prefira quantidadeDeAlunos a qtd ou n; prefira EnviarEmailDeConfirmacao a Enviar ou Proc. O custo de um nome longo e claro é digitar alguns caracteres a mais, uma vez; o benefício é a clareza, repetida em cada leitura. É a troca mais vantajosa da programação.
Funções pequenas, com uma responsabilidade
O segundo hábito é manter os métodos pequenos e focados. Um método deve fazer uma coisa, e fazê-la bem — ter uma responsabilidade clara, expressa no seu nome. Um método gigante que lê dados, valida, calcula, formata e exibe, tudo de uma vez, é difícil de entender, de testar e de reaproveitar. Dividi-lo em métodos menores, cada um com um propósito claro, torna o código legível e modular:
// Um método que faz tudo é difícil de acompanhar.
// Melhor: dividir em passos nomeados, cada um com uma responsabilidade.
void ProcessarPedido()
{
var dados = LerDadosDoPedido();
if (!ValidarPedido(dados)) return;
var total = CalcularTotal(dados);
ExibirRecibo(dados, total);
}
O método ProcessarPedido tornou-se um resumo legível — lê-se quase como uma descrição em português do que acontece —, e cada passo (LerDadosDoPedido, ValidarPedido, etc.) pode ser entendido, testado e corrigido isoladamente. Essa é a arte da decomposição que mencionamos lá na primeira aula, aplicada à estrutura do código: quebrar o grande em pequenos pedaços compreensíveis. Uma regra de bolso útil: se você sente a necessidade de escrever um comentário do tipo "// agora valida os dados" no meio de um método, isso é frequentemente um sinal de que aquele trecho merece virar um método próprio, chamado ValidarDados. O nome do método substitui o comentário, e o código fica mais organizado.
Comentários: explique o "porquê", não o "o quê"
Falando em comentários, eles merecem uma orientação cuidadosa, pois são frequentemente mal usados. Um comentário que apenas repete o que o código já diz é inútil, e pior, pode ficar desatualizado e mentir:
idade = idade + 1; // soma 1 à idade ← comentário inútil: o código já diz isso
O bom comentário explica o porquê, não o o quê — a razão, a intenção, o contexto que o código sozinho não consegue expressar:
// Aplicamos o desconto de 5% porque este cliente é do programa de fidelidade.
preco = preco * 0.95m;
Esse comentário agrega valor: o código mostra que multiplicamos por 0,95, mas só o comentário explica por que — uma informação de negócio que não se deduz do cálculo. A regra: se o código é claro (bons nomes, boa estrutura), ele já explica o "o quê", e você raramente precisa de comentários para isso; reserve os comentários para o "porquê" — decisões, motivos, avisos, contexto que o código não pode carregar. Um código que precisa de muitos comentários para ser entendido é, muitas vezes, um código que deveria ser reescrito com nomes e estrutura melhores. O melhor comentário é frequentemente o que você não precisou escrever porque o código já era claro.
Consistência e simplicidade
Dois princípios finais, mais gerais. O primeiro é a consistência: escreva o código sempre da mesma maneira ao longo do projeto — a mesma convenção de nomes, o mesmo estilo de formatação, os mesmos padrões para situações semelhantes. Um código consistente é previsível, e previsibilidade reduz o esforço de leitura: quem lê aprende o "sotaque" do projeto uma vez e o reconhece em toda parte. Inconsistência — nomes em estilos diferentes, estruturas variando sem razão — força quem lê a se readaptar a cada trecho. O C# tem convenções amplamente adotadas (nomes de classe e método em maiúscula inicial, variáveis em minúscula inicial, e assim por diante), e segui-las alinha seu código ao que a comunidade espera.
O segundo é a simplicidade: prefira sempre a solução mais simples que resolva o problema. Há uma tentação, especialmente conforme se aprende mais, de escrever código "esperto" — engenhoso, compacto, que exibe conhecimento. Resista a ela. Código esperto é frequentemente código difícil de entender, e a esperteza raramente compensa a confusão que causa. O bom programador busca o código mais claro, não o mais engenhoso. Se há duas formas de resolver algo e uma é mais simples de entender, ela quase sempre é a melhor, ainda que a outra pareça mais sofisticada. Um mestre não é quem escreve o código mais complicado, mas quem resolve problemas complicados com código simples.
Uma palavra sobre perfeccionismo
Encerro com um contrapeso honesto, para você não interpretar mal estes conselhos. Essas boas práticas são metas a perseguir, não algemas que devem paralisá-lo. Ninguém escreve código perfeito de primeira, e buscar a perfeição a ponto de não terminar nada é um erro maior do que escrever código imperfeito que funciona. A prática recomendada é: primeiro faça funcionar, depois faça ficar claro — escreva uma solução que resolva o problema, e então a melhore, dando nomes melhores, dividindo métodos grandes, removendo confusão. Essa etapa de melhorar código que já funciona, sem mudar o que ele faz, tem até um nome, refatorar, e é uma parte normal e saudável do trabalho. Não espere escrever tudo perfeito de uma vez; escreva, faça funcionar, e depois refine. O bom código raramente nasce pronto — ele é lapidado.
Os hábitos que separam código que funciona de código que se sustenta ficaram nomeados: nomes que revelam a intenção e dispensam comentário, funções pequenas com uma responsabilidade só, comentários que explicam o porquê e não o óbvio, e consistência acima de preferência pessoal.
A nota sobre perfeccionismo merece ser levada a sério. Boas práticas são orientações acumuladas pela experiência, não regras a cumprir sob ansiedade, e código que funciona e pode ser melhorado vale infinitamente mais do que código perfeito que nunca foi escrito. A qualidade vem da revisão sucessiva, não do acerto na primeira tentativa.
Fontes e leituras recomendadas
- Convenções de código em C# — as convenções oficiais de nomes, formatação e estilo; a base desta aula.
- Diretrizes de nomenclatura — orientações detalhadas sobre nomear bem.
- Princípios de design de código — princípios como simplicidade e responsabilidade única.
- O que é refatoração — a prática de melhorar código sem mudar seu comportamento.
- Código limpo (conceito) — o panorama dos princípios de código legível e manutenível.
Exercícios
Exercício 1
Pegue o seguinte trecho de código com nomes ruins e reescreva-o com nomes que revelem a intenção: int x = 7; decimal y = 100m; decimal z = y * x; (imagine que representa um preço diário multiplicado por um número de dias). Explique como os novos nomes tornam o código autoexplicativo.
Ver resposta
✓ Resposta: Reescrita:
int diasDeLocacao = 7;
decimal precoDiario = 100m;
decimal precoTotal = precoDiario * diasDeLocacao;
Os novos nomes tornam o código autoexplicativo porque revelam o significado de cada valor: diasDeLocacao diz que o 7 representa uma quantidade de dias, precoDiario diz que o 100 é o preço por dia, e precoTotal diz que o resultado é o total a pagar. Quem lê entende imediatamente que se trata de calcular o custo de uma locação, sem precisar adivinhar o que x, y e z representavam.
Exercício 2
Você tem um método longo que lê dados de um usuário, valida-os, calcula um resultado e exibe um relatório, tudo num só bloco. Descreva (em palavras ou esboço de código) como você o dividiria em métodos menores, dando um nome apropriado a cada responsabilidade.
Ver resposta
✓ Resposta: Uma boa divisão daria a cada responsabilidade um método próprio, e o método principal os coordenaria:
void GerarRelatorio()
{
var dados = LerDados();
if (!ValidarDados(dados)) return;
var resultado = CalcularResultado(dados);
ExibirRelatorio(resultado);
}
Cada método — LerDados, ValidarDados, CalcularResultado, ExibirRelatorio — tem uma responsabilidade única e clara, expressa em seu nome. O método principal torna-se um resumo legível do fluxo, e cada passo pode ser entendido, testado e corrigido isoladamente.
Exercício 3
Para cada comentário a seguir, diga se ele é útil ou inútil, justificando: (a) contador++; // incrementa o contador; (b) // Usamos ordenação decrescente porque o cliente pediu os mais recentes primeiro; (c) total = a + b; // soma a e b; (d) // Este valor é 42 por exigência da regulamentação fiscal vigente.
Ver resposta
✓ Resposta: (a) Inútil: o comentário apenas repete o que o código já diz claramente (contador++ já significa "incrementa o contador"). (b) Útil: explica o porquê da ordenação decrescente (uma decisão de negócio, o pedido do cliente), informação que não se deduz do código sozinho. (c) Inútil: repete o óbvio; total = a + b já é evidentemente uma soma. (d) Útil: explica por que o valor é 42 (uma exigência regulatória), contexto essencial que o código não carrega e que impede alguém de "corrigir" o valor por engano no futuro. Em resumo, os comentários úteis explicam o porquê e o contexto; os inúteis apenas repetem o o quê.
Exercício 4
Explique, com suas palavras, o princípio "código é lido muito mais vezes do que é escrito" e como ele justifica os hábitos de dar bons nomes e escrever de forma clara mesmo que custe mais tempo ao escrever.
Ver resposta
✓ Resposta: O princípio "código é lido muito mais vezes do que é escrito" observa que um trecho de código é escrito uma única vez, mas será lido muitas vezes ao longo de sua vida — por você mesmo ao revisá-lo ou corrigi-lo, e por outros que trabalharão nele. Isso justifica investir esforço na clareza (bons nomes, boa estrutura) mesmo que custe mais tempo ao escrever, porque esse custo é pago uma vez, enquanto o benefício da clareza — a facilidade de entender o código — é colhido em cada leitura futura. Otimizar para a leitura, portanto, é otimizar para a maior parte do trabalho que o código dará ao longo do tempo; alguns segundos a mais digitando um nome claro economizam minutos de confusão a cada vez que alguém lê aquele trecho.
Exercício 5
Sem código: a aula recomenda "primeiro faça funcionar, depois faça ficar claro". Explique por que essa ordem é sensata, e o que significa "refatorar". Por que buscar a perfeição de primeira pode ser contraproducente?
Ver resposta
✓ Resposta: A ordem "primeiro faça funcionar, depois faça ficar claro" é sensata porque separa dois objetivos distintos: primeiro resolver o problema (fazer o código produzir o resultado certo), e só então melhorar sua forma (nomes, estrutura, clareza) sem mudar o que ele faz. "Refatorar" é justamente essa melhoria de código que já funciona — reorganizá-lo, renomeá-lo, dividi-lo em partes melhores — mantendo seu comportamento inalterado. Buscar a perfeição de primeira pode ser contraproducente porque tentar acertar tudo — a solução e a forma ideal — de uma só vez trava o progresso: você pode ficar paralisado aperfeiçoando detalhes antes mesmo de o programa funcionar, ou nunca terminar por nunca estar "perfeito". É mais eficaz fazer funcionar primeiro (garantindo que o problema está resolvido) e depois lapidar, em passos, o que já está funcionando — pois código bom raramente nasce pronto, sendo antes o resultado de refinamento sucessivo.