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() { ... }
#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) { ... }
#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;
}
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))}`;
}
#6 Minimize cyclomatic complexity
Teoria: McCabe (1976) conta pontos de decisão (if, case, &&, ||, 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;
}
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)