Ship it or send it back
The whole level as one repeatable pass: six questions in order, a verdict, and a comment specific enough to act on.
A pass you can run when you are tired
Everything in this level is a technique. This lesson is the order you apply them in, so that reviewing a hundred-line diff at the end of a long day still produces the same result as reviewing it fresh.
Six questions. In this order, because each one tells you what to look for in the next.
1. What is the contract?
Say it in one sentence, including what happens for the awkward inputs, before you read the body. If you cannot, stop here: that is the review comment, and it is the most valuable one you will write today. Code that does not have a statable contract cannot be verified by anybody, including its author in six months.
2. What are the boundaries?
The body tells you. Any division, any index, any comparison against a threshold, any accumulator seeded with a literal, any assumption that a collection is non-empty. Write down the three inputs you would need in order to be sure, and check whether the tests contain them. Usually they do not.
3. What happens on the error paths?
Every try, every except, every external call, every parse. Is the exception narrowed to the one that is expected? Is a failure visible in the value that comes back, or does it vanish? What does the caller see when the dependency is down?
4. What does it mutate?
Arguments, module-level state, files, rows, anything outside the function. A function that sorts its argument in place is a different contract from one that returns a sorted copy, and the difference is invisible in the return value.
5. What will it cost at real size?
Membership tests against lists, front-of-list removals, string concatenation in loops, sorting inside loops. Correct output at the wrong cost is the one defect no test result will show you.
6. What does it depend on, and does it exist?
Imports, methods, keyword arguments, version-specific behavior. Loud failures are cheap. The dangerous case is a real API used with the wrong meaning.
Turning findings into a verdict
Not everything you notice should block a merge. Sorting your findings is part of the job, and doing it badly is how reviews become slow without becoming better.
Block when the code is wrong for an input that will occur, when a failure is silent, when data can be corrupted, or when the cost is wrong at the size the system actually runs at.
Comment without blocking when the finding is a readability preference, a smaller improvement, or a question you would like answered but whose answer will not change the merge.
Say nothing about style a formatter already owns.
One rule makes the difference between a review that gets acted on and one that gets a thumbs up and no change: every blocking comment names an input. Not "this might break on edge cases". Instead: "page_slice(rows, 1, 3) returns rows 4 to 6, and page 1 should be rows 1 to 3."
Sort each review finding by whether it should block the merge.
Writing the comment
A comment that gets acted on has three parts: the line, the input, and the consequence. Anything shorter starts a discussion instead of a fix.
Line 6:
seenis a list, son in seenscans it. On the 200k-row nightly import that is about twenty billion comparisons and the job will not finish in its window. Makingseena set is a one-word change.
Line 12: this returns
0when there are no ratings, and the caller renders it as the average. A listing with no reviews will display "0.0 stars" rather than "no reviews yet". Should this returnNoneinstead?
Notice the second one is a question. Plenty of findings are genuinely questions about intent, and asking is faster and more accurate than guessing at what the author meant.
What this level was actually about
The market changed the question. Writing a small function that passes hidden tests is the task that is now automated end to end, and the interviews that used to test it have moved on to something else: here is code you did not write, tell me what is wrong with it and prove it.
Every technique in this level is one answer to that question.
- Recover the contract, trace the boundary before you run anything.
- Assume the example passes, because whatever produced the code was fitted to it.
- Know the failure shapes on sight, so a suspicion becomes a specific input.
- Write the assertion that fails now, because that is the only durable artifact.
- Use properties and a slow oracle when examples run out.
- Shrink the failing input until it explains itself.
- Repair to the class the contract names, and do not rewrite.
- Read for cost, which no test result will show you.
- Wrap unreliable dependencies, validate what they return, verify the APIs are real.
None of this is anti-AI. Generated code is a draft from a fast collaborator who does not know your codebase, has not read your contract, and will not be on call when it fails. Judging that draft accurately, and proving your judgement with a test, is the part of the job that got more valuable, not less.
Interview nuance: if an interviewer hands you a function and asks what you think, the pass in this lesson is your answer, spoken out loud. Contract, boundaries, error paths, mutation, cost, dependencies. You will find something almost every time, because the code was chosen to have something in it, and you will find it in a visibly repeatable way rather than by luck.
def weekly_summary(entries):
total = 0
for day, minutes in entries:
total += minutes
return {"total": total, "average": round(total / len(entries), 1)}
# Question 1: what is the contract when a day was never logged?
# Question 2: what happens on an empty list?
print(weekly_summary([["mon", 30], ["tue", 45]]))Apply
Your turn
The task this lesson builds to.
Write a function check(name) that returns True only when the candidate stored under
name is a correct apply_refund, and False otherwise.
The contract: apply_refund(order, amount) refunds amount cents against an order dictionary
holding "total" and "refunded". The amount is capped so "refunded" never exceeds "total".
An amount of zero or less refunds nothing. The function returns a new dictionary and does not
modify the one it was given.
The starter holds five candidates. Two are correct, and the three that are not each fail a different
question from the review pass: one mutates its argument, one skips the cap, and one lets a negative
amount run backwards. Keep check as the last function in the file.
3 hints and 5 automated checks are waiting in the workspace.
Practice
Make it stick
A second problem on the same idea, so it survives past today.
Your habit-tracking product emails every user a weekly summary on Monday morning, and an
assistant wrote the weekly_summary function in the starter. It was tested on one full week of
data, which is the only shape it handles. Users who missed a day, users who logged nothing all week,
and users who signed up on Sunday are all currently getting either a crash or a wrong number.
Repair weekly_summary so it satisfies its contract.
The contract: entries is a list of [day, minutes] pairs, where minutes is a whole number or
None for a day that was never logged. Return a dictionary with "days_logged" (how many entries
have a real number), "total" (the sum of those minutes), "best_day" (the day with the most
minutes, with ties going to the one earliest in the list, and None when nothing was logged), and
"average" (the total divided by "days_logged", rounded to one decimal place, or 0.0 when
nothing was logged).
A logged day of zero minutes still counts as logged.
3 hints and 5 automated checks are waiting in the workspace.