5 min read
CS Fundamentals Index
Tier 1 -- Foundations
Object-Oriented Programming
Tier 2 -- Core Concepts
Tier 3 -- Debugging & Tradeoffs
CS Fundamentals Index
Tier 1 -- Foundations
Object-Oriented Programming
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.
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?
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, andFalseare falsy; everything else is truthy. In Java, onlybooleanis legal in anif— there's no truthiness forintor objects. In C++,0,nullptr, and empty integral values are falsy; anything else is truthy (the language auto-converts tobool). - Assignment vs. comparison.
if (x = 5)in C++ assigns 5 toxand evaluates truthy — almost certainly a bug. Java forbids this for non-booleans (type error). Python doesn't allow=insideifconditions (walrus:=is deliberate and separate). - Hanging else.
if (a) if (b) X; else Y;— does theelsebelong toaorb? In C++/Java it binds to the innermostif(i.e.,b). Always use braces to make this obvious. - Empty branches. An empty
ifbranch (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. NullPointerExceptioninside a condition → you dereferenced before the null check.
A backend developer is reviewing the following Python function and finds it hard to read due to deeply nested conditionals:
Which refactored version best applies the guard clause pattern to flatten this nesting?def process_order(order): if order: if order.is_valid: if order.items: fulfill(order)
4. Language Differences
| Aspect | Python | Java | C++ |
|---|---|---|---|
| Must condition be boolean? | No — truthiness applies | Yes — must be boolean | No — converted to bool |
elif keyword | Yes (elif) | No (else if) | No (else if) |
| Switch/match on strings | match yes | Yes (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 |
| Ternary | a if c else b | c ? a : b | c ? 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.
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?
5. Tradeoffs & Decisions
if/elsechains vs.switch/match. Useswitch/matchwhen dispatching on one value against many discrete cases — the compiler can optimize it to a jump table and the intent is clearer. Useif/elsefor 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 readableif/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 longif/elifand 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."
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?
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, andFalseas falsy; Java requires abooleanin the condition; C++ implicitly converts tobool. - Short-circuit evaluation means
a && bskipsbifais false, anda || bskipsbifais true — useful for null guards. - Guard clauses (early returns) reduce nesting and make preconditions explicit.
switch/matchis for one-value-many-cases dispatch; it compiles to efficient jump tables and is clearer than longif/elifchains.
Follow-ups:
- "What's truthy in Python?" — Everything that isn't
0,None,False, or an empty collection. - "Why is
switchsometimes faster thanif/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.