Pular para o conteúdo
Atualizado em 14 min de leitura

Como nomear variáveis e funções com clareza

Por Lucas Andrade ·

Nomear é uma das tarefas mais difíceis da programação. Veja como escolher nomes que revelam intenção, evitam desinformação e tornam o código legível.

Neste artigo

Existe uma piada antiga na engenharia de software que diz que só há dois problemas difíceis na computação: invalidação de cache, nomear coisas, e erros de um a mais. A graça está no erro proposital de contagem, mas o fundo é sério: nomear coisas está, sem ironia, entre as tarefas mais difíceis que um programador enfrenta no dia a dia. Não porque exija conhecimento técnico avançado, mas porque exige compreender profundamente o que uma coisa é, o que ela faz e como se encaixa no todo, e depois comprimir esse entendimento em poucas palavras que outra pessoa vai interpretar sem o seu contexto.

Um nome é a interface mais barata e mais frequente de um programa. Você não abre a implementação de uma função toda vez que a usa; você lê o nome dela e confia que ele diz a verdade. Multiplicado por todas as variáveis, funções, classes e módulos de um sistema, essa camada de nomes é o que você realmente lê ao navegar pelo código. Quando os nomes são bons, o programa quase se explica. Quando são ruins, cada leitura vira uma tradução, uma investigação, uma desconfiança constante sobre se aquilo faz mesmo o que aparenta fazer.

Este artigo é sobre a arte prática de nomear bem. Vamos ver por que nomes devem revelar intenção, por que abreviações e genéricos custam caro, como o comprimento certo depende do escopo, quais convenções ajudam o leitor a prever comportamento, como evitar nomes que mentem, e por que renomear é uma das refatorações mais baratas e mais subestimadas que existem.

Por que nomear é tão difícil#

A dificuldade de nomear não é acidental; ela é estrutural. Um bom nome precisa satisfazer, ao mesmo tempo, várias restrições que costumam puxar em direções diferentes. Ele deve ser preciso, para não induzir a erro; conciso, para não poluir a leitura; consistente com o vocabulário do resto do sistema; e compreensível para quem não participou da decisão de projeto. Acertar as quatro coisas de uma vez, em duas ou três palavras, é genuinamente difícil.

Há também um componente de conhecimento. Você só nomeia bem aquilo que entende bem. Quando um nome sai vago, do tipo dados, processar, gerenciador, isso quase sempre revela que o próprio autor ainda não decidiu com clareza qual é a responsabilidade daquele elemento. O nome ruim é sintoma, não causa: ele denuncia uma abstração mal definida. Nesse sentido, o esforço de nomear é uma ferramenta de projeto, porque a dificuldade de encontrar um bom nome frequentemente aponta que a estrutura precisa ser repensada.

Por fim, existe a maldição do contexto. No momento em que você escreve o código, todo o significado está fresco na sua cabeça, e qualquer nome parece suficiente, porque você preenche as lacunas com o que já sabe. O leitor futuro não tem esse contexto. Ele depende inteiramente do que o nome consegue comunicar sozinho, sem a narração mental que só você possuía. Escrever bons nomes é, em grande medida, o exercício de imaginar esse leitor sem contexto e escrever para ele.

Nomes que revelam intenção#

A regra de ouro é simples de enunciar e difícil de praticar: um nome deve dizer por que algo existe, o que faz e como é usado. Se um nome precisa de um comentário ao lado para ser entendido, ele falhou, e o comentário é a evidência do fracasso. O ideal é que o comentário seja desnecessário porque o nome já carrega a informação que ele traria.

Comparar extremos ajuda a fixar a ideia. Uma variável chamada d não diz nada; ela força o leitor a rastrear o uso para adivinhar o significado. Uma variável chamada dias diz um pouco mais, mas ainda deixa perguntas. Já diasDesdeUltimaModificacao diz exatamente o que representa, e o leitor não precisa de nenhuma investigação para entender. As três ocupam quase o mesmo lugar no programa, mas a diferença de custo cognitivo entre elas, somada a milhares de leituras, é enorme.

