← First principles STAGE 01 · 22 MIN

Decisions & repetition

Turn requirements into conditions and loops whose stopping rules you can explain.

Open in the laboratory ↗

BY THE END, YOU CAN

  • Translate requirements into Boolean conditions
  • Trace a loop without guessing
  • Define a stopping condition and an invariant
A loop with a purpose: each pass removes one job, so the loop must eventually stop.

Diagram description: Start → Jobs left?; Jobs left? → Take one, process it (yes); Take one, process it → Jobs left? (one fewer); Jobs left? → Stop (no); done + pending == total: true after every pass

Make a decision explicit

A requirement such as 'retry failed jobs fewer than three times' hides two conditions.

A job must have failed, and its attempt count must be below the limit. Write both. Python uses indentation to group statements, so formatting changes meaning.

Test boundary values such as zero, two, and three attempts. A boundary that is wrong by exactly one, such as <= where < was meant, is called an off-by-one error. Pay attention to whether a counter means attempts already made or attempts remaining; either convention can work, but mixing them creates errors that look random to a user.

Python
status = "failed"
attempts = 2
should_retry = status == "failed" and attempts < 3
if should_retry:
    print("Schedule another attempt")

Repeat with a purpose

A for loop processes items from an iterable, such as a list of jobs.

A while loop repeats while a condition remains true. Choose the form that makes the stopping rule easiest to see. If a while loop removes one pending job on every pass, the number of pending jobs decreases toward zero.

If it adds more work than it removes, termination needs a different argument. Production workers (long-running processes that take jobs from a queue) often run indefinitely, but still need explicit cancellation and shutdown behavior.

Keep one fact true

An invariant is a statement that remains true at a chosen boundary in the algorithm.

For a simple batch worker, it might be 'processed plus remaining equals the original batch size.' Check that it holds before the loop and after each iteration. This gives you a way to reason about a program larger than a few lines. Do not delete items from a list while iterating over that same list unless you deliberately handle the changed indices.

Building a result list is often clearer.

Python
jobs = ["email", "report", "thumbnail"]
processed = []
for job in jobs:
    processed.append(job.upper())
print(processed)

COMMON FAILURE MODE

Using <= when the requirement is strictly below a limit, or changing the collection you are currently iterating.

TRY IT YOURSELF

Given attempt counts [0, 1, 3, 2, 4], build a list containing only counts eligible for another attempt under a limit of three. Explain why three is excluded.

RETRIEVAL CHECK / APPLY THE IDEA

Make the call in the laboratory.

The two-question checkpoint, your notes, and your saved progress are in the laboratory. Free, with no account.

Open in the laboratory ↗

CONNECT IT TO RELAY

Process a fixed batch of Relay jobs and separate completed jobs from retryable jobs. State an invariant for your loop.