Conditionals: Making Decisions

Straight-line code does the same thing every run. Useful programs react: to input, to state, to time. The if statement is how they do it, and it is built directly on the boolean values from the previous lesson. One condition is evaluated, once, at the moment execution arrives, and exactly one path runs.

Think of a bouncer at a door with one rule: if the guest is on the list, open the main door, otherwise point to the exit. The bouncer checks the list at the moment each guest arrives, sends them exactly one way, and never sends anyone both ways. That is the entire semantics of if and else.

The anatomy of a branch

Every conditional has the same three parts: a condition, which is any expression that produces a boolean; a then-branch, which runs when the condition is true; and optionally an else-branch, which runs when it is false. The critical detail is the timing. The condition is read exactly when execution reaches the if, using the values in memory at that instant. Change the state one line earlier and the same if can go the other way.

Code in the branch not taken does not run slowly or partially. It does not run at all. On that run, it might as well not exist. Hold onto that idea: in Level 6 you will meet dead code injection, which plants entire fake branches that can never run, precisely because a reader must do real work to prove which branches are live.

Chains and nesting

When there are more than two outcomes, conditionals chain: if, else if, else if, else. The chain is checked top to bottom and the first true condition wins, so order carries meaning. A grading chain that checks >= 80 before >= 90 will never award the top grade. Conditionals also nest, one decision inside another, which is how programs express "if logged in, then if admin, then...". Deep nesting works but reads badly, which is why experienced programmers flatten it with early exits and combined conditions.

The same fork in eight languages

Syntax varies: then/end in Lua, braces in JavaScript and Go and Java and PHP, indentation in Python, elsif in Ruby. The semantics never vary. One boolean, one chosen path.

Watch a decision actually happen

Step through the program below one statement at a time. Before each step, predict which branch will run. Notice that the lines in the losing branch never light up: execution simply never visits them.

Run it again after changing bonus to 10 and watch a different branch light up with no other change. The condition did not change; the state it read did. When you trace unfamiliar code, real or obfuscated, the question is never "what does this branch do", it is "what is in memory when this condition is read".

Why the shape matters

Read any well-written function and the branching structure is a map of its intent: handle the error early, then the special case, then the main path. That top-to-bottom readable shape is free documentation, exactly like variable names. And like names, it can be destroyed without changing behaviour. Control flow flattening, in Level 6, rewrites every branch into an unreadable dispatch loop that behaves identically. You cannot appreciate what that transform costs an attacker until branching feels this natural to read.

Two expert notes. First, conditions with and/or short-circuit: in a and b, if a is false, b is never evaluated at all, which matters when b has side effects. Second, a compiler sees your if/else chain as a control flow graph, blocks connected by true/false edges, and every shape-level obfuscation is a graph rewrite that preserves the edge semantics. Level 4 makes that graph view concrete.

A conditional consults one boolean at one moment and runs exactly one path. Chains check top to bottom and the first true condition wins. The readable shape this gives code is documentation, and documentation is exactly what obfuscation is allowed to destroy.

Frequently asked questions

What is the difference between else if and a new if statement?

An else-if only runs its check when every condition above it was false, so at most one branch of the whole chain executes. Separate if statements are independent: several of them can all fire on the same run. Mixing the two up is a classic source of double-execution bugs.

Do all languages have a switch statement too?

Most do, and it expresses the same idea as an else-if chain over one value. It matters later in this course for an unexpected reason: control flow flattening compiles readable branching into one giant switch inside a loop, which is legal precisely because switch and if are interchangeable shapes for the same decisions.

Keep learning

  • Strings, Numbers, and Booleans
  • Loops: Repeating Work
  • Control Flow Flattening: Turning Structure into a State Machine
  • How Code Executes, Step by Step
  • Lua Obfuscator
  • JavaScript Obfuscator
  • Python Obfuscator

All lessons