Revelar intenção também vale para estruturas e coleções. Uma lista chamada lista apenas repete o que o tipo já diz; uma lista chamada pedidosPendentes informa o que está lá dentro e por quê. Quando o nome carrega a condição, como o fato de serem pendentes e não todos os pedidos, ele evita uma classe inteira de erros, porque o leitor que fosse usar aquela coleção como se fossem todos os pedidos seria alertado pelo próprio nome de que está enganado.

Um bom teste é ler o código em voz alta como se fosse uma frase. Código com nomes reveladores tende a soar quase como português: se o saldo disponível é menor que o valor solicitado, então recuse a transação. Quando a leitura em voz alta produz um amontoado de letras soltas e abreviações, é sinal de que os nomes estão escondendo a intenção em vez de expô-la.

O custo das abreviações e dos genéricos#

Abreviações prometem economia e entregam ambiguidade. qtd, usr, calc, tmp, val parecem óbvias para quem as escreve, porque o autor sabe o que quis dizer. Para o leitor, cada abreviação é um pequeno enigma, e enigmas se acumulam. Pior: a mesma abreviação pode significar coisas diferentes em lugares diferentes, e não há como o leitor saber sem investigar. val é valor, validação, válido? A economia de teclas na escrita se paga com juros altos na leitura, exatamente a troca que não queremos fazer, já que se lê muito mais do que se escreve.

Os nomes genéricos são um problema irmão. data, info, dados, item, obj, valor, resultado, temp são recipientes vazios de significado. Eles descrevem a categoria mais ampla possível da coisa, sem dizer nada sobre o papel específico que ela cumpre ali. Uma variável chamada data num sistema pode ser qualquer coisa; ela obriga o leitor a descer à implementação para descobrir o que realmente carrega. O nome genérico transfere para o leitor o trabalho que o autor deveria ter feito ao escolher a palavra certa.

Os nomes de uma só letra merecem menção à parte. x, y, i, n, e não são proibidos, mas têm um domínio muito estreito de uso legítimo. Um índice de laço chamado i, num laço curtíssimo e convencional, é aceitável porque a convenção é universal e o escopo é minúsculo. Fora desse nicho, uma letra solta é quase sempre uma perda: ela obriga o leitor a reconstruir o significado a cada uso. Coordenadas geométricas com x e y são outra exceção consagrada, mas repare que essas exceções compartilham uma propriedade, a de serem convenções tão difundidas que o próprio nome curto já comunica.

Existe uma tensão real entre concisão e precisão, e a chave para resolvê-la é o escopo, tema da próxima seção. Mas a diretriz geral é clara: prefira sempre a palavra inteira à abreviação, e a palavra específica à genérica. Um nome mais longo que dispensa investigação vence um nome curto que a exige.

Comprimento proporcional ao escopo#

Uma das heurísticas mais úteis sobre nomes é que o comprimento apropriado é proporcional ao tamanho do escopo em que o nome vive. Quanto mais amplo o alcance de uma variável, quanto mais longe do ponto onde ela é definida ela será usada, mais o nome precisa carregar significado por conta própria. Quanto mais estreito o escopo, mais curto o nome pode ser sem prejuízo, porque o contexto de definição ainda está visível.

Num laço de três linhas, um índice i é perfeitamente legível, porque tudo que precisa ser entendido está à vista, ali mesmo. Numa variável local usada dentro de uma função curta, um nome moderado basta, porque o leitor tem a função inteira como contexto imediato. Já uma constante global, um campo de configuração usado em vários módulos, ou uma função pública chamada de dezenas de lugares, precisa de um nome que se sustente sozinho, sem apoio do redor, porque será lido longe de onde foi definido, por pessoas que talvez nunca vejam a definição.

Essa proporção também explica por que nomes muito longos podem ser tão problemáticos quanto os muito curtos, quando fora de lugar. Um nome extenso e detalhado dentro de um escopo minúsculo é ruído; ele infla a leitura sem agregar informação, porque o contexto próximo já esclareceria tudo com uma palavra menor. A elegância está no ajuste: informação suficiente para o alcance do nome, nem mais, nem menos. Nomear bem é calibrar densidade de significado à distância que o nome vai viajar.

