Chapter 13 · Week 13

Iterative Refinement

What is patience in correction?

Chapter 13 — Iterative Refinement

“Let every person be quick to hear, slow to speak, slow to anger.” — James 1:19

“It is impossible to make anything foolproof, because fools are so ingenious.” — anonymous senior engineer, on debugging


Why This Matters

The AI’s first answer is going to be wrong sometimes.

Not catastrophically wrong, usually. Wrong in the way a fast junior is wrong: a method that looks right and almost works. Tests pass on three inputs and fail on the fourth. An off-by-one in a loop you can’t quite see. A regex that matches everything you tested and nothing you didn’t. The output compiles. The output runs. The output almost works.

You are now in the most expensive room in software engineering — the room where a confident, fluent, plausible answer is in front of you, and the only thing standing between that answer and shipped code is your ability to interrogate it.

Project 13 puts a name on the skill: drive AI to green. You are handed a small program with a complete test suite. Five tests fail. Your job is to fix the program — but you may not edit the code yourself. You may only prompt the AI. Your grade depends on (1) reaching all-green, (2) how few prompts it took, and (3) the quality of your diagnostic questions.

The skill is not new. It is the same skill from Chapter 5 of this book — Debugging Discipline — applied to a different patient. In Chapter 5, the bug was in the code in front of you. This week, the bug is in the code the AI just wrote for you, and your only debugger is your prompt.

The prompt is now a scalpel.


13.1 — The Pattern: Symptom → Hypothesis → Targeted Prompt

In Chapter 5 you learned that good debugging is hypothesis-driven inquiry. You do not “try things.” You form a hypothesis about why the program is broken, you go looking for evidence that confirms or refutes the hypothesis, and you fix the actual cause rather than the symptom.

Driving AI to green is the same loop, with one substitution:

Chapter 5 (you debug)Chapter 13 (you debug AI’s code)
Observe the failing testObserve the failing test
Form a hypothesis about the bugForm a hypothesis about the bug
Investigate the code to confirmAsk the AI a targeted question to confirm
Write the fixAsk the AI to write the targeted fix
Re-run the test suiteRe-run the test suite

Notice what did not change. You still form the hypothesis. You still decide where the bug probably lives. You still verify with evidence. The AI is doing the typing, not the thinking. The thinking is yours.

This is the senior/junior model from Chapter 9, sharpened into an iterative loop.

Coach’s Note — Students who skip the hypothesis step send prompts like “the test is failing, please fix it” and then wonder why the AI keeps making things worse. You are running a vague prompt against a fluent responder. The output will be fluent and vague. Of course it doesn’t fix the bug. The hypothesis step is non-negotiable.


13.2 — Two Ways to Correct: Diagnostic vs Directive

There are two fundamentally different ways to push back on the AI when its answer is wrong. Most students reach for one and never try the other.

The directive correction

You tell the AI what the bug is.

“The parseDate method is treating the year as a two-digit value. Change it to four digits.”

This works when you already know exactly what went wrong. The AI executes your fix. Fast. Cheap. Boring.

The diagnostic correction

You ask the AI to find its own bug.

“The test parseDate_handles2026 expects LocalDate.of(2026, 5, 24) and is getting LocalDate.of(26, 5, 24). Walk through parseDate("2026-05-24") line by line and tell me where the year becomes 26 instead of 2026.”

This works when you have a symptom but not yet a cause. The AI does the trace; you read the trace and confirm whether it lands on a real bug. The diagnostic prompt is slower per round, but each round teaches you something about the code — and crucially, it does not bake in your wrong guess about the cause.

The mature pattern is to use the diagnostic prompt first, then escalate to directive once you know what’s wrong.

The failure mode of always going directive: you confidently tell the AI to fix what you think is the bug. The AI obliges. The fix works for the failing test. Another test that was previously passing now fails — because your “fix” was wrong, and the AI didn’t push back because you didn’t ask. You’ve spent a prompt and broken something else.

The failure mode of always going diagnostic: you spend ten prompts having the AI trace code you could read in two minutes yourself. You’re slow, and you’re also outsourcing the part of the work that builds your judgment.

The skill is knowing which one to reach for, when.

Coach’s Note — A useful instinct: when you’re not sure what’s wrong, go diagnostic. When you’re sure what’s wrong but lazy about typing, go directive. When you’re sure what’s wrong and you’re right, both work. When you’re sure what’s wrong and you’re wrong — the directive prompt wastes a round, the diagnostic prompt saves you. The asymmetry favors diagnostic when in doubt.


