Conditionals & Branching

5 min read

Reading Progress0%
CS Fundamentals Index
Tier 1 -- Foundations
Tier 2 -- Core Concepts
Tier 3 -- Debugging & Tradeoffs
CS Fundamentals Index
Tier 1 -- Foundations
Tier 2 -- Core Concepts
Tier 3 -- Debugging & Tradeoffs

Conditionals & Branching

1. What Is It?

A conditional is a decision point in code: evaluate an expression, and based on whether it's true or false, run one block or another. Without conditionals, programs are linear recipes — they can't respond to input, handle errors, or take different paths. The entire idea of "logic" in software rests on conditional branching.

The subtle part isn't the syntax — it's what counts as true, how to structure many conditions without creating tangled code, and how the compiler or interpreter actually executes the branch.

QUICK CHECK

A web API receives a request and must return different responses based on whether the user is authenticated, whether the requested resource exists, and whether the user has permission to access it. Which fundamental programming concept makes this kind of multi-path behavior possible?

Choose one answer

2. How It Works

A conditional evaluates an expression to a boolean (or a value the language treats as one), then selects which block to execute. Only the selected block runs; the others are skipped.

# Python
if score >= 90:
    grade = "A"
elif score >= 80:
    grade = "B"
else:
    grade = "C"
// Java
String grade;
if (score >= 90) {
    grade = "A";
} else if (score >= 80) {
    grade = "B";
} else {
    grade = "C";
}
// C++
std::string grade;
if (score >= 90)      grade = "A";
else if (score >= 80) grade = "B";
else                  grade = "C";

Switch/match for discrete values

When you're dispatching on a single value against many specific options, use switch (Java/C++) or match (Python 3.10+). It's more readable and often faster than a chain of else if.

// Java
switch (day) {
    case MONDAY, TUESDAY, WEDNESDAY -> System.out.println("weekday");
    case SATURDAY, SUNDAY -> System.out.println("weekend");
    default -> System.out.println("unknown");
}
# Python 3.10+
match status:
    case 200 | 201:
        return "ok"
    case 404:
        return "not found"
    case _:
        return "other"

Ternary expressions

For a single value chosen between two options, a ternary keeps code tight:

label = "adult" if age >= 18 else "minor"            # Python
String label = age >= 18 ? "adult" : "minor";        // Java
std::string label = age >= 18 ? "adult" : "minor";   // C++

Short-circuit evaluation

&&/and stops evaluating the moment it sees a falsy value. ||/or stops when it sees a truthy one. This is used defensively:

# Python — won't crash even if user is None
if user and user.is_active:
    ...
// Java — same pattern
if (user != null && user.isActive()) { ... }

The order matters. Swap it and you'll get a NullPointerException.

3. What You Actually Need to Know

  • Truthiness varies across languages. In Python, 0, "", [], {}, None, and False are falsy; everything else is truthy. In Java, only boolean is legal in an if — there's no truthiness for int or objects. In C++, 0, nullptr, and empty integral values are falsy; anything else is truthy (the language auto-converts to bool).
  • Assignment vs. comparison. if (x = 5) in C++ assigns 5 to x and evaluates truthy — almost certainly a bug. Java forbids this for non-booleans (type error). Python doesn't allow = inside if conditions (walrus := is deliberate and separate).
  • Hanging else. if (a) if (b) X; else Y; — does the else belong to a or b? In C++/Java it binds to the innermost if (i.e., b). Always use braces to make this obvious.
  • Empty branches. An empty if branch (if (x);) is almost always a bug — a stray semicolon or missing body.
  • Deep nesting. Three-plus levels of nested ifs are a readability smell. Guard clauses — early returns for invalid states — flatten them:
# nested
def pay(user):
    if user:
        if user.active:
            if user.balance > 0:
                charge(user)

# guarded
def pay(user):
    if not user: return
    if not user.active: return
    if user.balance <= 0: return
    charge(user)
  • Debugging clues:
    • Wrong branch taken → print or log the condition's actual value. Most conditional bugs are "I thought this was true but it wasn't."
    • Condition always true/false → check for = vs. ==, operator precedence, or short-circuit ordering.
    • NullPointerException inside a condition → you dereferenced before the null check.