Há um corolário prático valioso. Se uma variável precisa de um nome muito longo e cheio de qualificadores para fazer sentido dentro de uma função curta, talvez o problema não seja o nome, e sim o fato de a função estar carregando responsabilidades demais, misturando níveis de abstração que deveriam estar separados. Mais uma vez, a dificuldade de nomear aponta para uma questão de estrutura.

Convenções que ajudam a prever comportamento#

Nomes ficam muito mais poderosos quando seguem convenções compartilhadas, porque a convenção permite ao leitor prever comportamento sem ler a implementação. Algumas dessas convenções são quase universais e vale a pena adotá-las com disciplina.

  • Funções são verbos ou frases verbais. Uma função faz algo, então seu nome deve descrever a ação: calcular, enviar, validar, buscar, construir. Um nome de função que é um substantivo puro confunde, porque não indica o que acontece quando você a chama.
  • Valores e variáveis são substantivos. Uma variável guarda uma coisa, então seu nome deve nomear essa coisa: saldo, usuário, pedido, total. O substantivo descreve o que está ali, não uma ação.
  • Booleanos são afirmações que respondem sim ou não. Prefixos como está, tem, pode, deve tornam a leitura natural: se o usuário está ativo, se o pedido tem itens, se a conta pode sacar. E, importante, formule a afirmação no positivo. Um booleano chamado com negação embutida, do tipo naoEstaInvalido, gera dupla negação na hora do uso e vira fonte confiável de bugs. Afirme; deixe a negação para o operador, quando necessária.
  • Coleções sugerem pluralidade. Um nome no plural, como usuários ou pedidos, comunica de imediato que ali há muitos, o que ajuda o leitor a esperar iteração em vez de um valor único.
  • Pares opostos são consistentes. Se você usa abrir e fechar num lugar, não use abrir e encerrar noutro para a mesma ideia. Adote pares simétricos e reutilize-os: começo e fim, primeiro e último, adicionar e remover. A simetria reduz a carga de memória.

Além dessas convenções de forma, há a convenção de caixa e estilo, que varia por linguagem e por time. Não importa tanto qual estilo você adota, mas que você o adote de forma consistente. Um leitor que aprendeu o padrão de nomes de um trecho consegue generalizá-lo para o resto; um leitor diante de estilos misturados gasta atenção só para lidar com a inconsistência, atenção que deveria ir para o problema real.

Evitar desinformação e manter vocabulário consistente#

Pior do que um nome que não informa é um nome que desinforma, isto é, que sugere algo falso. Uma variável chamada listaDeUsuarios que, na verdade, não é uma lista, mas outra estrutura, mente para o leitor e o leva a raciocinar sobre premissas erradas. Um nome que carrega uma unidade implícita, como tempo, sem dizer se é em segundos ou milissegundos, convida a erros de cálculo silenciosos. Evitar desinformação significa garantir que o nome seja fiel à natureza e ao comportamento reais da coisa, e não a uma intenção antiga que o código já não cumpre.

A desinformação costuma nascer da erosão. O nome era correto quando foi escrito, mas o código mudou e o nome não acompanhou. Uma função chamada de forma a sugerir que apenas valida passou, com o tempo, a também salvar dados, e ninguém atualizou o nome. Agora o nome mente, e cada leitor é enganado até descobrir, no susto, o efeito colateral não anunciado. Por isso o nome deve ser tratado como parte viva do código, revisto sempre que o comportamento muda, e não como um rótulo fixado uma vez e esquecido.

