All subjects · Computer Science · WCP02 Practical Programming and Problem-solving

Controlling behaviour: boundaries, guard clauses and safe conversion

Edexcel IAL Computer Science · WCP02 Practical Programming and Problem-solving

Code should behave correctly at the edges of its data and fail safely when given bad data. The specification lists six techniques.

Practise this in the app: instant marking and a fix for every mistake. Free, no email needed to start.

Practise this lesson

Boundary value analysis (spec 9.2.3)

Errors cluster at the edges of valid ranges. For a valid range of 10.0 to 51.9 (as in the sample paper's sensor readings), test the boundaries and the values just outside them: 9.9, 10.0, 51.9, 52.0. Check that the code uses < or <= exactly as the specification requires.

LOW = 10.0
HIGH = 51.9
for reading in [9.9, 10.0, 51.9, 52.0]:
    if reading < LOW or reading > HIGH:
        print(reading, "invalid")
    else:
        print(reading, "valid")

Output:

9.9 invalid
10.0 valid
51.9 valid
52.0 invalid

Guard clauses and fewer nested conditionals

A guard clause deals with bad cases first and returns early, so the main logic is not buried in nested ifs.

def ticket_price(age):
    if age < 0:
        return None
    if age < 5:
        return 0
    if age < 16:
        return 4
    return 8

print(ticket_price(-1), ticket_price(3), ticket_price(12), ticket_price(40))

Output:

None 0 4 8

A nested version (if age >= 0: if age >= 5: if age >= 16: ...) gives the same results but is harder to follow. Combining conditions with and/or also flattens nesting.

Managed type conversion and exception handling

Convert input inside try/except, so bad data is handled instead of crashing, and convert once, at the point of input:

def to_int(text, default):
    try:
        return int(text)
    except ValueError:
        return default

print(to_int("12", 0), to_int("12.5", 0), to_int("", -1))

Output:

12 0 -1

Minimising loops

Do each job in as few passes as possible: combine work into one loop, stop a search when the item is found, and do not loop just to copy a list that is never changed.

Worked example

A valid exam mark is a whole number from 0 to 80. Give boundary test data and the expected result of each.

  1. Lower boundary: -1 (reject), 0 (accept)
  2. Upper boundary: 80 (accept), 81 (reject)
  3. Plus normal data such as 45 (accept) and erroneous data such as "forty" or 45.5 (reject)

Answer: -1 reject, 0 accept, 80 accept, 81 reject

Key idea: Test both sides of every boundary; use guard clauses to return early instead of deep nesting; convert types inside try/except at the point of input; avoid unnecessary loops.

Check yourself

  1. What is displayed when this code runs? ```python def classify(n): if n < 0: return "negative" if n == 0: return "zero" if n < 10: return "small" return "large" print(classify(0), classify(9), classify(10)) ```
    Show answer

    zero small large. Each guard returns as soon as it applies: 0 is zero, 9 is small and 10 falls through to large: zero small large.

  2. A valid temperature is from -20 to 50 inclusive, in whole degrees. How many of these test values should be accepted: -21, -20, 0, 50, 51?
    Show answer

    3. -20, 0 and 50 are accepted; -21 and 51 are just outside the boundaries.

  3. What is displayed when this code runs? ```python entries = ["7", "x", "3", "2.5"] total = 0 rejected = 0 for e in entries: try: total = total + int(e) except ValueError: rejected = rejected + 1 print(total, rejected) ```
    Show answer

    10 2. "x" and "2.5" cannot be converted by int(), so they are counted as rejected: the total is 7 + 3 = 10 and 2 are rejected: 10 2.

  4. Valid readings are 10.0 to 51.9 inclusive. This code should display True True False but does not. Which line number contains the error? ``` 1 def is_valid_reading(r): 2 return r > 10.0 and r <= 51.9 3 4 print(is_valid_reading(10.0), is_valid_reading(51.9), is_valid_reading(52.0)) ```
    Show answer

    2. The valid range is 10.0 to 51.9 inclusive, but line 2 uses > 10.0, which rejects the boundary value 10.0. It should be return r >= 10.0 and r <= 51.9.

  5. What is the main benefit of guard clauses?
    • The program uses less memory
    • Invalid cases are handled first, so the main logic is not deeply nested
    • They remove the need for testing
    • They make every function return the same type
    Show answer

    Invalid cases are handled first, so the main logic is not deeply nested. Returning early for special cases keeps the remaining code flat and readable.

  6. Where is the best place to convert typed input into a number?
    • Every time the value is used in a calculation
    • Only when it is printed
    • At the end of the program
    • Once, straight after input, inside try/except
    Show answer

    Once, straight after input, inside try/except. Converting once at input, with exception handling, means the rest of the program can rely on the type.

Practise with instant feedback

Sources

Last updated 8 November 2026. Spotted a mistake? Tell us.