Worked Examples
From requirement to functions
Requirement: record this run's session durations and report totals across runs.
For existing history [25, 15] and new text "20", loading returns two sessions, parsing returns [20], concatenation gives [25, 15, 20], and the summary is count 3, total 60, longest 25. Only after loading and parsing succeed should saving occur. Printing belongs after calculation.
For new text "10,", splitting gives "10" and "". Converting the empty field fails. Do not filter empty fields away: doing so silently changes the input. The saved history must remain unchanged.
Trace before repair
Suppose a summary returns total 20 for [25, 15, 20]. A trace showing totals 25, 15, 20 suggests replacement rather than accumulation. Check the update statement. Changing the expected test result to 20 would hide the defect.
Distinguish absence and corruption
First run: opening a missing history raises FileNotFoundError, for which the contract chooses []. Later run: history contains "broken", so JSON parsing fails. The contract requires reporting that error. Returning [] in both cases discards the evidence needed to recover the file.
Build a calculation in three small steps
Before the full tracker, solve a smaller problem: “Given already validated positive integer study durations, count how many are at least a target.” Assume the target is a nonnegative integer. Keep the input unchanged. This example is a learning scaffold, not the independent assessment.
Step 1: one decision
For a target of 20, duration 19 does not qualify, 20 qualifies and 21 qualifies. This establishes that equality belongs on the accepted side. Write minutes >= target, not minutes > target.
Step 2: repeat the decision
sessions = [10, 20, 30, 20]
target = 20
count = 0
for minutes in sessions:
if minutes >= target:
count += 1
print(count)
Predict the count after each item: 0, 1, 2, 3. The count changes by one, not by the duration: we are counting sessions, not summing qualifying minutes. Initializing at zero also handles an empty input. Equal durations still represent separate sessions.
Step 3: package the calculation
def count_at_least(sessions, target):
count = 0
for minutes in sessions:
if minutes >= target:
count += 1
return count
assert count_at_least([10, 20, 30, 20], 20) == 3
assert count_at_least([], 20) == 0
assert count_at_least([19, 20, 21], 20) == 2
The caller supplies the list and target. Return only after the loop has considered all items. Returning inside the loop would stop too soon. No keyboard or file is involved, so the same function can be checked without an interactive session.
Now close the example. Recreate it, then change the requirement to count durations strictly below a target. Predict the new answers before editing. If you can only reproduce the code while looking at it, revisit the condition and state trace before starting the full tracker.
A bug that does not need an exception
Suppose the count is 70 for [10, 20, 30, 20] at target 20. A program may have used count += minutes: the qualifying durations sum to 70. There is no syntax or type error. The implementation solves a different problem. A test with a qualifying duration of 1 might accidentally conceal that difference, so choose a case that distinguishes counting from summing.