Aikido

Perché dovresti evitare assegnazioni nelle condizioni per prevenire bug nascosti

Leggibilità

Regola
Non inserire assegnazioni all'interno di condizioni. 
Mischiare assegnazione e logica condizionale rende il codice soggetto a errori
e più difficile da comprendere. Separa le assegnazioni dai controlli logici. 

Linguaggi supportati:** JavaScript, TypeScript, Python, PHP

Introduzione

Gli operatori di assegnazione all'interno di istruzioni condizionali rappresentano una fonte comune di bug che spesso i compilatori e i linter non rilevano. L'errore classico consiste nell'usare = (assegnazione) anziché == o === (confronto) in un'istruzione if, ma il problema è più profondo. Anche le assegnazioni intenzionali all'interno delle condizioni creano codice difficile da leggere, revisionare ed eseguire il debug. Quando l'assegnazione e la valutazione avvengono sulla stessa riga, chi legge deve mentalmente analizzare quale operazione abbia la precedenza e quale sia effettivamente il valore testato.

Perché è importante

Perché è importante

Introduzione di bug: Un errore di battitura che trasforma === in = non genererà un errore di sintassi, ma modificherà silenziosamente il comportamento. La condizione restituirà il valore assegnato (truthy/falsy), non il risultato del confronto.

Leggibilità del codice: Chi legge si aspetta che le condizioni verifichino dei valori, non che li modifichino. Quando entrambe le cose avvengono simultaneamente, chi effettua la manutenzione deve rintracciare quali variabili vengano modificate e quando.

Esempi di codice

❌ Non 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;
}

Perché è errato: Le assegnazioni all'interno delle condizioni rendono poco chiaro se si tratti di un'intenzione o di un errore di battitura. Il primo esempio potrebbe essere un bug in cui si intendeva usare ===, mentre il secondo unisce la corrispondenza di un'espressione regolare a un'assegnazione, rendendo difficile seguire il flusso del codice.

✅ Conforme:

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;
}

Perché questo è importante: Separare l'assegnazione dalla condizione rende l'intento estremamente chiaro. Chi legge vede immediatamente che user viene estratto per primo e poi testato. Il risultato della corrispondenza dell'espressione regolare viene catturato e successivamente valutato. Nessuna ambiguità, nessun carico cognitivo ed errori di battitura come = contro === diventano evidenti.

Conclusione

Tenere le assegnazioni separate dalle condizioni è una regola semplice che previene un'intera classe di bug. Il carico cognitivo derivante dall'analisi di operazioni combinate supera qualsiasi vantaggio di presunta brevità. Un codice chiaro ed esplicito, in cui l'assegnazione e la valutazione sono operazioni distinte, migliora la leggibilità, riduce i bug e rende la code review più efficace.

FAQ

Hai domande?

Che dire dei casi in cui l'assegnazione nelle condizioni è idiomatica, come la lettura di file?

Anche nei linguaggi in cui while (line = file.readline()) è comune, le moderne best practice favoriscono una separazione esplicita. In JavaScript, usa i protocolli iterator: for (const line of fileLines). In Python 3.8+, l'operatore tricheco := rende esplicito l'intento quando l'assegnazione nelle condizioni è realmente necessaria, ma anche in quel caso, valuta se istruzioni separate sarebbero più chiare.

Ci sono implicazioni sulle performance nel separare l'assegnazione dalle condizioni?

No. I moderni motori JavaScript ottimizzano entrambi i pattern in modo identico. La separazione aggiunge una sola dichiarazione di variabile, che ha un costo di runtime pari a zero dopo la compilazione. Qualsiasi differenza di performance percepita è trascurabile rispetto ai benefici di prevenzione dei bug e leggibilità. Scrivi prima un codice chiaro, ottimizzalo solo quando la profilazione individua effettivi colli di bottiglia.

Come gestisco pattern come if ((match = regex.exec(str)) !== null)?

Suddividilo in due istruzioni: const match = regex.exec(str); if (match !== null). O meglio, usa alternative moderne: const match = str.match(regex); if (match). Il controllo esplicito sul valore null diventa ridondante perché match() restituisce null in caso di fallimento, che è un valore falsy. La chiarezza migliora e l'intento diventa ovvio.

E per quanto riguarda le assegnazioni usate intenzionalmente per il loro valore di ritorno?

Essere intenzionali non significa che sia una buona pratica. Il codice che si basa sui valori di ritorno delle assegnazioni all'interno delle condizioni crea problemi di manutenzione. Futuri sviluppatori potrebbero "correggere" quello che sembra un errore di digitazione. Se devi assolutamente usare questo pattern, aggiungi un commento che ne spieghi il motivo, ma riconsidera se il codice potrebbe essere ristrutturato in modo più chiaro.

Questa regola si applica anche agli operatori ternari?

Sì. Evita const x = (y = getValue()) ? y : defaultValue. Questo è ancora più difficile da leggere rispetto alle istruzioni if. Usa: const y = getValue(); const x = y ? y : defaultValue. O meglio, usa il nullish coalescing: const x = getValue() ?? defaultValue. Esistono operatori moderni pensati specificamente per evitare questi pattern poco agevoli.

Come gestiscono questo pattern i linter e gli strumenti di analisi statica?

La maggior parte dei linter moderni segnala l'assegnazione nelle condizioni per impostazione predefinita o tramite configurazione. Di solito richiedono parentesi aggiuntive if ((x = y)) per segnalare l'assegnazione intenzionale, ma questo è un code smell. È meglio disabilitare l'eccezione del linter e correggere adeguatamente il codice. Gli strumenti di analisi statica possono rilevare questi pattern durante la CI/CD, impedendo che arrivino in produzione.

Metti in sicurezza ora

Metti in sicurezza il tuo codice, il cloud e il runtime in un unico sistema centralizzato.
Trova e risolvi le vulnerabilità rapidamente e automaticamente.

Nessuna carta di credito richiesta | Risultati della scansione in 32 secondi.