Study guides / CCAR-F / Domain 3

Claude Code Configuration & Workflows · Lesson 5 of 6

3.5 - Iterative Refinement Techniques

Master the technique hierarchy for iterative refinement: concrete examples over prose, test-driven iteration, the interview pattern, and batch vs sequential feedback

Working with Claude Code is iterative. The first output is rarely the final one. The exam checks that you know the specific techniques for steering Claude Code toward the right result - and which one to reach for first in each situation.

The Technique Hierarchy

Not all refinement techniques are equal. There's a clear pecking order:

1. Concrete input/output examples (most effective for inconsistent interpretation)

When you describe a code transformation in prose and Claude Code interprets it differently each time, the fix is not more prose. The fix is concrete examples.

Provide 2-3 examples showing the exact input and the exact expected output:

Input:
  getUserData(userId: string): Promise<UserData>

Expected output:
  getUserData(userId: string): Promise<Result<UserData, ApiError>>
Input:
  fetchOrders(customerId: string): Promise<Order[]>

Expected output:
  fetchOrders(customerId: string): Promise<Result<Order[], ApiError>>

The model generalises from these examples more reliably than from any prose description. Two or three concrete examples set the pattern, and the model applies it to new cases. This is the first technique to reach for when interpretation is inconsistent.

2. Test-driven iteration (most effective for complex transformations)

Write the tests first. Define the expected behaviour through test cases covering:

Then share the test failures with Claude Code. The failures give concrete, unambiguous feedback about what needs fixing. There's no room for interpretation when the test output says "Expected X, got Y."

FAIL: testMigrationHandlesNullValues
  Expected: null preserved in output JSON
  Actual: null replaced with empty string ""

This failure message tells Claude Code exactly what to fix. No prose explanation needed.

3. Interview pattern (most effective for unfamiliar domains)

When you're working in a domain where you lack expertise, have Claude ask questions before implementing. This surfaces considerations you'd otherwise miss.

Instead of prescribing a solution:

"Build me a caching layer for the API"

Use the interview pattern:

"I need a caching layer for the API. Before implementing, ask me questions about the requirements, edge cases, and constraints I should consider."

Claude might ask about cache invalidation strategies, TTL policies, consistency requirements, and failure modes - considerations that an expert would know to address but that you might overlook.

Key Concept

The interview pattern is for unfamiliar domains where the developer might miss important considerations. Concrete examples are for when the developer knows the exact transformation but the model interprets it inconsistently. Do not confuse the two - they solve different problems.

Batch vs Sequential Feedback

How you deliver feedback matters. The rule:

Single message (batch) when fixes interact with each other:

If changing the error handling pattern also affects the logging format and the response structure, provide all three pieces of feedback in one message. The model needs to see all the interacting constraints at once to produce a coherent fix.

Three changes needed (they interact with each other):
1. Error responses must include an error code field
2. Logging must include the error code in structured format
3. The client SDK type definitions must reflect the new error code field

Sequential iteration when issues are independent:

If the naming convention issue and the indentation issue don't affect each other, fix them one at a time. Batching independent issues can confuse the model about which feedback applies to which part of the code.

First iteration: "Fix the function naming - use camelCase throughout"
[Wait for result]
Second iteration: "Now update the indentation to use 2 spaces"

Example-Based Communication in Practice

When prose descriptions produce inconsistent results, the switch to examples follows a clear pattern:

  1. Observe inconsistency: You describe a transformation, Claude Code does it differently each time.
  2. Switch to examples: Provide 2-3 concrete before/after pairs showing the exact transformation.
  3. Verify generalisation: Test on a new case to confirm the model generalises the pattern correctly.
  4. Add edge case examples if needed: If the model handles the standard case but misses edge cases, add examples specifically showing edge case handling.

It's not about piling on more examples. Two or three well-chosen ones that cover the standard case and a key edge case are enough. The model generalises the pattern; you don't need to hand it every possible case.

When Each Technique Applies

Situation Technique
Prose description interpreted differently each time Concrete input/output examples
Complex transformation with many edge cases Test-driven iteration
Working in an unfamiliar domain Interview pattern
Multiple issues that affect each other Batch feedback (one message)
Multiple independent issues Sequential feedback

Exam traps

Practice question

A developer describes a code transformation in prose. Claude Code interprets it differently each time, producing inconsistent results. What technique should the developer try first?

  • A Rewrite the prose description with more precise language and technical terminology

    More precise prose still relies on the model interpreting natural language. If interpretation is already inconsistent, adding more words to interpret does not fix the root cause.

  • B Write a comprehensive test suite and iterate by sharing test failures

    Test-driven iteration is effective but heavier than necessary as a first step. Concrete examples are faster and directly address the interpretation inconsistency. Save test-driven iteration for complex transformations with many edge cases.

  • C Use the interview pattern to have Claude ask clarifying questions before implementing

    The interview pattern surfaces considerations the developer might miss in unfamiliar domains. Here, the developer knows the exact transformation - the problem is the model interpreting it inconsistently. Examples, not questions, are the fix.

  • D Provide 2-3 concrete input/output examples showing the exact before and after transformation Correct

    Concrete examples eliminate interpretation ambiguity entirely. The model sees exactly what the input looks like and exactly what the output should look like. It generalises from examples more reliably than from descriptions. This is the documented first-line technique.

