Clean Code: Em busca de nomes significativos

Érick Barbosa
Data Hackers
Published in
5 min readJun 11, 2023
Photo by Maxwell Nelson on Unsplash

Muitas vezes podemos julgar ser desnecessário o tempo que levamos para encontrar nomes relevantes para os diferentes elementos do código que estamos trabalhando. Tais elementos podem ser variáveis, funções, classes ou até mesmo a coluna de uma tabela, todos eles são identificados através de nomes.

A busca por nomes significativos é uma das inúmeras preocupações que surgem no decorrer do desenvolvimento como, por exemplo, uma boa modelagem de classes e aplicação dos princípios SOLID. Além destas preocupações, também existem fatores externos que subtraem nossa atenção e nos induzem a concluir que nomeações deveriam ser a última prioridade. Prazo de entrega e a atenção que o time dá ao tema são dois exemplos clássicos destes fatores.

What is your name? (Qual é o seu nome?)

Se considerarmos a escrita de código um processo evolutivo, iniciaremos este caminho focados em fazê-lo funcionar. Após a conclusão desta etapa, podemos seguir com ajustes finos e, finalmente, concentrarmos na legibilidade.

Neste artigo vamos abordar algumas reflexões válidas a serem consideradas no momento em que estamos buscando melhorar a legibilidade do código. O algoritmo que calcula o número Pi foi escolhido como exemplo para desenvolvermos a busca por nomes significativos.

Contexto: Cálculo do Pi

O Papiro de Rhind, considerado um guia matemático, foi escrito em 1650 a.C. por um escriba egípcio. Nele foram registradas as primeiras estimativas do número Pi. No decorrer da história, intelectuais de diferentes povos exploraram problemas matemáticos relacionados ao Pi.

Vamos utilizar a fórmula de Gottfried Leibniz(1646–1716) para calcular o valor de Pi, representada por uma sequência infinita de termos que seguem o padrão exibido na Figura 1.

Figura 1 — Fórmula de Leibniz para o cálculo de Pi

Há três elementos importantes a serem observados nesta série. O primeiro é o numerador igual a 4 em todos os termos. O segundo é o denominador sendo sempre um número ímpar. E por último, notamos que as operações entre termos sucessivos alternam entre adição e subtração.

Hora de escrever: Primeira versão do código

O código abaixo pode ser considerado o resultado final da primeira parte do processo de escrita, onde nossa preocupação é exclusivamente fazê-lo funcionar.

def const(i: int) -> float:
x: float = 4.0
y: float = 1.0
z: float = 1.0
p: float = 0.0

for _ in range(i):
p += z * (x/y)
y += 2.0
z *= -1.0

return p

Realizando chamadas da função acima para diferentes valores de entrada, obtemos os seguintes resultados:

print(f'Pi = {const(10):.6f}')   # Pi = 3.041840
print(f'Pi = {const(1000):.6f}') # Pi = 3.140593
print(f'Pi = {const(10000):.6f}') # Pi = 3.141493

Com estas execuções é possível constatar que o código de fato funciona, pois a soma converge para o valor de Pi. Quanto maior o parâmetro de entrada, mais próximo de Pi será o valor retornado pela função.

Refletindo sobre as primeiras escolhas

Poderíamos justificar a escolha de const, como nome da função, pelo fato de Pi ser uma constante. O termo i geralmente é utilizado para nomear variáveis relacionadas a iterações/loops, então a escolha não é infundada. Também poderíamos defender que, na leitura do código, é evidente que os termos x e y expressam numerador e denominador, respectivamente.

Estas são justificativas válidas, os nomes foram escolhidos com um mínimo de argumentação para justificá-las. Todavia, quando pensamos em legibilidade, devemos considerar o entendimento que as outras pessoas terão no ato de leitura. Logo, vale a pena nos questionarmos se é possível melhorar a legibilidade do que acabamos de escrever.

Para iniciar nossa busca por nomes significativos, vamos refletir sobre os pontos a seguir:

  1. Nossa argumentação inicial é verdadeira para todas as pessoas que irão ler e dar manutenção neste código?
  2. Um leitor que desconhece o contexto do problema, terá facilidade em reconhecer que se trata do cálculo do Pi?
  3. Para alguém que não tenha base matemática, a relação entre o loop for e os termos da série numérica é evidente?
  4. Para um provável leitor, é evidente a relação entre o retorno da função e o parâmetro de entrada?
  5. É possível reduzir o tempo entre a primeira leitura e o completo entendimento do algoritmo?

Após refletir sobre estas questões, será que conseguimos reescrever o código levando-as em consideração? Vamos lá.

Hora de [RE]escrever: Segunda versão do código

def calculate_pi(n_terms: int) -> float:
numerator: float = 4.0
denominator: float = 1.0
signal: float = 1.0
pi: float = 0.0

for _ in range(n_terms):
pi += signal * (numerator/denominator)
denominator += 2.0
signal *= -1.0

return pi

Alterando o nome da função de const para calculate_pi, o propósito da função fica mais explícito. Uma pessoa sem o contexto do código entenderia que trata-se do cálculo do pi e não de uma constante qualquer.

Por se tratar de uma série numérica, n_terms seria melhor que i para nomear o parâmetro de entrada. Este nome deixa mais claro que o loop for reproduz as operações de adição e subtração entre os termos da série. Isso facilita o entendimento de que quanto maior o valor do parâmetro mais próximo o resultado será de Pi.

Uma vez que temos a função e todas as variáveis em seu escopo nomeadas segundo as reflexões propostas, o tempo que o leitor leva para assimilar o algoritmo é menor pelo fato dos nomes serem mais específicos e descritivos.

O que é importante ter em mente

Em geral, nomes devem relevar o porquê da existência do que está sendo nomeado. Apesar de existirem divergências e não vermos esta regra ser aplicada até mesmo em grandes projetos, segundo o livro Código Limpo do lendário Uncle Bob, é preferível nomear uma variável como elapsedTimeInDays ao invés de, simplesmente, usar uma letra qualquer como, por exemplo, t.

Outro ponto importante é quão específico é o nome escolhido. Deixar evidente a razão de existir do elemento nomeado é bom, ser específico nesta evidenciação é ainda melhor. Por exemplo, comparando time e t, como escolhas de nome, o primeiro deixa mais evidente que trata-se de tempo. Porém, elapsedTimeInDays é muito mais específico que time.

Devemos considerar termos comuns da engenharia de software. Se, por exemplo, nomearmos uma classe como RequestAdapter, fica evidente que os objetos desta classe desempenharão o papel do padrão de projeto Adaptador(Adapter).

Palavras finais

Durante a busca por nomes significativos, tente balancear a bagagem que possui com o conhecimento médio do time. Independente de seu nível de conhecimento, o resultado final deve ser um código de fácil e correto entendimento por parte do leitor.

Se você é um profissional repleto de referências, reflita se o seu time irá entendê-las. Busque, por exemplo, realizar sessões de pair programming para equalizar o conhecimento. Por outro lado, se você tiver pouco tempo de experiência, talvez a atitude mais inteligente seja dar bastante atenção às sugestões de seus pares. À medida que você absorver mais conhecimento, maiores serão as chances de escrever códigos mais legíveis.

O livro Código Limpo contém estas e muitas outras dicas de como escrever códigos que melhoram a experiência do leitor. Para esta finalidade, a leitura deste livro é mais que recomendada.

Este artigo foi útil pra você? Tem algum relato sobre nomeações indevidas?Sinta-se à vontade para compartilhar aqui nos comentários.

--

--