QUICK CHECK

A backend developer is reviewing the following Python function and finds it hard to read due to deeply nested conditionals:

def process_order(order):
    if order:
        if order.is_valid:
            if order.items:
                fulfill(order)
Which refactored version best applies the guard clause pattern to flatten this nesting?

Choose one answer
def process_order(order):
    if order and order.is_valid and order.items:
        fulfill(order)
def process_order(order):
    while order:
        if order.is_valid and order.items:
            fulfill(order)
            break
def process_order(order):
    if not order: return
    if not order.is_valid: return
    if not order.items: return
    fulfill(order)
def process_order(order):
    try:
        if order.is_valid and order.items:
            fulfill(order)
    except AttributeError:
        return

4. Language Differences

AspectPythonJavaC++
Must condition be boolean?No — truthiness appliesYes — must be booleanNo — converted to bool
elif keywordYes (elif)No (else if)No (else if)
Switch/match on stringsmatch yesYes (since Java 7)No — only integral/enum types; use if/else or a hash map
Pattern matching (destructuring)Yes (match with case Point(x, y))switch patterns (Java 21+)std::variant + std::visit; no language-level pattern match
Ternarya if c else bc ? a : bc ? a : b

Python's truthiness is both a convenience and a hazard: if data: is idiomatic for "non-empty," but bites you when data could legitimately be 0 or False and you meant "is not None" — use if data is not None: explicitly.

QUICK CHECK

A backend service stores a configuration flag that can legitimately hold the value 0 (meaning 'disabled') or None (meaning 'not yet configured'). A Python developer writes if config_flag: to check whether the flag has been set. What is the problem with this approach?

Choose one answer

5. Tradeoffs & Decisions

  • if/else chains vs. switch/match. Use switch/match when dispatching on one value against many discrete cases — the compiler can optimize it to a jump table and the intent is clearer. Use if/else for heterogeneous conditions (if age > 18 and country == "US").
  • Guard clauses vs. nested conditions. Guards are better when the function has several preconditions to reject early. Nested conditions are fine when the logic naturally forms a tree with shared tails.
  • Ternary vs. if/else. Ternary is fine for a single short pick. Nested ternaries are almost always worse than a readable if/else.
  • Strategy/dispatch tables vs. long conditionals. If you have dozens of branches all selecting a behavior, a dictionary of handlers ({"GET": handle_get, "POST": handle_post}) is cleaner than a long if/elif and easier to extend without touching working code.

If you see a function with five levels of nested ifs, it usually means there are implicit preconditions — convert them to guard clauses. You'd choose a dispatch table when the conditions are uniformly "match one value against many."

QUICK CHECK

A web server routes incoming HTTP requests by method. Currently it uses a long if/elif chain checking whether the method is GET, POST, PUT, DELETE, PATCH, and so on — and new methods keep being added. Which refactoring best addresses this situation and why?

Choose one answer

6. Interview Cheat Sheet

  • A conditional picks one branch to execute based on a boolean expression; the other branches are skipped.
  • Truthiness rules differ by language. Python treats empty collections, 0, "", None, and False as falsy; Java requires a boolean in the condition; C++ implicitly converts to bool.
  • Short-circuit evaluation means a && b skips b if a is false, and a || b skips b if a is true — useful for null guards.
  • Guard clauses (early returns) reduce nesting and make preconditions explicit.
  • switch/match is for one-value-many-cases dispatch; it compiles to efficient jump tables and is clearer than long if/elif chains.

Follow-ups:

  • "What's truthy in Python?" — Everything that isn't 0, None, False, or an empty collection.
  • "Why is switch sometimes faster than if/else?" — The compiler can emit a jump table or a binary search, avoiding sequential comparisons.
  • "When should you use a ternary?" — For a single, short either/or assignment. Nested ternaries are almost always worse than if/else.
Glossary History

Click dotted jargon to save explanations here.

Glossary History

Click dotted jargon to save explanations here.