← Home

043. Nested Conditionals

Avoid deep nesting with early returns

043. Nested Conditionals

RAM Manager: Kill Eligibility

Aryan’s RAM manager needs to decide whether a process can be killed:

  • Must not be a system process (pid > 100)
  • Must be using more than 10% RAM
  • Must not be in the protected list

His first version is a “pyramid of doom” — deeply nested if/else that’s hard to read and easy to get wrong:

# Pyramid of doom version
def can_kill(pid, mem_pct, protected):
    if pid > 100:
        if mem_pct > 10:
            if pid not in protected:
                return True
            else:
                return False
        else:
            return False
    else:
        return False

The guard clause refactoring is not just a style preference — it prevents bugs. When you add a 4th condition to the pyramid, you have to find the right nesting level. With guard clauses, you just add another if not ...: return False line.


💡 Fun fact: The “pyramid of doom” or “arrow anti-pattern” — deeply nested conditionals that push code to the right — is so notorious that Jeff Atwood coined the term on his blog Coding Horror in 2006. The problem exists in every language, but it’s especially visible in Python where indentation is syntactically enforced, making the nesting physically painful to read.

⚠️ Watch out: When inverting conditions for guard clauses, beginners often flip the logic incorrectly. if age >= 18: inverts to if age < 18: return False — not if age <= 18:. An off-by-one in the guard condition silently excludes valid inputs or accepts invalid ones at the boundary value.

🤔 Think about it: The guard clause pattern returns False early for each failing condition, then returns True at the bottom. What if you have a function with 10 guard clauses — is that a sign the function is well-structured, or a code smell suggesting it should be broken into smaller functions?

Learning objectives

  • Recognize and refactor deeply nested conditionals
  • Use early returns (guard clauses) to flatten nesting
  • Replace nested if/else with if/elif chains

Key concepts

  • nested conditionals
  • guard clauses
  • early return
  • flat structure

Try it

Concept detail

The “pyramid of doom” — nested conditionals — makes code hard to read and extend:

if cond1: if cond2: if cond3: return success # buried at level 3 else: return fail3 # where does this case end up? else: return fail2 else: return fail1

Guard clause refactoring — invert each failing condition, return early, flatten:

if not cond1: return fail1 # guard 1 if not cond2: return fail2 # guard 2 if not cond3: return fail3 # guard 3 return success # happy path — always last, always visible

Benefits:

  • Each condition is one line, easy to scan
  • Adding a 4th condition is trivial (add one line)
  • The success case is obvious (it’s the last line)
  • Easier to unit test each guard independently

For mutually exclusive classification (not guards), use if/elif/else, not nested else-if. Nested else-if and elif chains are functionally identical, but elif is one indentation level.

Solution

def is_eligible(age, income, credit_score):
    if age < 18:
        return False
    if income < 30000:
        return False
    if credit_score < 650:
        return False
    return True

def classify_loan(amount):
    if amount < 1000:
        return "micro"
    elif amount < 10000:
        return "personal"
    elif amount < 100000:
        return "business"
    else:
        return "enterprise"

Tests

def test_eligible_all_good():
    assert is_eligible(25, 50000, 700) == True

def test_too_young():
    assert is_eligible(17, 50000, 700) == False

def test_low_income():
    assert is_eligible(25, 25000, 700) == False

def test_low_credit():
    assert is_eligible(25, 50000, 600) == False

def test_exactly_eligible():
    # Boundary values — exactly at minimums
    assert is_eligible(18, 30000, 650) == True

def test_micro_loan():
    assert classify_loan(500) == "micro"
    assert classify_loan(999) == "micro"

def test_personal_loan():
    assert classify_loan(1000) == "personal"
    assert classify_loan(5000) == "personal"

def test_business_loan():
    assert classify_loan(10000) == "business"
    assert classify_loan(50000) == "business"

def test_enterprise_loan():
    assert classify_loan(100000) == "enterprise"
    assert classify_loan(200000) == "enterprise"

Resources