Questions for Every Change
Five questions find most of what a diff hides. What behaviour changed? Who else depends on what changed? What was removed? What is new that nobody asked for? What should be there and is not?
They work on any diff from any author, which is why Ines asks them of every change on the team, yours included. On the refund diff they find five problems that the passing suite did not.
What Behaviour Changed
State the before and the after as rules a member could notice, not as lines of code. "Before, a cancellation exactly 24 hours ahead was refunded. After, one exactly 12 hours ahead is not." Every changed comparison and every changed constant gets a sentence like that, written down.
The sentence is the point. In code, the boundary flip in the refund diff is a single comparison, a character or a word different, inside a block the agent re-indented. In a sentence it is a member at the front desk who cancelled at exactly the time the cancellation screen promised and got nothing back. Writing the rule out is what turns the comparison into the member. Do the same for the rounding in the class-share function: before, one class of a three-class pack bought for $50.00 refunded $16.66; after, it refunds $16.67, on every pack refund from the day this ships.
Written out for the whole refund diff, the list is short and sobering. Before, a cancellation exactly 24 hours ahead was refunded; after, one exactly 12 hours ahead is not. Before, a member at a gym in the second zone was told and paid on their own clock; after, on the server's, an hour early. Before, a pack's share rounded down; after, to the nearest cent. Before, every gym had the same window by construction; after, any gym can be given its own. Of all of that, only the move from 24 hours to 12 was asked for.
Who Else Depends on It
Search the whole repository for every caller of a changed function and every use of a changed value, templates and documentation included. The agent changed what it read, and it read what the task pointed it at (Chapter 4). The search covers what it did not read.
$ git grep -n -w -e 24 -e refund_deadline
src/cragmoor/booking.py:22: until = refunds.refund_deadline(booking.starts_at, policy)
src/cragmoor/refunds.py:19:def refund_deadline(starts_at: datetime, policy: Policy) -> datetime:
src/cragmoor/refunds.py:32: deadline = refund_deadline(booking.starts_at, policy)
templates/cancellation_email.txt:5:You can cancel any class up to 24 hours before it starts for a full refund.
templates/schedule_page.html:6: <p class="policy">Cancel up to 24 hours before a class for a full refund.</p>$ git grep -n -w -e 24 -e refundDeadline
src/booking.ts:17: const until = refunds.refundDeadline(booking.startsAt, policy);
src/refunds.ts:13:export function refundDeadline(startsAt: string, policy: Policy): Date {
src/refunds.ts:25: const deadline = refundDeadline(booking.startsAt, policy);
templates/cancellation_email.txt:5:You can cancel any class up to 24 hours before it starts for a full refund.
templates/schedule_page.html:6: <p class="policy">Cancel up to 24 hours before a class for a full refund.</p>$ git grep -n -w -e 24 -e refundDeadline
src/main/java/org/cragmoor/Booking.java:14: ZonedDateTime until = Refunds.refundDeadline(startsAt, policy);
src/main/java/org/cragmoor/Refunds.java:16: public static ZonedDateTime refundDeadline(LocalDateTime startsAt, Policy policy) {
src/main/java/org/cragmoor/Refunds.java:28: ZonedDateTime deadline = refundDeadline(booking.startsAt(), policy);
templates/cancellation_email.txt:5:You can cancel any class up to 24 hours before it starts for a full refund.
templates/schedule_page.html:6: <p class="policy">Cancel up to 24 hours before a class for a full refund.</p>The function has the callers you expected, in refunds and in booking. The old number turns up in two places no line of the diff touches: the cancellation email and the schedule page, both still telling members they can cancel up to 24 hours before a class for a full refund. A search for the function's name would never have found them, because they say "24 hours" in words.
What Was Removed
Every deleted line answers the question "what stopped happening?" The deletions that matter most are conversions, checks, error handling and logging, because each one stood between some input and a wrong result. The refund diff's case is the time-zone conversion from the previous topic: the call that put a class's start on its gym's clock stopped happening, and with it the correct deadline at every gym in the second time zone. Nobody asked for that line to go, and nothing in the summary says it went.
Other diffs remove other guards, and the shape is always the same. A check for a missing value goes because "the type already says it cannot be missing". A log line goes because it "duplicated" another one. An exception handler goes because "it was never hit in the tests". Each removal looks like tidying, and each one deletes the only thing that stood between some real input and a wrong result. Ask what stopped happening, and then ask whether anything else still makes it happen.
What Is New and Unasked-For
Every addition the task did not request needs a stated reason. The refund diff has three: the policy object with its gym setting, so each gym "can configure its own window"; a method on it that nothing calls; and a typo fixed in a schedule comment. New dependencies, environment variables and network calls go on the same list whenever they appear, because each is a deployment question, a supply-chain question (Chapter 12) or a secrets question.
Unrequested code still has to be reviewed, tested and maintained by someone, and per-gym refund windows are now a product behaviour that nobody at Cragmoor decided to offer. Chapter 7 calls this over-building and shows why it is the commonest shape in agent diffs.
What Should Be There and Is Not
The plan from Chapter 5 is the agreed scope, and the diff is held against it in both directions. One direction is what the diff does beyond the plan, which the previous question listed. The other is what the plan, or the change itself, implies and the diff never touched: the cancellation email and the schedule page, still promising 24 hours, and the boundary tests. The plan promised tests at 11 hours 59 minutes and 12 hours 1 minute, the table from Chapter 5 adds one at exactly 12 hours, and the diff has none of them; the last would have caught the boundary flip. No line of the diff points at any of these gaps. Only the search for the old rule, and the plan and the table from Chapter 5, find them.
A diff that does more than its plan is either a plan change nobody flagged or scope creep. Either way it is a decision you make now, explicitly, not one that happens by merging.
The Same Questions for a Colleague
Ines asks these five of Kai's changes and of yours. Using one set of questions for agents and for people keeps a single review bar on the team: nobody's diff gets a lighter read because of who or what wrote it. Chapter 13 makes the five the team's review template, and shows Ines asking them of Kai's next pull request.
The questions also travel well beyond refunds. On a change to the waitlist they ask what an offer does differently, who else reads the offer table, what check was removed, what new retry appeared, and which notification still describes the old behaviour. The answers differ; the questions do not. That is what makes them worth learning as a set rather than as a list of refund-specific checks.
- "If the tests pass, nothing changed except what I asked." Tests cover the behaviours someone wrote tests for. Nothing tested exactly 12 hours, so the boundary flip passed every test (Chapter 8 adds one).
- "The agent searched the codebase, so it found every caller." It read what it chose to read in this session, and a search for a function name never finds a template that says "24 hours" in words.
- "Extra code is harmless if it works." The
policyobject adds a configuration surface nobody owns, and per-gym refund windows are now a product behaviour that nobody decided. - "Removing code is always a simplification." A removal changes behaviour as much as an addition does. It simply shows less on the screen.
- "These questions are for large changes." The refund task was one number, and the diff behind it changed five behaviours.
- Write a before-and-after rule for every changed comparison and constant. The sentence exposes a boundary flip the code hides in a single comparison.
- Search for every changed name and every changed literal across code, templates and documentation. The agent's search covered only what it chose to read.
- Demand a reason for every unasked addition, and drop it when the reason is "it might be useful". It can return as its own task on its own branch (Chapter 9).
- Put the five questions in the team's review template. The same questions for agents and colleagues keep one bar (Chapter 13).
Knowledge Check
Which question finds the cancellation email that still says "24 hours"?
- What behaviour changed, read off the comparisons and constants in the diff
- Who else depends on it, answered by searching for the old number in words
- What was removed, read off the deleted lines in each file of the diff
- What is new and unasked-for, read off the additions the task did not ask for
The comparison changed from "cancelled at or before the deadline" to "cancelled before the deadline". What happens to a member who cancels exactly 12 hours ahead?
- They get a full refund, since the window is 12 hours and they are exactly on it
- They get half the refund, because the code splits the refund at the boundary
- They get nothing, because "before" now excludes the exact deadline moment
- It depends on the gym's time zone, which decides which side of the line they fall on
The diff adds a per-gym refund setting that nobody sets. What does it cost over the next year?
- A configuration surface someone must review, test and own, and an undecided product rule
- Nothing at all, since no gym sets the value and the default behaves the same as it did before
- A slower refund calculation, since the new policy object is looked up again on every refund
- A failing build some time next year, when the setting finally expires and has to be renewed
Why can a green suite never answer "who else depends on this"?
- Because test runners never render the templates that members actually get to see
- Because the agent may have skipped some of the tests, so the suite run is not complete
- Because suites only ever run the code in the files that a diff has changed
- Because tests check what they assert, and no test asserts what a template says
You got correct