DEV Community

Cover image for Defensive Coding: Writing Invariants, Preconditions, and Result Types That Never Panic
DEVANSHU PATIL
DEVANSHU PATIL

Posted on AI-assisted

Defensive Coding: Writing Invariants, Preconditions, and Result Types That Never Panic

Defensive Coding: Writing Invariants, Preconditions, and Result Types That Never Panic

Tony Hoare famously referred to his invention of the null reference as his "Billion-Dollar Mistake".

Decades later, NullPointerException (Java), TypeError: Cannot read properties of undefined (JavaScript), and panic: runtime error: invalid memory address or nil pointer dereference (Go) remain the most frequent causes of production crashes.

Defensive coding does not mean wrapping every line of code in paranoia-driven try-catch blocks.

Defensive coding means designing types and boundaries so that invalid states are unrepresentable by compile-time construction.

Here is how to write bulletproof, self-documenting code that never panics in production.

1. Make Illegal States Unrepresentable

Consider an e-commerce order status model:

# FRAGILE DESIGN:
class Order:
    def __init__(self, status: str, tracking_number: str = None, refund_reason: str = None):
        self.status = status
        self.tracking_number = tracking_number
        self.refund_reason = refund_reason
Enter fullscreen mode Exit fullscreen mode

What stops someone from instantiating an order with status="REFUNDED" but with a tracking_number and no refund_reason? Nothing!

The Defensive Architecture: Algebraic Data Types

In modern languages, model mutually exclusive states as distinct types:

from dataclasses import dataclass
from typing import Union

@dataclass(frozen=True)
class PendingOrder:
    order_id: str

@dataclass(frozen=True)
class ShippedOrder:
    order_id: str
    tracking_number: str # Guaranteed present by type definition!

@dataclass(frozen=True)
class RefundedOrder:
    order_id: str
    refund_reason: str   # Guaranteed present by type definition!

Order = Union[PendingOrder, ShippedOrder, RefundedOrder]
Enter fullscreen mode Exit fullscreen mode

Now, code handling ShippedOrder is guaranteed by the type system to have a valid tracking_number. The need for runtime null checks vanishes completely.

2. Replace Exceptions with the Result[T, E] Pattern

from typing import Generic, TypeVar, Union

T = TypeVar("T")
E = TypeVar("E")

class Ok(Generic[T]):
    def __init__(self, value: T):
        self.value = value
    def is_ok(self) -> bool: return True

class Err(Generic[E]):
    def __init__(self, error: E):
        self.error = error
    def is_ok(self) -> bool: return False

Result = Union[Ok[T], Err[E]]

def divide(a: float, b: float) -> Result[float, str]:
    if b == 0:
        return Err("Division by zero")
    return Ok(a / b)

res = divide(10, 0)
if res.is_ok():
    print(f"Result: {res.value}")
else:
    print(f"Handled error safely: {res.error}")
Enter fullscreen mode Exit fullscreen mode

Summary Checklist for Defensive Code

  • [x] Fail Fast at the Boundary: Validate inputs at the API edge.
  • [x] Prefer Immutability: Use frozen=True dataclasses or records. Immutable data cannot be corrupted by concurrent threads.
  • [x] Never Return null Collections: Return an empty list or set ([]), never null.

Top comments (0)