Skip to main content

Defensive Programming

beginner10 min readLesson 29 of 169

Validate at the boundary, raise on broken invariants, and turn silent bugs into loud ones.

raise throws an exception deliberately — when the data is wrong, fail loudly:

def set_price(price):
    if price < 0:
        raise ValueError(f"price cannot be negative, got {price}")
    return price

A function that fails fast with a clear message is kinder than one that returns nonsense and lets the bug surface three layers away. Raising early converts a silent logic error into a loud, debuggable runtime error.

Defensive checks in practice

def average(values):
    if not values:                 # None, [], empty — all falsy
        raise ValueError("average of empty data is undefined")
    return sum(values) / len(values)

The check is one line; the debugging time it saves is hours. As a rule: validate at the boundary (where data enters your program), raise inside (when your own invariants break).