When the AI’s output is too large to debug, narrow what you ask it to look at.

A bad prompt:

“Something is wrong with the StudyStreakTracker. Fix it.”

A better prompt:

“Only the recordCheckIn(LocalDate) method is wrong. The test recordCheckIn_advancesStreakByOne fails. Do not change any other method. Show me only the new body of recordCheckIn.”

What changed:

  1. Scoped the surface. One method, not the whole class.
  2. Stated the failing test. The AI now knows what “wrong” means.
  3. Constrained the output. “Show me only the new body” prevents the AI from rewriting half the class — which is the most common way an AI breaks previously-passing tests.

The third constraint matters more than students expect. Left to its own devices, an AI told to “fix” a bug often returns a refactor — three methods reorganized, a helper renamed, a constant moved. That refactor probably works for the failing test. It probably breaks something else. Constraining the output to “only the body of this one method” is the cheapest available defense.

This is also why Chapter 12’s architecture work pays off here. If your module boundaries are clean, narrowing the search is easy: the bug is in this one class with three methods, and I can isolate it to one of them in two prompts. If your modules are sprawling and tangled, you can’t narrow. The AI will rewrite half the system in response to every prompt, and you’ll never reach green.

Coach’s Note — The discipline of “show me only X” is the same discipline as a good code review comment: don’t ask for “improvements to this PR.” Ask for “the body of parseDate, changed minimally to satisfy this one failing test.” Specificity bounds the blast radius.


13.4 — Showing the AI the Failing Test

One of the cheapest correction techniques is also the most under-used: paste the failing test into the prompt.

Compare these two prompts for the same bug.

Prompt A (no test):

“The factorial method returns the wrong value for some inputs. Please fix it.”

Prompt B (with test):

“The following JUnit test fails. Fix the factorial method so it passes:

@Test
void factorial_ofZero_isOne() {
    assertEquals(1, MathUtils.factorial(0));
}

Current implementation:

public static int factorial(int n) {
    int result = 1;
    for (int i = 1; i <= n; i++) {
        result *= i;
    }
    return result;
}

Show only the new factorial method body.”

Prompt B is longer. Prompt B will also produce a correct fix on the first try, while Prompt A might take three or four rounds of “no, that’s not right either.”

The reason: in Prompt B, the AI has the exact contract it must satisfy. It can simulate the test in its head. It can see the contract is factorial(0) == 1, look at the loop, notice the loop body never runs for n == 0, see that result is correctly initialized to 1, and then — having traced the code — realize that the implementation actually does pass this test, which means the test isn’t what you said it was, or there’s a different test failing, or you misread the failure. The conversation moves forward.

In Prompt A, the AI is guessing what “wrong” means. It guesses something plausible. It is often guessing wrong.

Coach’s Note — Show, don’t tell. You learned this in writing class. It applies to prompting an AI. A pasted failing test is worth ten sentences of “here’s what should happen.” The test is unambiguous executable English.


13.5 — The Rubber Duck Prompt

There’s a debugging technique called “rubber duck debugging”: you explain your problem out loud to a rubber duck on your desk, and halfway through the explanation you spot the bug. The duck doesn’t help. The articulation helps.

You can do this with the AI. The pattern:

“I’m trying to understand why this test fails. Walk me through what happens, step by step, when I call parseDate("2026-05-24"). Just trace the code. Don’t propose a fix yet.”

The AI obliges. It produces a step-by-step trace. You read the trace. Somewhere in the middle, the trace says something like:

“Line 14 splits the string on - and takes the first token, which is "2026". Line 15 calls Integer.parseInt on this, getting 2026. Line 16 then does year % 100 which gives 26 …”

There it is. Line 16. The bug isn’t in the parsing; the bug is a leftover % 100 from when the code handled two-digit years. You’d never have caught that by re-reading the code yourself — you’d have read past it the same way you wrote past it. But the trace surfaces it.

The trick: explicitly tell the AI not to propose a fix yet. If you don’t, the AI will propose a fix as part of the trace, and you’ll be tempted to accept it without reading the trace. The trace is the value. The fix is the easy part once the trace surfaces the bug.

The duck doesn’t help. The articulation helps.


13.6 — Recognizing the Loop

The most expensive failure mode of AI-assisted debugging is the correction loop.

The pattern looks like this:

  1. Test fails. You prompt.
  2. AI fixes the test. A different test now fails.
  3. You prompt again — “now this test fails.”
  4. AI fixes that test. The first test fails again.
  5. Go to 1.

