JAVA • CONTROL FLOW

Avoiding Conditional Bugs — Avoid common conditional bugs (ordering, boundary conditions)

Learn to spot and fix the sneaky logic errors that hide inside if-statements, comparisons, and boundary checks.

Historical Context & Motivation

Since the earliest days of programming, conditional bugs have been among the most common and frustrating errors developers face. A conditional bug occurs when your if statements, comparisons, or boundary checks don't behave the way you intended. These bugs are especially dangerous because the code compiles and runs without obvious errors — it just produces the wrong result in certain situations.

The history of software is filled with expensive mistakes caused by subtle conditional errors. From space missions to banking software, a single misplaced comparison operator or an incorrect boundary check has led to catastrophic failures. Understanding where these bugs come from helps you write more reliable code.

1962
Mariner 1 Spacecraft Lost
A missing hyphen in guidance software caused NASA's Mariner 1 rocket to veer off course and be destroyed. A tiny syntax error in a conditional formula cost $18.5 million.
1996
Ariane 5 Explosion
The European Space Agency's Ariane 5 rocket exploded 37 seconds after launch because a 64-bit number was converted to a 16-bit integer — a classic boundary overflow condition that was never properly checked.
1999
Y2K Bug Scare
Programs that stored years using only two digits faced boundary conditions when 1999 rolled over to 2000. Billions of dollars were spent fixing conditional logic that assumed years would always start with '19'.
2014
Heartbleed Vulnerability
A missing bounds check in OpenSSL allowed attackers to read server memory. This boundary condition bug affected millions of websites and demonstrated how a single unchecked condition can compromise global security.

These real-world disasters share a common thread: code that didn't correctly handle edge cases and boundary conditions. As you learn Java's control flow, understanding where conditional bugs hide and how to prevent them is one of the most valuable skills you can develop.

Core Principles of Conditional Bugs

Conditional bugs generally fall into a few well-known categories. Once you learn to recognize these patterns, you can catch errors before they ever cause problems. Let's explore the foundational categories of mistakes that programmers make when writing conditional logic in Java.

1

Off-by-One Errors

Using < when you meant <=, or vice versa. This causes your loop or condition to include one too many or one too few values — the most common boundary bug.
2

Wrong Operator Bugs

Accidentally using = (assignment) instead of == (comparison), or confusing && with ||. These change the entire meaning of a conditional.
3

Order-of-Evaluation Bugs

Placing conditions in the wrong order so that an earlier branch catches cases that should be handled by a later, more specific branch. The order of your if / else if chain matters.
4

Equality vs. Reference Bugs

In Java, using == to compare objects (like Strings) checks reference identity, not value equality. You should use .equals() instead.
5

Missing Edge Cases

Forgetting to handle special values like null, empty strings, negative numbers, or zero. These edge cases crash programs or produce unexpected output.
✦ KEY TAKEAWAY
Think of conditionals like sorting mail into mailboxes. If you label one box 'letters A–M' and another 'letters M–Z', every letter starting with M gets put in the first box and the second box never receives M letters. That's an ordering bug — the overlap at the boundary causes incorrect behavior. Always check: where do my boundaries land, and is there overlap or a gap?

Visualizing Conditional Bug Patterns

Let's visualize the most common conditional bugs using a flowchart. The diagram below shows how a grading program can produce wrong results depending on the order of conditions and the boundary operators used. Pay special attention to where scores of exactly 90 or 80 end up in each version.

The buggy version checks the broadest condition first (score >= 60), so a score of 90 matches that branch and never reaches the >= 90 check. The correct version checks the most restrictive condition first.

The key insight here is that in an if / else if chain, Java evaluates each condition from top to bottom and takes the first branch that is true. Once a branch is entered, all remaining else if and else blocks are skipped entirely. If your broadest condition appears first, it acts like a catch-all net that grabs cases meant for later, more specific branches.

How Conditional Bugs Work Under the Hood

Bug Type 1: Off-by-One Boundary Errors

An off-by-one error (sometimes called OBOE) happens when you use < instead of <=, or start counting at 1 instead of 0. In Java, arrays are zero-indexed, meaning the first element is at index 0 and the last element of an array with n elements is at index n − 1. Using <= arr.length instead of < arr.length will cause an ArrayIndexOutOfBoundsException.

ARRAY BOUNDS RULE
Valid indices: 0 ≤ i ≤ (length − 1)
For an array of length n, the valid indices range from 0 to n − 1. Always use i < arr.length in your loop conditions, never i <= arr.length.

Bug Type 2: Assignment Instead of Comparison