A consistência de vocabulário no domínio é o outro pilar. Um sistema deve escolher uma palavra para cada conceito e usá-la em todo lugar. Se o conceito é cliente, não o chame de cliente num módulo, usuário noutro e conta num terceiro, a menos que sejam genuinamente coisas diferentes, caso em que a distinção precisa ser clara e intencional. Sinônimos espalhados obrigam o leitor a perguntar, a cada termo novo, se aquilo é o mesmo conceito com outro nome ou um conceito distinto. Um vocabulário disciplinado, onde cada palavra tem um significado e cada significado tem uma palavra, transforma o código num texto coerente que o leitor aprende uma vez e reaproveita em toda parte.

Vale igualmente o inverso: não use a mesma palavra para conceitos diferentes. Se adicionar significa inserir em uma coleção num lugar, não use adicionar para somar valores numéricos noutro. A ambiguidade de um verbo sobrecarregado é tão custosa quanto a dispersão de sinônimos. O ideal é um mapeamento limpo entre palavras e ideias, sem sobreposições traiçoeiras.

Renomear é refatoração barata#

Talvez a mensagem mais libertadora sobre nomes seja esta: você não precisa acertar de primeira. O primeiro nome que você escolhe é um rascunho, uma hipótese, e é absolutamente normal que ele melhore à medida que você entende melhor o problema. Renomear, com as ferramentas modernas, é uma das refatorações mais seguras e baratas que existem, e tratá-la como tal muda a relação com a nomeação.

Ambientes de desenvolvimento atuais fazem renomeação com consciência de escopo: eles trocam todas as ocorrências de um identificador de forma segura, sem confundir com outros de mesmo nome em contextos diferentes, e sem depender de busca cega por texto. Isso significa que o custo de melhorar um nome é minúsculo comparado ao benefício acumulado de todas as leituras futuras que ele vai facilitar. Deixar um nome ruim no lugar por preguiça de renomear é economizar segundos hoje para pagar em confusão amanhã.

A prática saudável é renomear assim que perceber que um nome ficou impreciso. Você descobriu que aquela variável na verdade guarda pedidos pendentes, não todos os pedidos? Renomeie na hora. A função cresceu e passou a fazer mais do que o nome anuncia? Ou você atualiza o nome, ou, melhor ainda, você usa o desconforto como sinal para dividir a função. Cada renomeação é um pequeno investimento na clareza permanente do sistema, e o retorno se realiza em cada leitura seguinte.

Encarar nomes como algo vivo, revisável e barato de ajustar remove a paralisia de tentar encontrar o nome perfeito antes de escrever qualquer coisa. Escreva o melhor nome que ocorrer agora, siga em frente, e melhore quando o entendimento amadurecer. A qualidade dos nomes de uma base de código raramente vem de acertos geniais na primeira tentativa; ela vem do hábito contínuo de refinar, de nunca deixar passar um nome que enganaria o próximo leitor.

Síntese: o nome é a primeira documentação#

Nomear bem não é um detalhe cosmético; é uma das alavancas mais poderosas de legibilidade que um desenvolvedor controla. Os nomes formam a camada que mais se lê num programa, a interface pela qual entendemos o que cada peça faz sem abrir a implementação. Quando eles revelam intenção, evitam abreviações e genéricos, ajustam o comprimento ao escopo, seguem convenções previsíveis, não mentem e mantêm um vocabulário consistente, o código inteiro fica mais fácil de entender, de mudar e de confiar.

A dificuldade real de nomear vem de que um bom nome exige clareza de pensamento sobre a coisa nomeada, e escreve-se para um leitor que não tem o seu contexto. Por isso o esforço de encontrar o nome certo é também um exercício de projeto: quando o nome não sai, a estrutura costuma estar pedindo revisão. E, porque renomear é barato e seguro, não há desculpa para conviver com nomes que enganam; o hábito de refinar continuamente é o que, com o tempo, transforma uma base de código num texto que quase se lê sozinho. Trate cada nome como a primeira e mais frequente documentação do seu programa, porque é exatamente isso que ele é.

Leituras relacionadas

Nenhum comentário ainda

Seja o primeiro a comentar.

Deixe seu comentário

Entre com sua conta Canverly para comentar. Você pode usar a mesma conta em qualquer site da rede.

Entrar com Canverly