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!