In many languages, writing if (x = 5) would silently assign 5 to x and always evaluate as true. Java is stricter — this only compiles if x is a boolean. However, the bug if (flag = true) versus if (flag == true) compiles without error in Java and always evaluates to true because the assignment returns the assigned value.

Bug Type 3: String Comparison with ==

In Java, the == operator checks whether two object references point to the same memory location, not whether they contain the same characters. The .equals() method checks the actual content. For example, new String("hello") == new String("hello") returns false because they are two separate objects, even though they contain the same text.

OBJECT COMPARISON RULE
== compares references | .equals() compares values
For primitive types (int, double, boolean), use ==. For objects (String, arrays, custom classes), use .equals().

Bug Type 4: Short-Circuit Evaluation Traps

Java uses short-circuit evaluation for && and ||. With &&, if the left side is false, the right side is never evaluated. With ||, if the left side is true, the right side is skipped. This can be helpful — for example, str != null && str.length() > 0 safely avoids a NullPointerException because the second part is only checked when str is not null. But reversing the order would cause a crash.

Detailed Bug Catalog with Examples

Now let's look at a comprehensive catalog of conditional bugs, showing the buggy code alongside the corrected version. This reference will help you quickly identify and fix common mistakes in your own programs.

Four common conditional bug patterns shown with their fixes. Notice how each bug involves a subtle mistake — a wrong operator, a wrong order, or missing braces — that can completely change program behavior.
Summary of common conditional bug types and their fixes
Bug TypeWhat Goes WrongHow to Prevent It
Off-by-OneLoop runs one too many or one too few timesUse < length for arrays; trace boundary values mentally
Condition OrderingBroad condition catches specific cases firstOrder from most restrictive to least restrictive
String ==Compares memory addresses, not string contentAlways use .equals() for Strings and objects
Null OrderingCalling methods on null causes NullPointerExceptionCheck != null before using the object
Dangling Elseelse pairs with inner if, not outer if as intendedAlways use curly braces { } to make scope explicit
Floating-Point ==Rounding errors make exact comparison unreliableCompare with a tolerance: Math.abs(a - b) < 0.0001

Worked Example: Debugging a Ticket Pricing Program

Suppose you're writing a program that calculates ticket prices for a movie theater. Children (under 12) pay $8, seniors (65 and over) pay $9, and everyone else pays $12. The original code has several bugs. Let's identify and fix them step by step.

