AI is redefining software quality and security. Insights from 450 CISOs & devs →
Aikido

Avoid assignments in conditionals: preventing hidden bugs

Rule
Don't place assignments inside conditionals. 
Mixing assignment and condition logic makes code error-prone and harder to understand. Separate assignments from logical checks. 
Supported languages:** JavaScript, TypeScript, Python, PHP

Introduction

Assignment operators inside conditional statements are a common source of bugs that compilers and linters often miss. The classic mistake is using = (assignment) instead of == or === (comparison) in an if statement, but the problem runs deeper. Even intentional assignments in conditionals create code that's difficult to read, review, and debug. When assignment and evaluation happen on the same line, readers must mentally parse which operation takes precedence and what value is actually being tested.

Why it matters

Why it matters

Bug introduction: A typo changing === to = won't cause a syntax error, just silently changes behavior. The condition evaluates to the assigned value (truthy/falsy), not the comparison result.

Code readability: Readers expect conditionals to test values, not modify them. When both happen simultaneously, maintainers must trace which variables are being modified and when.

Code examples

❌ Non-compliant:

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

Why it's wrong: The assignments inside conditionals make it unclear whether this is intentional or a typo. The first example could be a bug where === was intended, and the second mixes regex matching with assignment, making the code flow hard to follow.

✅ Compliant:

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

Why this matters: Separating assignment from the conditional makes intent crystal clear. Readers immediately see that user is extracted first, then tested. The regex match result is captured, then evaluated. No ambiguity, no cognitive overhead, and typos like = vs === become obvious.

Conclusion

Keeping assignments separate from conditionals is a simple rule that prevents an entire class of bugs. The cognitive overhead of parsing combined operations outweighs any perceived brevity benefit. Clear, explicit code where assignment and evaluation are distinct operations improves readability, reduces bugs, and makes code review more effective.

FAQs

Got Questions?

What about cases where assignment in conditionals is idiomatic, like file reading?

Even in languages where while (line = file.readline()) is common, modern best practice favors explicit separation. In JavaScript, use iterator protocols: for (const line of fileLines). In Python 3.8+, the walrus operator := makes intent explicit when assignment in conditionals is genuinely needed, but even then, consider whether separate statements would be clearer.

Are there performance implications to separating assignment from conditionals.

No. Modern JavaScript engines optimize both patterns identically. The separation adds one variable declaration, which has zero runtime cost after compilation. Any perceived performance difference is negligible compared to the bug prevention and readability benefits. Write clear code first, optimize only when profiling identifies actual bottlenecks.

How do I handle patterns like if ((match = regex.exec(str)) !== null)?

Break it into two statements: const match = regex.exec(str); if (match !== null). Or better, use modern alternatives: const match = str.match(regex); if (match). The explicit null check becomes redundant because match() returns null on failure, which is falsy. Clarity improves and the intent becomes obvious.

What about assignments that are intentionally used for their return value?

Intentional doesn't mean good practice. Code that relies on assignment return values in conditionals creates maintenance hazards. Future editors might "fix" what looks like a typo. If you absolutely must use this pattern, add a comment explaining why, but reconsider whether the code could be restructured more clearly.

Does this rule apply to ternary operators?

Yes. Avoid const x = (y = getValue()) ? y : defaultValue. This is even harder to read than in if statements. Use: const y = getValue(); const x = y ? y : defaultValue. Or better, use nullish coalescing: const x = getValue() ?? defaultValue. Modern operators exist specifically to avoid these awkward patterns.

How do linters and static analysis tools handle this pattern?

Most modern linters flag assignment in conditionals by default or via configuration. They typically require extra parentheses if ((x = y)) to signal intentional assignment, but this is a code smell. Better to disable the linter exception and fix the code properly. Static analysis tools can detect these patterns during CI/CD, preventing them from reaching production.

Get secure for free

Secure your code, cloud, and runtime in one central system.
Find and fix vulnerabilities fast automatically.

No credit card required | Scan results in 32secs.