You can run this loop indefinitely. Each round feels productive. Each round costs a prompt. At the end of an hour, you’ve sent twenty prompts and you’re back where you started — except you now have less confidence about what the code actually does, because it has been rewritten four times.

The signal that you are in the loop: the AI keeps proposing the same shape of fix, and the test set keeps oscillating. It fixes test A, breaks test B. Fixes test B, breaks test A. The AI is not learning across prompts — it doesn’t remember its earlier attempts the way you do. It sees a fresh problem each round, makes a fresh guess, and the guess oscillates between two plausible-but-wrong implementations.

Breaking the loop requires you to do something the AI cannot do: hold both failing tests in your head at the same time and reason about the constraint they jointly impose.

The prompt that breaks the loop looks like this:

“Both of these tests must pass simultaneously:

@Test void emptyInput_returnsEmptyList() { assertEquals(List.of(), parse("")); }
@Test void singleItem_returnsOneElementList() { assertEquals(List.of("a"), parse("a")); }

Your last two attempts each passed one and failed the other. Tell me, in one sentence, what invariant the method must satisfy to pass both. Then propose the implementation.”

The AI now has to articulate the joint constraint before writing code. That articulation is what was missing from each previous round, and it is what stops the oscillation.

Coach’s Note — The loop is the single most common way students burn an hour on P13 and end up no closer to green. Train yourself to notice it after two rounds, not ten. The cue is “same shape of fix, different oscillating failures.” When you see that pattern, stop. Re-prompt with both constraints explicit.


13.7 — When to Stop Iterating and Write It Yourself

Sometimes the correct move, after three or four prompts, is to stop prompting and write the code yourself.

This is heresy in some AI-evangelism circles. It should not be heresy in yours. You are a senior engineer; the AI is a junior partner; sometimes the junior is in over their head and the senior takes the keyboard.

The cue that it’s time to stop prompting and write it:

  • The bug is in code that’s already small enough that you can read it.
  • The AI has made the same wrong fix twice in a row.
  • You can describe the correct fix in one sentence.
  • The remaining fix is essentially typing, not thinking.

When all four are true, just write it. Five minutes of typing beats fifteen minutes of prompting that converges on the same answer.

The reverse is also true. When the bug is in a 200-line method you didn’t write, when you have only a vague hypothesis about the cause, when the fix is mechanical-but-extensive — that is when prompting wins, because the AI types faster than you do, and the cost of being wrong is one prompt rather than thirty minutes of edits to undo.

The skill is reading the situation correctly. The cue: can I describe the fix in one sentence? If yes, write it. If no, prompt.

Coach’s Note — P13 grades you on prompts spent, not just on reaching green. A solution that reached green in 3 prompts plus some hand-typing beats a solution that reached green in 17 prompts of pure AI. The grader is checking whether you know when to take the keyboard.


13.8 — Showing the AI Its Own Recent Mistake

When the AI repeats a mistake, show it the mistake explicitly.

“Your last attempt produced this implementation, which fails the test handlesNegativeInput:

public static int abs(int n) {
    return Math.abs(n);  // returns Integer.MIN_VALUE when n == Integer.MIN_VALUE
}

The failure occurs on input Integer.MIN_VALUE, where Math.abs returns Integer.MIN_VALUE itself (overflow). The method must either handle this case or throw an exception. Which should it do, according to the spec? Then implement it.”

The AI cannot remember its last few prompts the way you can. Quoting the prior attempt back to it serves two purposes:

  1. It makes the AI reason about why the prior attempt was wrong, rather than producing a similar attempt.
  2. It surfaces the underlying ambiguity (in the example above, the spec was ambiguous about what to do on overflow) so you can decide it consciously.

The diagnostic prompt from §13.2 and the show-the-mistake prompt from this section are cousins. Both work by forcing the AI to articulate something it would otherwise gloss past.


13.9 — The Test as Spec, Revisited

Chapter 10 taught you that a prompt is a specification. This chapter completes the thought: when you have a failing test, the failing test is your spec.

A test is the most unambiguous form of specification that exists. It says: for this input, expect this output, in this exact form. It compiles. It runs. It either passes or fails. There is no room for “the AI might have misunderstood me.”

In every iteration on P13, the test suite is your contract with the AI. The progress signal isn’t “the code looks better.” The progress signal is “the number of failing tests went down and didn’t come back up.”

A useful prompt template for the whole project:

