Conditions and Decisions: How Programs Choose What to Do
📑 On this page
- Boolean values
- If and else
- Comparison boundaries
- Logical AND
- Logical OR
- Logical NOT
- Operator precedence
- Short-circuit evaluation
- Truthiness
- A concrete checkout decision
- Switch and pattern matching
- Decision tables
- Floating-point comparisons
- Common misunderstandings
- "An if statement checks intent"
- "AND and OR can be read casually"
- "Truthy means valid"
- "Client conditions protect server actions"
- Knowledge check
- The one idea to remember
Programs must respond differently to different data:
- Accept or reject login
- Apply or skip discount
- Show mobile or desktop layout
- Retry or report failure
A condition evaluates an expression to true or false, and control flow uses that result to select instructions.
Reliable decisions require clear rules, correct operators, and attention to edge cases.
Boolean values
A Boolean has two values:
true
falseComparisons produce Booleans:
age >= 18
passwordLength >= 12
status === "active"The expression is evaluated using current values.
Names such as isActive, hasPermission, and canRetry make Boolean meaning clear.
If and else
if (temperature > 38) {
showHighTemperatureWarning();
} else {
showNormalStatus();
}Only one branch executes.
Additional branches:
if (score >= 90) {
grade = "A";
} else if (score >= 80) {
grade = "B";
} else {
grade = "C";
}Order matters. The first true branch wins.
Comparison boundaries
Small operator differences change behavior:
age > 18
age >= 18Ask:
- Is the boundary included?
- What happens exactly at zero?
- Are units consistent?
- Can the value be absent?
Boundary testing checks values:
- Just below
- Exactly equal
- Just above
Many bugs live at the edge rather than the ordinary middle.
Logical AND
AND requires both conditions:
| A | B | A AND B |
|---|---|---|
| false | false | false |
| false | true | false |
| true | false | false |
| true | true | true |
Example:
const canCheckout = cartHasItems && addressIsValid;Both requirements must hold.
Logical OR
OR requires at least one condition:
| A | B | A OR B |
|---|---|---|
| false | false | false |
| false | true | true |
| true | false | true |
| true | true | true |
const canEdit = isOwner || isAdministrator;Security rules need precise grouping when AND and OR combine.
Logical NOT
NOT reverses a Boolean:
if (!isLoggedIn) {
showLogin();
}Double negatives are hard to read:
if (!isNotDisabled) { ... }Prefer positive names where possible.
Operator precedence
Expressions have precedence rules:
isOwner || isAdmin && accountActiveAND commonly binds more tightly than OR, so this means:
isOwner || (isAdmin && accountActive)If the intended policy is:
(isOwner || isAdmin) && accountActiveparentheses are required.
For security-sensitive conditions, explicit grouping is worth the extra characters.
Short-circuit evaluation
Logical operators often stop once the result is known.
For AND:
user !== null && user.isActiveIf user !== null is false, the second expression is not evaluated.
For OR:
cachedValue || loadValue()The second expression runs only if the first is falsy.
Short-circuiting enables safe guards but can hide side effects. Avoid relying on it for unrelated state changes.
Truthiness
Some languages convert non-Boolean values in conditions.
JavaScript treats values such as these as falsy:
false, 0, "", null, undefined, NaNThis can be convenient:
if (name) { ... }But it merges missing name and empty name.
Use explicit checks when those states mean different things.
A concrete checkout decision
function canPlaceOrder(cart, customer) {
if (cart.items.length === 0) return false;
if (!customer.deliveryAddress) return false;
if (!customer.paymentMethod) return false;
return cart.totalInPaise > 0;
}These early returns are guard clauses.
They handle invalid conditions first and keep the successful path clear.
The server must repeat these checks even if the client disables the checkout button.
Switch and pattern matching
When choosing among several distinct values:
switch (status) {
case "pending":
return showPending();
case "paid":
return showReceipt();
case "cancelled":
return showCancelled();
default:
return showUnknown();
}Pattern-matching features in some languages can inspect structured data and require exhaustive cases.
An exhaustive compiler check helps when a new state is added.
Decision tables
Complex business rules can be represented as a table.
| Member | Coupon valid | Cart minimum met | Discount |
|---|---|---|---|
| yes | yes | yes | 20% |
| yes | no | yes | 10% |
| no | yes | yes | 5% |
| any | any | no | 0% |
A decision table exposes missing and overlapping cases better than nested conditions.
Tests can be generated for each row.
Floating-point comparisons
Direct equality with computed floating-point values can fail because of rounding:
0.1 + 0.2 === 0.3 // falseCompare within a tolerance when appropriate:
Math.abs(actual - expected) < epsilonThe tolerance depends on the domain and scale.
For exact values such as money, choose a suitable representation instead.
Common misunderstandings
"An if statement checks intent"
It evaluates the exact expression written, including mistakes and missing cases.
"AND and OR can be read casually"
Grouping and precedence can change authorization and business behavior.
"Truthy means valid"
A non-empty value can still violate format or domain rules.
"Client conditions protect server actions"
Clients can be modified. The authoritative system must validate independently.
Knowledge check
1. What does AND require?
Every combined condition must be true.
2. Why test values at a condition boundary?
Inclusive versus exclusive operators and off-by-one mistakes often fail exactly at the edge.
3. What is short-circuit evaluation?
The language stops evaluating a logical expression once the final result is already determined.
4. Why use guard clauses?
They handle invalid or exceptional cases early and reduce deeply nested control flow.
The one idea to remember
Conditions turn values into branches by evaluating precise Boolean rules.
Correct decisions depend on boundaries, grouping, absence handling, explicit policy, and tests for every meaningful case.
Next, we will use loops to express controlled repetition over ranges and collections.