DEV Community

Yuri Peixinho
Yuri Peixinho

Posted on

Cluster 1 do Clean Code: #1 consistência, #2 nomes, #3 estilo, #4 métodos/classes/arquivos pequenos, #6 complexidade ciclomática

Introdução

Esse é um material que depende de leitura anterior. Recomendo ler o artigo introdutório antes de prosseguir.

Como já dito anteriormente: a restrição aqui não é de gosto, é de hardware. A memória humana de trabalho segura certca de 7±2 "pedaços" de informação simultâneos. O que isso significa de fato? Uma função com 8 variáveis e 6 caminhos de decisão ativos exige que quem lê segure mais estado na cabeça do que ela fisicamente comporta. O erro de interpretação não é falta de atenção, é o estouro da capacidade cognitiva. Isso tem confirmação empírica direta em revisão de código. O revisor humano simplesmente para de conseguir processar depois de certo volume. Consistência, nomes claros e funções pequenas não deixam o código "bonito" — elas mantêm o problema dentro do que um cérebro humano consegue segurar de uma vez.

#1 Be Consistent

Teoria: o experimento de Chase & Simon (1973) com mestres de xadrez mostrou que expertise é reconhecimento de padrões recorrentes tratados como um chunk só, e essa vantagem desaparece quando o padrão é quebrado (posição aleatória no tabuleiro). Consistência é o que permite ao leitor transferir o reconhecimento aprendido num arquivo para o próximo; inconsistência zera essa transferência e obriga reconstrução do zero a cada arquivo.

Prática:

// Ruim — três verbos para a mesma operação, em três arquivos diferentes
async function getUser(id: string) { ... }
async function fetchProducts() { ... }
async function retrieveOrders() { ... }

// Bom — mesmo verbo, reconhecível instantaneamente em qualquer arquivo
async function getUser(id: string) { ... }
async function getProducts() { ... }
async function getOrders() { ... }
Enter fullscreen mode Exit fullscreen mode

#2 Meaningful names over comments

Teoria: um nome bem escolhido externaliza um chunk pronto; o leitor consome o significado já processado em vez de derivá-lo ao vivo a partir da lógica bruta. Comentário tenta fazer o mesmo trabalho, mas com uma falha estrutural: não é verificado por nada, então quando o código muda e o comentário não acompanha, ele entrega um chunk errado — pior que não ter nome nenhum, porque agora o leitor gasta memória extra descobrindo que o comentário mentiu.

// Ruim — comentário tentando compensar nome ruim, e pode ficar desatualizado
// verifica se pode aplicar desconto de fidelidade
if (u.t === 2 && d < 30) { ... }

// Bom — o nome já é o chunk, comentário vira desnecessário
const isEligibleForLoyaltyDiscount = user.tier === Tier.Gold && daysSinceSignup < 30;
if (isEligibleForLoyaltyDiscount) { ... }
Enter fullscreen mode Exit fullscreen mode

#3 Indentation and Code Style

Teoria: Larkin & Simon (1987) mostram que representações espaciais bem construídas permitem inferência por reconhecimento visual (rápido, quase grátis) em vez de busca simbólica sequencial (lenta, consome working memory). Indentação consistente transforma aninhamento — uma informação lógica abstrata — em informação espacial, processada em paralelo pelo sistema visual em vez de competir pelo mesmo gargalo cognitivo usado para entender a regra de negócio.

// Ruim — indentação inconsistente esconde a estrutura de aninhamento
function calculate(x) {
if (x > 0) {
      return x * 2
  } else {
return 0
}}

// Bom — indentação consistente revela a estrutura de graça
function calculate(x: number) {
  if (x > 0) {
    return x * 2;
  }
  return 0;
}
Enter fullscreen mode Exit fullscreen mode

No projeto de vocês isso nem deveria ser decisão manual — oxfmt rodando via husky/lint-staged já resolve isso no commit, antes de chegar em code review.

#4 Keep methods / classes / files small

Teoria: o orçamento de working memory (4-7 chunks) é fixo por leitura. Uma função pequena cabe inteira dentro desse orçamento; uma função grande obriga o leitor a "paginar" informação — soltar um chunk pra abrir espaço pra outro — e é exatamente aí que se esquece uma condição vista no início enquanto se lê o fim.

Prática:

// Ruim  uma função fazendo filtro, soma e formatação juntos
function getOrderSummary(orders: Order[]) {
  const active= orders.filter(o=> o.status!== 'cancelled');
  const total= active.reduce((sum, o) => sum+ o.value, 0);
  return `${active.length} pedidos ativos, total de R$${total.toFixed(2).replace('.', ',')}`;
}

// Bom  cada função com um único nível de abstração, testável isoladamente
function getActiveOrders(orders: Order[]) {
  return orders.filter(o=> o.status!== 'cancelled');
}
function getTotalValue(orders: Order[]) {
  return orders.reduce((sum, o) => sum+ o.value, 0);
}
function formatCurrency(value: number) {
  return `R$${value.toFixed(2).replace('.', ',')}`;
}
function getOrderSummary(orders: Order[]) {
  const active= getActiveOrders(orders);
  return `${active.length} pedidos ativos, total de${formatCurrency(getTotalValue(active))}`;
}
Enter fullscreen mode Exit fullscreen mode

#6 Minimize cyclomatic complexity

Teoria: McCabe (1976) conta pontos de decisão (ifcase&&||, laços) na fórmula V(G) = E − N + 2; o NIST (SP 500-235) classifica acima de 10 como risco moderado e acima de 50 como praticamente intestável. Vale uma ressalva importante: a fórmula original de McCabe não pesa profundidade de aninhamento — um if aninhado dentro de outro conta igual a um if solto no mesmo nível, mesmo que o aninhado exija segurar mais chunks simultâneos na cabeça. Foi exatamente essa lacuna que motivou a SonarSource a propor, em 2017, a métrica de Complexidade Cognitiva, que penaliza aninhamento explicitamente — essa é, na prática, a métrica mais alinhada com o argumento do Cluster 1 (memória de trabalho), enquanto McCabe original mede melhor “quantos caminhos preciso testar” (o que cruza com o Cluster 4, de testes).

// Complexidade alta  decisões aninhadas, todas precisam ficar ativas na cabeça
function canEditOrder(user: User, order: Order) {
  if (user.role === 'admin') {
    return true;
  } else {
    if (order.status === 'draft') {
      if (order.ownerId === user.id) {
        return true;
      } else {
        return false;
      }
    } else {
      return false;
    }
  }
}

// Complexidade baixa  guard clauses, cada condição resolvida e descartada da memória
function canEditOrder(user: User, order: Order) {
  if (user.role === 'admin') return true;
  if (order.status !== 'draft') return false;
  return order.ownerId === user.id;
}
Enter fullscreen mode Exit fullscreen mode

Aqui o número de McCabe é quase o mesmo nas duas versões (mesmas 3 decisões) — a diferença real é a complexidade cognitiva: na primeira versão você precisa segurar até 3 condições aninhadas ativas ao mesmo tempo pra saber qual return está sendo executado,na segunda, cada linha resolve e descarta uma condição antes de passar pra próxima.

Top comments (0)