Aikido

Pourquoi il faut éviter les affectations dans les conditions pour prévenir les bugs cachés

Lisibilité

Règle
Ne pas placer affectations à l'intérieur conditionnelles. 
Mélanger des affectation et condition logique rend le code sujet aux erreurs
et plus plus plus difficile à comprendre. Séparer devoirs des logiques logiques. 

Langues supportés:** JavaScript, TypeScript, Python, PHP

Introduction

Les opérateurs d'affectation au sein des instructions conditionnelles sont une source courante de bugs que les compilateurs et les linters manquent souvent. L'erreur classique est d'utiliser = (affectation) au lieu de == ou === (comparaison) dans une instruction if, mais le problème est plus profond. Même les affectations intentionnelles dans les conditions créent un code difficile à lire, à réviser et à déboguer. Lorsque l'affectation et l'évaluation se produisent sur la même ligne, les lecteurs doivent analyser mentalement quelle opération a la priorité et quelle valeur est réellement testée.

Pourquoi c'est important

Pourquoi c'est important

Introduction de bugs : Une faute de frappe modifiant === à = ne causera pas d'erreur de syntaxe, mais modifiera silencieusement le comportement. La condition évalue la valeur assignée (vraie/fausse), et non le résultat de la comparaison.

Lisibilité du code : Les lecteurs s'attendent à ce que les conditions testent des valeurs, et non à ce qu'elles les modifient. Lorsque les deux se produisent simultanément, les mainteneurs doivent suivre quelles variables sont modifiées et à quel moment.

Exemples de code

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

Pourquoi c'est incorrect : Les affectations à l'intérieur des conditions rendent incertain si cela est intentionnel ou une faute de frappe. Le premier exemple pourrait être un bug où === était prévu, et le second mélange la correspondance d'expressions régulières avec l'affectation, rendant le flux de code difficile à suivre.

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

Pourquoi c'est important : Séparer l'affectation de la conditionnelle rend l'intention parfaitement claire. Les lecteurs voient immédiatement que utilisateur est extrait en premier, puis testé. Le résultat de la correspondance regex est capturé, puis évalué. Pas d'ambiguïté, pas de surcharge cognitive, et les fautes de frappe comme = vs === deviennent évidentes.

Conclusion

Séparer les affectations des conditions est une règle simple qui prévient toute une catégorie de bugs. La charge cognitive liée à l'analyse des opérations combinées l'emporte sur tout avantage perçu en termes de concision. Un code clair et explicite où l'affectation et l'évaluation sont des opérations distinctes améliore la lisibilité, réduit les bugs et rend la revue de code plus efficace.

FAQ

Des questions ?

Qu'en est-il des cas où l'affectation dans les conditions est idiomatique, comme la lecture de fichiers ?

Même dans les langages où `while (line = file.readline())` est courant, les bonnes pratiques modernes privilégient une séparation explicite. En JavaScript, utilisez les protocoles d'itérateur : `for (const line of fileLines)`. En Python 3.8+, l'opérateur morse `:=` explicite l'intention lorsque l'affectation dans les conditions est réellement nécessaire, mais même dans ce cas, demandez-vous si des instructions séparées seraient plus claires.

Y a-t-il des implications en termes de performance à séparer l'affectation des conditionnelles.

Non. Les moteurs JavaScript modernes optimisent les deux modèles de manière identique. La séparation ajoute une déclaration de variable, ce qui n'a aucun coût runtime après compilation. Toute différence de performance perçue est négligeable par rapport aux avantages en matière de prévention des bugs et de lisibilité. Écrivez d'abord un code clair, n'optimisez que lorsque le profilage identifie de réels goulots d'étranglement.

Comment gérer les motifs comme if ((match = regex.exec(str)) !== null)?

Divisez-le en deux instructions : const match = regex.exec(str); if (match !== null). Ou mieux, utilisez des alternatives modernes : const match = str.match(regex); if (match). La vérification explicite de null devient redondante car match() renvoie null en cas d'échec, ce qui est une valeur falsy. La clarté s'améliore et l'intention devient évidente.

Qu'en est-il des affectations utilisées intentionnellement pour leur valeur de retour ?

Être intentionnel ne signifie pas adopter une bonne pratique. Le code qui s'appuie sur les valeurs de retour d'affectation dans les conditions crée des risques de maintenance. Les futurs éditeurs pourraient « corriger » ce qui semble être une faute de frappe. Si vous devez absolument utiliser ce modèle, ajoutez un commentaire expliquant pourquoi, mais reconsidérez si le code pourrait être restructuré plus clairement.

Cette règle s'applique-t-elle aux opérateurs ternaires ?

Oui. Évitez `const x = (y = getValue()) ? y : defaultValue`. C'est encore plus difficile à lire que dans les instructions `if`. Utilisez : `const y = getValue(); const x = y ? y : defaultValue`. Ou mieux, utilisez l'opérateur de coalescence des nuls : `const x = getValue() ?? defaultValue`. Les opérateurs modernes existent spécifiquement pour éviter ces schémas maladroits.

Comment les linters et les outils d'analyse statique gèrent-ils ce modèle ?

La plupart des linters modernes signalent l'affectation dans les conditions par défaut ou via la configuration. Ils nécessitent généralement des parenthèses supplémentaires si ((x = y)) pour signaler une affectation intentionnelle, mais c'est une mauvaise pratique de codage (code smell). Il est préférable de désactiver l'exception du linter et de corriger le code correctement. Les outils d'analyse statique peuvent détecter ces schémas pendant le CI/CD, les empêchant d'atteindre la production.

Sécurisez-vous maintenant.

Sécuriser votre code, votre cloud et votre runtime dans un système centralisé unique.
Détectez et corrigez les vulnérabilités rapidement et automatiquement.

Pas de carte de crédit requise | Résultats du scan en 32 secondes.