--- 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: ```csharp // Before — telling attributes [Test] public void CompletedOrder_TotalIsCorrect() { var order = new Order(); order.AddLine(100m, 1); order.Complete(); Assert.That(order.Total, Is.EqualTo(100m)); Assert.That(order.Status, Is.EqualTo("completed")); } // After — encapsulating behavior [Test] public void CompletedOrder_TotalIsCorrect() { var order = new Order(); order.AddLine(100m, 1); order.Complete(); Assert.That(order, Has.OrderTotalCorrect(100m)); } ``` Then ask the agent: > The first test was refactored to use `Has.OrderTotalCorrect()`. 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.