assertions
programming
debugging
best practices
software development

Best practice for using assert?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Assertions are debugging checks that verify assumptions about your program's internal state. Use assert to catch programmer errors (bugs) during development, not to validate user input or handle expected runtime conditions. Assertions can be disabled in production (python -O, C/C++ NDEBUG), so code must never depend on them for correctness. The core rule: if the condition can legitimately be False due to external factors (bad input, network failure, missing file), use error handling instead of assert.

Basic Assertion Syntax

Python

python
1def calculate_average(numbers):
2    assert len(numbers) > 0, "Cannot average an empty list"
3    return sum(numbers) / len(numbers)
4
5calculate_average([1, 2, 3])  # 2.0
6calculate_average([])          # AssertionError: Cannot average an empty list

JavaScript (Node.js)

javascript
1import assert from 'node:assert';
2
3function divide(a, b) {
4  assert(b !== 0, 'Divisor must not be zero');
5  return a / b;
6}
7
8divide(10, 2);  // 5
9divide(10, 0);  // AssertionError: Divisor must not be zero

C/C++

c
1#include <assert.h>
2
3void process_buffer(char *buf, int size) {
4    assert(buf != NULL);    // Crash with message if NULL
5    assert(size > 0);       // Crash if size is invalid
6    // ... process buffer
7}

Java

java
1public class MathUtils {
2    public static double sqrt(double value) {
3        assert value >= 0 : "sqrt requires non-negative input: " + value;
4        return Math.sqrt(value);
5    }
6}
7// Run with: java -ea MathUtils (assertions disabled by default in Java)

When to Use Assert

1. Internal Invariants

python
1def process_state(state):
2    if state == "active":
3        handle_active()
4    elif state == "inactive":
5        handle_inactive()
6    else:
7        # Should never reach here if our code is correct
8        assert False, f"Unknown state: {state}"

2. Preconditions on Internal Functions

python
1def _internal_sort(data, start, end):
2    # This is a private helper — callers are our own code
3    assert start >= 0, "start index must be non-negative"
4    assert end <= len(data), "end index out of bounds"
5    assert start <= end, "start must not exceed end"
6    # ... sort logic

3. Postconditions (Verify Results)

python
1def binary_search(sorted_list, target):
2    # ... search logic ...
3    result = _do_search(sorted_list, target)
4
5    # Verify our implementation is correct
6    assert result == -1 or sorted_list[result] == target, \
7        f"Binary search bug: found index {result} but value is {sorted_list[result]}"
8    return result

4. Class Invariants

python
1class BankAccount:
2    def __init__(self, balance):
3        self.balance = balance
4        self._check_invariant()
5
6    def withdraw(self, amount):
7        self.balance -= amount
8        self._check_invariant()
9
10    def _check_invariant(self):
11        assert self.balance >= 0, f"Balance went negative: {self.balance}"

When NOT to Use Assert

Do Not Validate User Input

python
1# WRONG — assert can be disabled with python -O
2def create_user(username):
3    assert len(username) > 0, "Username required"  # Bug: disabled in production!
4    assert "@" not in username, "Invalid character"
5
6# RIGHT — use proper validation
7def create_user(username):
8    if not username:
9        raise ValueError("Username is required")
10    if "@" in username:
11        raise ValueError("Username cannot contain '@'")

Do Not Check External Conditions

python
1# WRONG — file existence is not a programming error
2def read_config(path):
3    assert os.path.exists(path), f"Config not found: {path}"
4
5# RIGHT — raise an appropriate exception
6def read_config(path):
7    if not os.path.exists(path):
8        raise FileNotFoundError(f"Config not found: {path}")

Do Not Use for Flow Control

python
1# WRONG — using assert for expected error handling
2def parse_int(value):
3    try:
4        return int(value)
5    except ValueError:
6        assert False, "Not a number"  # Misuse of assert
7
8# RIGHT
9def parse_int(value):
10    try:
11        return int(value)
12    except ValueError:
13        raise ValueError(f"Expected integer, got: {value!r}")

Assertions Are Removed in Production

python
1# Python: python -O disables assertions
2# The -O flag sets __debug__ to False and removes assert statements
3
4# This code has a BUG when run with python -O:
5def delete_user(user_id):
6    assert isinstance(user_id, int), "user_id must be int"
7    # With -O, the assert is removed — any type gets through
8    database.delete(user_id)
9
10# This is safe regardless of optimization level:
11def delete_user(user_id):
12    if not isinstance(user_id, int):
13        raise TypeError(f"user_id must be int, got {type(user_id).__name__}")
14    database.delete(user_id)
c
1// C/C++: compile with -DNDEBUG to disable assertions
2// assert(ptr != NULL) becomes a no-op
3
4// Java: assertions disabled by default
5// Must run with java -ea to enable

Assert with Side Effects (The Biggest Mistake)

python
1# CRITICAL BUG: the side effect is removed when assertions are disabled
2assert users.pop() is not None  # Removes and checks
3
4# With python -O, .pop() is never called — the list is never modified
5
6# CORRECT: separate the operation from the check
7user = users.pop()
8assert user is not None

Never put code with side effects inside an assert statement. When assertions are disabled, the entire expression is removed.

Testing Frameworks and Assert

python
1# pytest — assert with automatic introspection
2def test_addition():
3    assert 1 + 1 == 2  # pytest shows detailed failure info
4
5def test_list_contains():
6    items = [1, 2, 3]
7    assert 4 in items  # AssertionError: assert 4 in [1, 2, 3]
javascript
1// Node.js assert module — for testing
2import assert from 'node:assert/strict';
3
4assert.strictEqual(1 + 1, 2);
5assert.deepStrictEqual([1, 2], [1, 2]);
6assert.throws(() => { throw new Error('fail'); }, /fail/);

In test code, assertions are the primary verification mechanism. Testing frameworks typically never disable assertions.

Common Pitfalls

  • Side effects in assert: assert data.save() does nothing in production with optimizations enabled. The .save() call is completely removed. Always separate side effects from assertions.
  • Asserting on user/external input: Users can send any data. Files can be missing. Network calls can fail. These are not programming errors — they are expected conditions that need proper error handling (ValueError, FileNotFoundError, try/catch).
  • Forgetting assertions are disabled: Python's -O flag, C's NDEBUG, and Java's default mode all disable assertions. If your program depends on assert to prevent dangerous operations, those checks vanish in production.
  • Assertions as documentation instead of code: assert x > 0 is not a substitute for a docstring or type hint. Use type annotations (x: int) and docstrings for documentation, and assert only for runtime invariant checking during development.
  • Performance-heavy assertions: assert sorted(huge_list) == huge_list sorts the entire list just to verify it is sorted. Use lightweight checks or add a flag to skip expensive assertions: assert not DEBUG_SLOW or is_sorted(data).

Summary

  • Use assert for internal invariants, preconditions on private functions, and postconditions — things that should never fail if your code is correct
  • Never use assert for input validation, external conditions, or error handling — these need if/raise/try/catch
  • Never put side effects inside assert — the entire expression is removed when assertions are disabled
  • Assertions can be disabled in production: Python -O, C/C++ NDEBUG, Java without -ea
  • In testing, assertions are the primary tool — frameworks like pytest enhance assert with detailed failure messages
  • The rule of thumb: if the failure indicates a bug in your code, use assert; if it indicates bad input or environment, use exceptions

Course illustration
Course illustration

All Rights Reserved.