Build exercise: Practice Iterative Refinement Techniques

Beginner · 30 minutes

You'll practice:

  1. Describe a code transformation in prose and run it three times, noting how interpretation varies across runs

    This demonstrates the core problem that concrete examples solve. Prose descriptions rely on interpretation, and interpretation varies across runs. Observing this inconsistency firsthand makes the case for switching to examples.

    You should see: Three different outputs from the same prose description. The variations may be subtle (different naming choices, different edge case handling) or significant (different structural approaches). This proves that prose alone produces inconsistent results.

    Hints
    1. Choose a transformation that has some ambiguity in how it could be applied. Run the exact same prompt three times.
    2. Try a transformation like: "Wrap all function return types in a Result type." Run it three times on the same function and compare the outputs for consistency.
    3. Use this prompt three times:
      "Refactor getUserData to wrap the return type in a Result type for error handling."
      Compare: Does each run produce the same Result shape? The same error type? The same parameter handling?
  2. Provide 2-3 concrete input/output examples of the same transformation and run it three times - compare the consistency

    Concrete examples are the documented first-line technique for inconsistent interpretation. The model generalises from examples more reliably than from prose. This step proves the effectiveness difference experimentally.

    You should see: Three outputs that are consistent with each other and match the pattern established by the examples. The variation observed in the prose-only step is eliminated or drastically reduced.

    Hints
    1. Take the same transformation from step 1 but this time provide explicit before/after examples instead of a prose description.
    2. Show 2-3 concrete input/output pairs. The model should generalise the pattern and apply it consistently to new cases.
    3. Provide examples like:
      Input: getUserData(userId: string): Promise<UserData>
      Output: getUserData(userId: string): Promise<Result<UserData, ApiError>>
      
      Input: fetchOrders(customerId: string): Promise<Order[]>
      Output: fetchOrders(customerId: string): Promise<Result<Order[], ApiError>>
      
      Now apply this pattern to: deleteAccount(accountId: string): Promise<void>
      Run three times - the outputs should be consistent.
  3. Write a test suite for a function with happy path, edge cases, and error cases, then iterate by sharing test failures with Claude Code

    Test-driven iteration is the most effective technique for complex transformations. Test failures provide unambiguous feedback - "Expected X, got Y" leaves no room for interpretation. This technique complements examples for more complex scenarios.

    You should see: After sharing test failures, Claude Code makes targeted fixes that address the specific failing assertions. Each iteration reduces the number of failing tests. The feedback loop is faster and more precise than prose-based corrections.

    Hints
    1. Write tests first that define the expected behaviour, then ask Claude to implement the function and share any failures back.
    2. Cover three categories: happy path (standard input produces expected output), edge cases (null, empty, boundary values), and error cases (invalid input, missing data).
    3. Write tests like:
      test("formats date correctly", () => {
        expect(formatDate("2024-01-15")).toBe("15 January 2024");
      });
      test("handles null input", () => {
        expect(formatDate(null)).toBe("Unknown date");
      });
      test("rejects invalid date", () => {
        expect(() => formatDate("not-a-date")).toThrow("Invalid date format");
      });
      Share any FAIL output directly with Claude Code for targeted fixes.
  4. Use the interview pattern for a task outside your expertise - ask Claude to pose questions before implementing and note what considerations surface

    The interview pattern is for unfamiliar domains where you might miss important requirements. It surfaces considerations an expert would know to address. The exam tests whether you can distinguish this from the examples technique - they solve different problems.

    You should see: Claude asks 5-10 targeted questions about requirements, edge cases, and constraints you had not considered. The questions reveal considerations like cache invalidation strategies, consistency requirements, failure modes, or security implications that would have been missed.

    Hints
    1. Choose a task in a domain where you lack deep expertise. Instead of prescribing a solution, ask Claude to question you first.
    2. Frame your request as: "I need X. Before implementing, ask me questions about requirements, edge cases, and constraints I should consider."
    3. Try: "I need a caching layer for the API. Before implementing, ask me questions about the requirements, edge cases, and constraints I should consider."
      Expected questions might include:
      - What is the cache invalidation strategy?
      - What consistency model is required?
      - What happens on cache failure - fail open or fail closed?
      - What is the TTL policy?
  5. Practice batching: give Claude three interdependent issues in one message and observe whether the fix is coherent across all three

    When issues interact, batching them in one message lets the model see all constraints simultaneously. Sequential fixing of interdependent issues causes the model to fix one issue in a way that conflicts with the others. The exam tests this distinction.

    You should see: A single coherent fix that addresses all three interdependent issues consistently. The error response shape, the logging format, and the type definitions all align with each other. Compare this to fixing them sequentially, where each fix might conflict with the next.

    Hints
    1. Choose three changes where fixing one affects the others. Provide all three in a single message.
    2. Good examples: changes to error response shape + logging format + client SDK types, where all three must agree on the error structure.
    3. Provide in one message:
      Three changes needed (they interact):
      1. Error responses must include an errorCode field
      2. Logging must include errorCode in structured JSON format
      3. The client SDK TypeScript types must reflect the new errorCode field
      
      All three share the same error structure, so they must be fixed together.

Sources