..

Elseless

Here's a little "cognitive complexity" tip for your next programming project: get rid of your else statements.

Okay... maybe not all of them. But as many as you can, because unnecessary else statements can decrease the readability of your code in small, but often very meaningful, ways.

This isn't a hard-and-fast rule—which was made abundantly clear when I posted this on LinkedIn and triggered a small flame war—but when I write my own code, I tend to keep the following guidelines in mind to keep else-driven cognitive complexity down:

Return or raise early?

If returning or raising from within an if block, you can often drop the else and unindent the remainder.

Before

def process(value):
    if value is None:
        raise ValueError("value cannot be None")
    else:
        return value * 2

After

def process(value):
    if value is None:
        raise ValueError("value cannot be None")

    return value * 2

Continue/break inside loops?

Similar to the above rule, if you continue or break inside an if, you can often skip the else and put the "keep" logic after the guard.

Before

for item in items:
    if not is_valid(item):
        continue
    else:
      process(item)

After

for item in items:
    if not is_valid(item):
        continue

    process(item)

Assign defaults first

An old "C" programming language convention: assign defaults first. When you do that, you can often avoid else statements entirely.

Before

def get_status(user):
    if user.is_active:
        status = "active"
    else:
        status = "inactive"

    return status

After

def get_status(user):
    status = "inactive"

    if user.is_active:
        status = "active"

    return status

Or, an even cleaner version, dropping the intermediate variable and using a guard clause:

def get_status(user):
    if user.is_active:
        return "active"

    return "inactive"

Guard clauses for invalid/edge cases?

If you have multiple branches to handle edge cases, consider using guard clauses to handle them first so the main path of your function is clear. What's a "guard clause"? It's just an if statement that returns or raises early.

Before

def calculate_discount(price, customer):
    if customer.is_vip:
        return price * 0.8
    else:
        if customer.is_loyal:
            return price * 0.9
        else:
            return price

After

def calculate_discount(price, customer):
    if customer.is_vip:
        return price * 0.8

    if customer.is_loyal:
        return price * 0.9

    return price

Nested if/else causing indentation creep?

More indentation means more cognitive load. If you find yourself with nested if/else statements, see if you can convert inner branches to guard returns/raises and unwrap.

Before

def process_order(order):
    if order.is_valid():
        if order.is_in_stock():
            if order.payment_received():
                if order.ship():
                    return "Order shipped"
                else:
                    return "Shipping failed"
            else:
                return "Payment not received"
        else:
            return "Out of stock"
    else:
        return "Invalid order"

After

def process_order(order):
    if not order.is_valid():
        return "Invalid order"

    if not order.is_in_stock():
        return "Out of stock"

    if not order.payment_received():
        return "Payment not received"

    if order.ship():
        return "Order shipped"
    else:
        return "Shipping failed"

Both branches return the same type/value shape?

Sometimes both branches of an if/else return the same type or shape of value. In those cases, I prefer two returns over a return and else.

Before

def get_user_status(user):
    if user.is_active:
        return "active"
    else:
        return "inactive"

After

def get_user_status(user):
    if user.is_active:
        return "active"

    return "inactive"

--

If you like this post or one of my projects, you can buy me a coffee, or send me a note. I'd love to hear from you!