Files
learning-hour-maker/exercises/show-dont-tell/learning-hour.md
T
mostalive 5965ed8998 Fix Concept section: use IsFullyPaid() example, not custom matcher
The Concept demo should show a different pattern (encapsulate behavior)
than the exercise (custom matcher) so the exercise stays fresh.
2026-09-02 22:30:03 +01:00

151 lines
5.6 KiB
Markdown

---
theme: agentic_engineering
title: Show, Don't Tell — Using Examples with Coding Agents
name: show_dont_tell
difficulty: 2
author: willem
affiliation:
tags: agentic copilot refactoring tests examples
---
# Show, Don't Tell — Using Examples with Coding Agents
You've described what you want to a coding agent, and it didn't get it right. Or it took so many rounds of corrections that you'd have just done it yourself. This learning hour explores a simpler approach: instead of telling the agent what style you want, show it.
## Learning Goals
* Use a concrete code example (show, don't tell) to guide a coding agent in propagating a refactoring pattern across a test suite.
## Session Outline
* 5 min connect: Frustrating agent conversations
* 5 min concept: LLMs are pattern matchers
* 35 min concrete practice: Refactor tests using a custom matcher example
* 5 min conclusion: Share back and recognize the pattern in your own code
## Connect — Frustrating Agent Conversations
Pair up and share a time when you had to iterate multiple times with a coding agent just to get it to match the style you wanted. What did you try? How many rounds did it take?
This is a [Pair Share]({% link _activities/connect/pair_share.md %}) connect.
## Concept — LLMs Are Pattern Matchers
LLMs are pattern matchers and translation machines. They are better at recognizing a pattern in concrete code than interpreting abstract instructions.
### The "Tell" Approach (doesn't work well)
> Refactor these tests to encapsulate the total price check. Instead of asserting on `order.Total`, create a method `IsFullyPaid()` and test for that.
The agent might:
- Create the method but not use it consistently
- Misinterpret "encapsulate" and change production code instead of test code
- Apply the pattern incorrectly or only partially
### The "Show" Approach (works better)
Refactor one test yourself — encapsulate the attribute check behind a behavior method:
```csharp
// Before — checking an attribute
[Test]
public void CompletedOrder_TotalIsCorrect()
{
var order = new Order();
order.AddLine(100m, 1);
order.Complete();
Assert.That(order.Total, Is.EqualTo(100m));
}
// After — encapsulating behavior
[Test]
public void CompletedOrder_TotalIsCorrect()
{
var order = new Order();
order.AddLine(100m, 1);
order.Complete();
Assert.That(order.IsFullyPaid(100m), Is.True);
}
```
Then ask the agent:
> The first test was refactored to use `order.IsFullyPaid(expected)` instead of asserting on `order.Total`. Apply the same pattern to the remaining tests.
The agent sees the concrete pattern and replicates it. No description needed.
### Key Insight
**Examples beat descriptions.** One concrete example is worth a paragraph of instructions. This works for any refactoring pattern — splitting tests, introducing custom matchers, extracting methods, renaming for clarity. Show the agent what "done" looks like.
## Concrete Practice — Refactor Tests with a Custom Matcher
### Setup
Open the project in `exercises/show-dont-tell/` in Rider with GitHub Copilot Agent mode.
The project contains an `Order` domain and a test file `OrderTotalTests.cs` with 6 tests.
### Step 1 — Identify the Smell (5 min)
Open `OrderTotalTests.cs`. Look at the tests. What's the pattern you see?
- The first test (`DiscountOnFirstLine_TotalAndLineValues`) was already refactored — notice how it uses `Has.OrderState()` with a single assertion.
- The remaining 5 tests each have multiple `Assert.That()` calls checking `order.Total`, `order.Lines[0].Value`, `order.Lines[1].Value`, etc.
These multiple asserts are checking attributes (state) rather than expressing intention. They're also hard to read and fragile — if the order structure changes, every test needs updating.
### Step 2 — Examine the Example (5 min)
Look at the first test and the custom matcher file `OrderStateConstraint.cs`. You don't need to understand how the matcher is implemented — just recognize the pattern:
```csharp
// One assertion that bundles all checks
Assert.That(order, Has.OrderState(
expectedTotal: 210m,
expectedLineValues: new[] { 160m, 50m }));
```
vs.
```csharp
// Three separate assertions
Assert.That(order.Total, Is.EqualTo(240m));
Assert.That(order.Lines[0].Value, Is.EqualTo(200m));
Assert.That(order.Lines[1].Value, Is.EqualTo(40m));
```
### Step 3 — Use the Agent (20-25 min)
Use GitHub Copilot Agent mode to refactor the remaining 5 tests. Prompt:
> The first test uses a custom matcher `Has.OrderState()`. Refactor the remaining tests to use the same pattern — one assertion with `Has.OrderState()` instead of multiple individual asserts.
Run the tests after the agent makes changes to verify nothing broke.
**If the agent doesn't get it right on the first try:**
- Check that the example test is visible in the same file
- Be more specific: "Look at `DiscountOnFirstLine_TotalAndLineValues` as the example"
- Point out what went wrong and ask it to retry
### Tools Needed
- Rider with GitHub Copilot Agent mode
- The `exercises/show-dont-tell/` project (included in this repo)
- .NET 10 SDK
## Conclusions — Share Back
Go around and ask:
1. **Did the agent get it right on the first try? What happened if it didn't?**
2. **What made the example work — what would have made it fail?**
3. **Do you recognize this pattern in your own tests?** Where could you use "show, don't tell" in your real work?
This is an [Explain the Main Idea]({% link _activities/conclusions/explain_main_idea.md %}) conclusion.
### One-Sentence Takeaway
When working with coding agents, show them what you want with a concrete example instead of describing it in words — LLMs are pattern matchers, and examples are patterns.