“Below is the current implementation of <Module>.java. Below that is the JUnit test suite. Currently, N tests are failing. Their names are: <list>. Fix the implementation. Output only the new file. Do not modify the test suite.”

That template, parameterized by the failing-test list, is one of the most reliable prompts in your toolbox. Save it.

Coach’s Note — “Do not modify the test suite” is critical. Without it, you will eventually get an AI response that “fixes” the implementation by also “improving” the tests — making them weaker, so they now pass. Watch for it. It is the silent failure mode of Project 13.


13.10 — When the AI Is Hallucinating an API

Sometimes the AI’s “fix” introduces a call to a method that doesn’t exist. LocalDate.fromString(s). String.reverse(). ArrayList.sortBy(...). The compiler catches these — they are obvious — but a plausible-but-wrong API costs you a round.

The fix is to be specific in the prompt about what API surface is allowed:

“Fix the date parsing using only methods from java.time.LocalDate and java.time.format.DateTimeFormatter. Do not invent helper methods. Do not import anything outside java.time.* and java.util.*.”

The constraint “do not invent helper methods” is the secret weapon. Without it, the AI loves to invoke MyDateUtils.normalize(...) — a method it just made up, which doesn’t exist anywhere. Constraint pins the AI to the actual standard library.

You learned this in Chapter 11 (Code Review) as a hallucinated-API failure mode. Here, you’re learning the prompting fix.


13.11 — Diagnostic Templates Worth Saving

Five templates that pay rent. Memorize the shape; adapt the specifics.

Template 1 — Trace, don’t fix

“Walk me through what happens when this method is called with <input>. Trace each line. Do not propose a fix. Just trace.”

Template 2 — Find the bug yourself

“The test <name> fails with this assertion error: <error>. Look at the implementation below. Where, exactly, does the value diverge from what the test expects? Identify the line. Don’t change anything yet.”

Template 3 — Minimal targeted fix

“Change only <methodName>. Do not modify other methods. Do not refactor. Output the new method body only.”

Template 4 — Both constraints

“These two tests must pass simultaneously: <test1>, <test2>. Your last two attempts each passed one and failed the other. State the joint invariant. Then implement.”

Template 5 — Stop and read the spec

“Before you propose a fix, restate what the method is supposed to do, in two sentences, based on the spec comment above the method. Then propose the fix.”

That last one is gold. Half of all AI “fixes” go off the rails because the AI is fixing something — but not the actual contract. Forcing the AI to restate the contract before fixing it filters out a huge class of confident wrong answers.

Coach’s Note — Build a prompt-toolbox.txt of your own. Every time a prompt template works well for you, save it. Every time one fails for a specific reason, save the failure mode too. By the end of the term you will have a personal prompt library worth more than any blog post on prompting you can find online.


13.12 — Common AI Pitfalls (Week 13 Edition)

Pitfall: The AI keeps making the same wrong fix. Why it happens: The AI doesn’t remember your earlier prompts as a correction history; each round is fresh-ish. Fix: Quote the prior wrong attempt back at it, with the failing test, and ask it to explain why the prior attempt was wrong before proposing a new one.


Pitfall: The AI’s “fix” passes the failing test but breaks a previously-passing test. Why it happens: You constrained the prompt to “make this test pass” without saying “and don’t break the others.” Fix: Always paste the full test list (or at least the test class) and the list of currently-failing tests. “These N tests fail; these M pass. Keep the M passing while making the N pass.”


Pitfall: The AI rewrites half the class instead of fixing one method. Why it happens: No scoping constraint in the prompt. Fix: “Modify only <methodName>. Output the new method body only.”


Pitfall: The AI proposes a “fix” that introduces a call to a method that doesn’t exist. Why it happens: Hallucinated API. The AI optimizes for plausibility, not existence. Fix: Constrain the allowed imports/classes explicitly. Compile after every accepted change.


Pitfall: You can’t tell whether the AI’s explanation of the bug is correct. Why it happens: You skipped Chapter 1’s reading work and can’t audit a 20-line method confidently. Fix: Read the method yourself before reading the AI’s explanation. If your read and the AI’s read agree, the explanation is probably right. If they disagree, you are the senior — investigate.


Pitfall: The conversation has gone on so long the AI no longer remembers what file it’s editing. Why it happens: Long context drift. Fix: Start fresh. Paste the current file state and the current failing tests into a single new prompt. Treat the new conversation as the only one that exists.