Finding and Fixing Conditional Bugs in Ticket Pricing
1
Step 1 — Identify the Buggy CodeHere is the original buggy code: public static double getPrice(int age, String membership) { if (age < 13) // Bug 1: should be < 12 return 8.0; else if (age > 65) // Bug 2: should be >= 65 return 9.0; else if (membership == "gold") // Bug 3: should use .equals() return 10.0; else return 12.0; } This code has three distinct bugs. Let's trace through each one.
2
Step 2 — Fix Bug 1: Off-by-One in Child PricingThe requirement says children under 12 pay $8. The condition age < 13 includes 12-year-olds, who should pay the full adult price. We need to change it to age < 12. This is a classic off-by-one boundary error.
Fix: Change age < 13 to age < 12
3
Step 3 — Fix Bug 2: Boundary Condition for SeniorsThe requirement says seniors 65 and over get the discount. The condition age > 65 excludes people who are exactly 65. A 65-year-old would pay $12 instead of $9. We need age >= 65 to include the boundary value.
Fix: Change age > 65 to age >= 65
4
Step 4 — Fix Bug 3: String ComparisonThe code uses membership == "gold" to check for gold membership. In Java, == checks whether the two String references point to the same object in memory, not whether they contain the same text. If membership was created with new String("gold") or read from user input, the comparison would return false even though the text matches. We also should guard against null.
Fix: Change to "gold".equals(membership) — putting the literal first avoids NullPointerException
5
Step 5 — Write the Corrected CodeHere is the fully corrected version: public static double getPrice(int age, String membership) { if (age < 12) return 8.0; else if (age >= 65) return 9.0; else if ("gold".equals(membership)) return 10.0; else return 12.0; } Notice the defensive technique of writing "gold".equals(membership) instead of membership.equals("gold"). This way, if membership is null, the call is still safe because the String literal can never be null.
All three bugs fixed: correct boundary, correct operator, and safe String comparison.

Defensive Strategies Compared

There are several defensive coding strategies you can adopt to reduce conditional bugs. Some are simple habits, while others require more discipline. The table below compares these strategies in terms of how effective they are and how easy they are to apply.

Comparison of defensive coding strategies for conditional bugs
StrategyPreventsDifficulty
Always use curly braces { }Dangling else, accidental multi-line bodyEasy — just a habit to build
Test boundary values firstOff-by-one errors at edgesEasy — plug in edge values mentally
Put literals first in .equals()NullPointerException on String comparisonEasy — reverse the call order
Order conditions strictest-firstBroad branches catching specific casesMedium — requires thinking about overlap
Use epsilon comparison for doublesFloating-point rounding comparison failuresMedium — choose appropriate epsilon
Write unit tests for edge casesAll types — catches bugs before deploymentHarder — requires learning test frameworks
✦ KEY TAKEAWAY
Think of testing boundary values like checking the edges of a fence you've built. If you only walk along the middle, the fence looks fine. But if you push on each fence post — especially the ones at the corners — you'll find which ones are loose. In programming, the corner cases (minimum value, maximum value, zero, null, empty string) are where bugs hide. Always test those first.

Connection to Advanced Techniques

The conditional bug patterns you've learned here form the foundation for more advanced software quality practices. As you progress in computer science, you'll encounter formal methods for preventing and detecting these bugs. Let's see how these basic ideas connect to more advanced techniques.

How basic conditional bug prevention connects to advanced CS techniques
What You Know NowAdvanced TechniqueHow They Connect
Testing boundary values by handBoundary Value Analysis (BVA)BVA is a formal testing method that systematically tests the exact boundary, one below, and one above for every condition
Checking conditions are in correct orderDecision Tables & MC/DC CoverageProfessional testers use decision tables to map every possible combination of conditions and ensure full coverage
Null checks and type safetyOptional Types & Pattern MatchingModern Java (17+) introduces Optional, sealed classes, and pattern matching to eliminate null bugs at compile time
Writing corrected conditionsDesign by ContractPreconditions, postconditions, and invariants formally specify what conditions must hold at each point in the code

For now, focus on building the habit of checking your conditionals carefully. As you advance through AP Computer Science and into college-level courses, you'll learn to apply these techniques more formally. The instinct you build now — to always ask "What happens at the boundary?" — will serve you throughout your programming career.

💡 Pro Tip
When you write any if statement, immediately ask yourself three questions: (1) What value makes this condition just barely true? (2) What value makes it just barely false? (3) What happens when the input is null, zero, negative, or empty? If your code handles all three scenarios correctly, it's likely bug-free.

Practice Problems

PROBLEM 1 — CONCEPTUAL
Explain why the order of conditions matters in an if / else if chain. What happens if you put a broad condition like score >= 50 before a more specific condition like score >= 90?
PROBLEM 2 — BASIC CALCULATION
Consider the following code. What output does it produce when x = 10? int x = 10; if (x > 5) System.out.println("A"); System.out.println("B"); System.out.println("C"); Is there a bug? If so, what is it and how would you fix it?
PROBLEM 3 — INTERMEDIATE
The following method is supposed to return "child" for ages 0–12, "teen" for ages 13–17, "adult" for ages 18–64, and "senior" for ages 65+. Find and fix all the bugs: public static String category(int age) { if (age <= 12) return "child"; else if (age < 17) return "teen"; else if (age < 65) return "adult"; else return "senior"; }
PROBLEM 4 — APPLIED
You're writing a login system. The method below checks if a user can log in. It has two bugs — one that could crash the program and one that uses the wrong comparison. Find and fix both: public static boolean canLogin(String username, String password) { if (username.length() > 0 && username != null) { if (password == "secret123") { return true; } } return false; }
PROBLEM 5 — CRITICAL THINKING
Consider a method that checks if a temperature reading is within a 'safe zone' between 36.0°C and 37.5°C (inclusive on both ends). A student writes: public static boolean isSafe(double temp) { return (temp >= 36.0 && temp <= 37.5); } Is this code correct? Discuss potential issues with comparing double values at exact boundaries. How would you make this method more robust? Also explain what would happen if the student accidentally wrote || instead of &&.

Lesson Summary

Conditional bugs are among the most common errors in Java programming, and learning to avoid them is essential for writing reliable software. The main categories include off-by-one errors (using < versus <=), condition ordering bugs (placing broad conditions before specific ones), String comparison errors (using == instead of .equals()), null check ordering (checking null before calling methods), and dangling else bugs (mismatched braces causing wrong scope).

The best defense is to build strong habits: always use curly braces for clarity, order conditions from most restrictive to least restrictive, test boundary values (the exact values where conditions switch from true to false), and put String literals first in .equals() calls. Every time you write a conditional, ask: what happens at the boundary? What if the input is null, zero, or empty? These questions catch bugs before they ever run.

Varsity Tutors • Java • Avoiding Conditional Bugs