Aikido

Por que você deve evitar atribuições em condicionais para prevenir bugs ocultos

Legibilidade

Regra
Não colocar atribuições dentro de condicionais. 
Misturar atribuição e condição lógica faz com que o código propenso a erros
e mais difícil de compreender. Separar tarefas de lógicas lógicas. 

Suportadas linguagens suportadas:** JavaScript, TypeScript, Python, PHP

Introdução

Operadores de atribuição dentro de instruções condicionais são uma fonte comum de bugs que compiladores e linters frequentemente não detectam. O erro clássico é usar = (atribuição) em vez de == ou === (comparação) em uma instrução if, mas o problema é mais profundo. Mesmo atribuições intencionais em condicionais criam código que é difícil de ler, revisar e depurar. Quando a atribuição e a avaliação ocorrem na mesma linha, os leitores precisam analisar mentalmente qual operação tem precedência e qual valor está sendo realmente testado.

Por que isso importa

Por que isso importa

Introdução de bug: Um erro de digitação alterando === para = não causará um erro de sintaxe, apenas alterará o comportamento silenciosamente. A condição avalia o valor atribuído (verdadeiro/falso), não o resultado da comparação.

Legibilidade do código: Leitores esperam que condicionais testem valores, não os modifiquem. Quando ambos ocorrem simultaneamente, os mantenedores devem rastrear quais variáveis estão sendo modificadas e quando.

Exemplos de código

❌ Não-conforme:

function processUser(userData) {
    if (user = userData.user) {
        console.log(`Processing user: ${user.name}`);
        return user.id;
    }
    return null;
}

function validateInput(value) {
    if (result = value.match(/^\d{3}-\d{2}-\d{4}$/)) {
        return result[0];
    }
    return false;
}

Por que está errado: As atribuições dentro de condicionais tornam incerto se isso é intencional ou um erro de digitação. O primeiro exemplo pode ser um bug onde === era o pretendido, e o segundo mistura correspondência de regex com atribuição, tornando o fluxo do código difícil de seguir.

✅ Compatível:

function processUser(userData) {
    const user = userData.user;
    if (user) {
        console.log(`Processing user: ${user.name}`);
        return user.id;
    }
    return null;
}

function validateInput(value) {
    const result = value.match(/^\d{3}-\d{2}-\d{4}$/);
    if (result) {
        return result[0];
    }
    return false;
}

Por que isso importa: Separar a atribuição da condicional torna a intenção cristalina. Os leitores veem imediatamente que usuário é extraído primeiro, depois testado. O resultado da correspondência regex é capturado, depois avaliado. Sem ambiguidade, sem sobrecarga cognitiva e erros de digitação como = vs. === tornar-se óbvio.

Conclusão

Manter as atribuições separadas das condicionais é uma regra simples que previne uma classe inteira de bugs. A sobrecarga cognitiva de analisar operações combinadas supera qualquer benefício percebido de concisão. Código claro e explícito, onde atribuição e avaliação são operações distintas, melhora a legibilidade, reduz bugs e torna a revisão de código mais eficaz.

FAQs

Dúvidas?

E quanto aos casos em que a atribuição em condicionais é idiomática, como na leitura de arquivos?

Mesmo em linguagens onde `while (line = file.readline())` é comum, as melhores práticas modernas favorecem a separação explícita. Em JavaScript, use protocolos de iterador: `for (const line of fileLines)`. No Python 3.8+, o operador walrus `:=` torna a intenção explícita quando a atribuição em condicionais é genuinamente necessária, mas mesmo assim, considere se declarações separadas seriam mais claras.

Existem implicações de desempenho ao separar a atribuição de condicionais?

Não. Engines JavaScript modernos otimizam ambos os padrões de forma idêntica. A separação adiciona uma declaração de variável, que tem custo de runtime zero após a compilação. Qualquer diferença de performance percebida é insignificante comparada aos benefícios de prevenção de bugs e legibilidade. Escreva código claro primeiro, otimize apenas quando o profiling identificar gargalos reais.

Como lidar com padrões como if ((match = regex.exec(str)) !== null)?

Divida-o em duas declarações: const match = regex.exec(str); if (match !== null). Ou melhor, use alternativas modernas: const match = str.match(regex); if (match). A verificação explícita de nulo torna-se redundante porque match() retorna nulo em caso de falha, o que é um valor falsy. A clareza melhora e a intenção se torna óbvia.

E quanto às atribuições que são intencionalmente usadas por seu valor de retorno?

Intencional não significa boa prática. Código que depende de valores de retorno de atribuição em condicionais cria riscos de manutenção. Futuros editores podem "corrigir" o que parece ser um erro de digitação. Se você realmente precisa usar este padrão, adicione um comentário explicando o porquê, mas reconsidere se o código poderia ser reestruturado de forma mais clara.

Esta regra se aplica a operadores ternários?

Sim. Evite const x = (y = getValue()) ? y : defaultValue. Isso é ainda mais difícil de ler do que em instruções `if`. Use: const y = getValue(); const x = y ? y : defaultValue. Ou melhor, use o operador de coalescência nula: const x = getValue() ?? defaultValue. Operadores modernos existem especificamente para evitar esses padrões desajeitados.

Como linters e ferramentas de análise estática lidam com este padrão?

A maioria dos linters modernos sinaliza atribuições em condicionais por padrão ou via configuração. Eles geralmente exigem parênteses extras se ((x = y)) para sinalizar uma atribuição intencional, mas isso é um code smell. É melhor desabilitar a exceção do linter e corrigir o código adequadamente. Ferramentas de análise estática podem detectar esses padrões durante o CI/CD, impedindo que cheguem à produção.

Fique seguro agora

Proteja seu código, Cloud e runtime em um único sistema centralizado.
Encontre e corrija vulnerabilidades rapidamente de forma automática.

Não é necessário cartão de crédito | Resultados da varredura em 32 segundos.