Pitfall: Twenty prompts in, no green. You’re exhausted and getting reckless. Why it happens: You’re in the loop (§13.6) and haven’t recognized it. Fix: Stop. Walk away for ten minutes. When you come back, write the fix by hand (§13.7).


13.13 — The Apologetic: Patience in Correction

The framing question for this chapter is what is patience in correction? This is pastoral wisdom: the church has thought hard, for centuries, about what it means to correct another person well.

The pastoral tradition says: the right correction, at the right time, with the right specificity. Not every fault gets named in the same moment. Not every fault gets the same tone. The pastor who confronts a struggling parishioner with the maximum-strength rebuke on the first sign of struggle is not a faithful pastor; he is an angry one. The pastor who never confronts is also not faithful; he is a coward. The skill is calibration. The skill is reading the moment.

James 1:19 — let every person be quick to hear, slow to speak, slow to anger. The diagnostic prompt (§13.2) is quick to hear — you listen to what the code is actually doing before you decide what’s wrong. The narrowing constraint (§13.3) is slow to speak — you don’t fire off a blanket complaint, you scope your correction to the one thing that needs to change. The willingness to stop iterating and take the keyboard yourself (§13.7) is slow to anger — you don’t punish the AI for not converging; you take responsibility for the part of the work that’s yours.

This is not a forced analogy. The disciplines genuinely overlap. Correcting code well is a small instance of a much larger skill — the skill of correcting anyone well. The Christian student who has learned, in their congregation or in their family, what good pastoral correction looks like has already learned, in the relevant sense, what good AI correction looks like. The vocabularies differ. The skill is the same.

There is also a deeper move here. The pastoral tradition holds that correction is for the corrected, not for the corrector. The point of pointing out the AI’s bug is not to make yourself feel smart. The point is to get to working code. When the correction stops serving that end — when you find yourself testing the AI for sport, or scolding it for being wrong — you have lost the plot. Slow to anger. The AI does not have feelings to wound; you have time to lose, and a posture to maintain.

That last point matters more than students expect. The senior engineer who treats their AI partner with contempt eventually treats their human juniors with contempt. The posture you build now becomes the posture you bring to your future team. Practice the right one.

Coach’s Note — If you grew up around healthy church discipline — gentle, specific, restoration-oriented — you already have a model for what this chapter is teaching. If you grew up around the unhealthy version — public, vague, shaming — you have to unlearn it. The same skill that produces a good pastor produces a good code reviewer, a good senior engineer, and a good AI driver. Build it deliberately.


13.14 — Reps

Open the exercises for the full set.

Rep 1. Take a broken Math.factorial and drive it to green in three or fewer prompts.

Rep 2. A test oscillates between two failing states. Write the prompt that breaks the loop.

Rep 3. Receive a “fix” that hallucinates an API. Write the prompt that scopes the AI to real Java standard library only.

Full set in the exercises.


13.15 — This Week’s Project: Drive AI to Green

You’re ready for Project 13 — Drive AI to Green, in Project 13.

You will be given a small Java program with a JUnit test suite. Five tests fail. You may not edit the program directly. You may only prompt the AI. Your prompts and the AI’s responses are logged. The grader reads the log.

Your grade depends on:

  1. Whether you reach green.
  2. How few prompts it took.
  3. The diagnostic quality of your prompts.

Medium tier introduces a regression: at some point, the AI’s fix breaks a previously-passing test. You have to catch that yourself and redirect. Hard tier asks you to do the whole exercise twice — once with the course-default AI assistant, once with a different one — and compare.

This is the most realistic Phase 2 project so far. It is also the one most students underestimate. Read the spec before you start.


13.16 — Coach’s Final Word for Week 13

The thing this week is teaching you is not “how to prompt AI.” Anyone with internet access can learn that in an afternoon. The thing this week is teaching you is how to correct under pressure, with specificity, without losing your composure.

That is a senior-engineer skill. It is also a pastoral skill. It is also, in its smaller way, an ordinary human skill — the skill of being the person other people want to be wrong in front of, because they know your corrections will land well.

When you drive AI to green for the fourth time on the fourth project, you will notice your prompts getting tighter. Fewer words. More specificity. The instinct to “just rewrite the whole thing” fading. The instinct to “trace and narrow and target” becoming automatic. That is the muscle this chapter is asking you to build.

You have one more skill chapter, then one consolidation chapter, then the final. Three weeks. The architecture is in place. The pieces are all on the board.

See you on Monday.


Up next: Read the exercises and run every rep. Then open Project 13 and drive AI to green. After that, Chapter 14 — the honesty question.