Module 4: The agentic workflow: Explore → Plan → Code
Multi-turn Conversations: Iterating Effectively with Claude Code
Multi-turn Conversations: Iterating Effectively with Claude Code
Overview
Claude Code isn't a chatbot you throw a prompt at while you wait for magic. It's a conversational agent: every message you send builds on the last one, and the quality of your result depends directly on how you steer that conversation.
The difference between a developer who uses Claude Code poorly and one who has mastered it lives here: in how they iterate. The people who get exceptional results don't write one perfect 50-line prompt — they write 5-8 short messages, give precise feedback, redirect when something drifts, and build the solution incrementally. That's a multi-turn conversation.
In this capsule you'll learn the patterns that make your conversations with Claude Code productive: how to give feedback, when to split a task across several messages, how to use commands like /clear and /compact, and how CLAUDE.md supplies consistent context across the whole session.
Multi-turn: The Mental Model
Claude Code remembers everything within the session
When you start a session with claude, everything you type and everything Claude answers piles up in the context window. Which means:
- Claude "remembers" what you asked 10 messages ago
- It can refer back to code it generated earlier
- It can correct mistakes based on your feedback
- It can build on previous decisions
You: "Analyze the structure of this project"
Claude: [reads files, shows the structure]
You: "Now create a GET /users endpoint"
Claude: [uses what it learned about the project to create the endpoint correctly]
You: "Add validation with Zod"
Claude: [takes the previous endpoint and adds validation, without you repeating anything]
Every message carries implicit context. You don't need to say "the endpoint you created earlier" — Claude knows.
One-shot vs Multi-turn
| Aspect | One-shot (one message) | Multi-turn (several messages) |
|---|---|---|
| Control | Low — all or nothing | High — you adjust at every step |
| Risk | If it fails, you start over | If one step fails, you fix only that one |
| Maximum complexity | Medium | High |
| Feedback | None | Continuous |
| Context | Just your prompt | Your prompt + everything before it |
Practical rule: If your task has more than 2 logical steps, or if you're not 100% sure what you want, go multi-turn.
How to Give Effective Feedback
Feedback is your main tool
The most important skill in Claude Code isn't writing prompts — it's giving feedback. Every Claude response is a chance to adjust course.
Specific vs vague feedback
Bad — vague and useless:
You: "No, that's wrong"
You: "Do it better"
You: "I don't like it"
Claude doesn't know what's wrong. It'll guess, and it'll probably guess wrong.
Good — specific and actionable:
You: "The endpoint returns 200 when it should return 201 for creation.
It's also missing the Location header with the created resource's URL."
You: "The validation is fine, but I want the Zod errors
transformed into this shape: { field: string, message: string }[]"
You: "The return type is right, but use interface instead of type
— that's this project's convention (it's in CLAUDE.md)."
The 5 types of feedback
1. Direct correction — something is objectively wrong:
You: "The port is hardcoded to 3000. It should read from process.env.PORT
with a 3000 fallback."
2. Redirection — that isn't what you asked for:
You: "That's not what I need. I don't want an ORM, I want direct SQL
queries with the pg driver. Rewrite it using pool.query()."
3. Refinement — it's fine but you want more:
You: "Good, now add rate limiting to the endpoint. Use express-rate-limit
with a maximum of 100 requests per minute per IP."
4. Confirmation + next step — all good, move on:
You: "Perfect. Now create the tests for that endpoint with Vitest."
5. Exploration — you want to understand before deciding:
You: "Before implementing, what are the trade-offs between using JWT
and session cookies for this case?"
Effective Conversation Patterns
Pattern 1: Incremental Development
The most common and most powerful pattern. You build a feature step by step.
# Step 1: Base structure
You: "Create an authentication module with login and signup.
Just the file structure and the types for now, no implementation."
Claude: [creates src/auth/types.ts, src/auth/login.ts, src/auth/register.ts
with types and interfaces]
# Step 2: Core implementation
You: "Implement login. Use bcrypt to compare passwords and jsonwebtoken
to generate tokens. The secret comes from env."
Claude: [implements login with bcrypt and jwt]
# Step 3: Refinement
You: "Add error handling: user not found (404),
wrong password (401), internal error (500). Each error with
a descriptive message."
Claude: [adds error handling to the existing login]
# Step 4: Testing
You: "Create unit tests for login covering the 3 error cases
and the success case."
Claude: [creates tests with bcrypt and jwt mocks]
# Step 5: Signup
You: "Now implement register following the same pattern as login."
Claude: [implements register following the conventions already established]
Why it works: Every step is verifiable. If something breaks at step 3, you don't lose steps 1-2. Claude has the full context of what it already built.
Pattern 2: Iterative Refinement
You have something that works but you want to improve it progressively.
# V1: Functional
You: "Create a function that parses CSV files and returns an array of objects."
Claude: [creates a basic parseCSV]
# V2: Robust
You: "Good, but it doesn't handle quotes inside fields, or line breaks
inside quoted fields. Add RFC 4180 support."
Claude: [improves the parser]
# V3: Performant
You: "It works, but for large files (>100MB) it runs out of memory
because it loads everything into an array. Convert it to a Readable stream
that emits objects one by one."
Claude: [refactors to streaming]
# V4: Typed
You: "Now make it generic: parseCSV<T>(file, schema) where schema
is a Zod object that validates and transforms each row."
Claude: [adds generics and validation]
Pattern 3: Conversational Debugging
Maybe the most valuable pattern. You use Claude Code as a debugging partner.
# Problem report
You: "I have a bug in production: users with special characters
in their name (ñ, á, ü) get a 500 when creating their profile.
The error is 'SQLITE_CONSTRAINT: NOT NULL constraint failed: users.slug'."
Claude: [reads the profile creation code, finds the slugify function]
Claude: "The problem is in src/utils/slugify.ts. The function uses a regex
that strips every non-ASCII character, leaving the slug empty
for names like 'José García'."
# Verification
You: "Show me what slugify('José García') currently produces."
Claude: [runs the code]
Claude: "It produces '' (empty string), which violates the NOT NULL constraint."
# Fix
You: "Fix it. The slug should transliterate: é→e, ñ→n, á→a, etc.
Use the transliteration library."
Claude: [installs transliteration, updates slugify]
# Post-fix verification
You: "Now show me the result for these names: 'José García',
'Müller Straße', '田中太郎'."
Claude: [runs tests with the three names, shows the results]
# Prevention
You: "Add unit tests for these edge cases and a regression test
for the original bug."
Claude: [creates the tests]
Pattern 4: Explore Before Acting
When you don't know exactly what you want, explore first.
# Exploration
You: "How is error handling organized in this project?
I want to understand the pattern before adding my own."
Claude: [analyzes the codebase in Explore mode]
Claude: "The project uses a centralized error pattern:
- Custom errors in src/errors/ extend AppError
- Middleware in src/middleware/errorHandler.ts catches them
- Each error has code, message, and statusCode
- Controllers don't try/catch, they just throw errors"
# Informed decision
You: "Ok, then create a NotFoundError and a ConflictError following
that exact pattern."
Claude: [creates the errors following the existing conventions]
One Message vs Several Messages
Use a single message when:
- The task is clear, atomic, and unambiguous
- You don't need to verify intermediate steps
- You already know exactly what you want
# A single message works fine here:
You: "Add an 'updatedAt' timestamp field with default NOW()
to the users table and create the migration."
Use several messages when:
- The task has more than 2-3 logical steps
- You need to verify before continuing
- You're not sure about the approach
- You're debugging
- You're building something complex
# Several messages are better here:
You: "I need a notification system. What options do we have
given that we use PostgreSQL and Node.js?"
Claude: [presents options]
You: "Let's go with PostgreSQL LISTEN/NOTIFY + WebSockets.
Start with the DB infrastructure."
Claude: [creates PG triggers and functions]
You: "Now the WebSocket server."
Claude: [implements the WS server]
# ... etc
The "do I need to see this first?" rule
Before each step, ask yourself: Do I need to check this step's output before Claude keeps going?
- Yes → Separate message
- No → It can go in the same message
Essential Commands for Multi-turn
/clear — Reset the context
Wipes the whole conversation. Claude forgets everything discussed. CLAUDE.md reloads automatically.
# When to use /clear:
# - The conversation drifted too far
# - The context got "poisoned" with failed attempts
# - You want to start a completely new topic without opening a new terminal
> /clear
# After /clear, Claude only has:
# - CLAUDE.md
# - Filesystem access
# - Nothing from the previous conversation
Tip: Before /clear, if there's something valuable in the conversation, ask Claude to save it to a file or to CLAUDE.md.
/compact — Compress without losing everything
Compresses the conversation while keeping the key points. It frees up context window space without wiping everything.
# When to use /compact:
# - The conversation is long but still relevant
# - Claude starts "forgetting" things from several messages back
# - The context indicator is high (>70%)
> /compact
# After /compact:
# - Claude has a summary of the conversation
# - The exact details are gone
# - The general direction survives
# - CLAUDE.md is still intact
The difference from /clear: /compact preserves a summary; /clear erases everything.
/effort — Control depth
Not exclusive to multi-turn, but it affects the quality of every answer.
> /effort high # Deep reasoning, slower
> /effort medium # Balanced (default)
> /effort low # Fast answers, less detail
A multi-turn pattern with /effort:
> /effort low
You: "Which files touch payment handling?"
Claude: [quick list of files]
> /effort high
You: "Now analyze src/payments/processor.ts. Are there race conditions
in how concurrent webhooks are handled?"
Claude: [deep analysis with extensive reasoning]
CLAUDE.md's Role in Multi-turn
Persistent context vs conversational context
There are two kinds of context in Claude Code:
| Type | Source | Lifetime | Survives /clear |
|---|---|---|---|
| Persistent | CLAUDE.md | Always | Yes |
| Conversational | Your messages | This session only | No |
CLAUDE.md is your insurance. If you run /clear, the project conventions survive because they live in CLAUDE.md, not in the conversation.
What goes in CLAUDE.md vs what you say in conversation
In CLAUDE.md — things that always apply:
# Conventions
- TypeScript strict, no any
- Tests with Vitest
- Errors extend AppError
- File names in kebab-case
In the conversation — things specific to this task:
You: "For this endpoint specifically, I want the response
to include cursor-based pagination, not offset-based."
Anti-pattern: Repeating what's already in CLAUDE.md
# BAD — repeating conventions in every message:
You: "Create an endpoint. Use TypeScript strict, no any, tests with Vitest,
errors that extend AppError, kebab-case for files..."
# GOOD — trusting CLAUDE.md:
You: "Create a GET /products endpoint with filters by category and price."
Claude already read CLAUDE.md. You don't need to repeat what's in there.
Comparisons and decisions
"How many messages should my task take?"
| Task complexity | Typical messages | Example |
|---|---|---|
| Trivial | 1 | Rename a variable, add a field |
| Simple | 2-3 | CRUD endpoint, utility function |
| Medium | 4-8 | Full feature with tests |
| Complex | 8-15 | System with several components |
| Architectural | 10-20+ | Big refactor, migration |
Productive vs unproductive conversation
Productive:
You: "Create a POST /orders endpoint" # Step 1: clear action
Claude: [creates the endpoint]
You: "Add body validation" # Step 2: refinement
Claude: [adds validation]
You: "The total field should be computed # Step 3: specific correction
server-side, not sent by the client"
Claude: [fixes it]
You: "Tests for happy path and 3 errors" # Step 4: testing
Claude: [creates the tests]
4 messages, complete result, every step verified.
Unproductive:
You: "Build me a backend" # Way too vague
Claude: [does something generic]
You: "No, not that" # No useful feedback
Claude: [tries again]
You: "Not that either" # No direction
Claude: [another random attempt]
You: "Forget it, I'll start over" # Frustration
4 messages, zero result, zero actionable feedback.
Common patterns
Pattern: "Checkpoint and Continue"
When you're many messages deep, consolidate before moving on.
You: "Before we continue, summarize what we've done so far and what's left."
Claude: "Done:
1. POST /orders endpoint with validation
2. Server-side total calculation
3. Unit tests
Remaining:
1. GET /orders endpoint with pagination
2. GET /orders/:id endpoint
3. Integration tests"
You: "Correct. Continue with GET /orders using cursor-based pagination."
Pattern: "Divide and Conquer"
For big tasks, split into mini-sessions.
# Session 1: Design
You: "I'm building an invoicing system.
Design the data structures and the API. Design only, no code."
Claude: [designs schema, endpoints, flows]
You: "Adjust it: invoices need line items, not just a total."
Claude: [adjusts the design]
# /clear or a new session
# Session 2: Model implementation
You: "Implement the Invoice model with line items.
Use Prisma. The schema is in [reference to the design]."
# Session 3: Endpoint implementation
# Session 4: Tests
# ...
Pattern: "Rubber Duck with Claude"
Use Claude as a rubber duck before asking it to implement.
You: "I need to implement retry logic for HTTP calls that fail.
What should I be thinking about?"
Claude: "Considerations:
1. Exponential backoff vs fixed delay
2. Which errors are retryable (5xx, network) vs not (4xx)
3. Maximum retries
4. Request idempotency
5. Circuit breaker to avoid cascading failures
6. Logging every attempt"
You: "Good, implement it with exponential backoff, max 3 retries,
only for 5xx and network errors. No circuit breaker for now."
First you explored the problem space. Now you give informed instructions.
Pitfalls and edge cases
Pitfall 1: The "yeah, yeah, keep going" without verifying
# Dangerous:
You: "Create the database"
Claude: [creates a schema with decisions you never reviewed]
You: "Ok, now the endpoints"
Claude: [builds on a schema that might be wrong]
You: "Ok, now the frontend"
Claude: [builds on endpoints that might be wrong]
# 30 minutes later you discover the schema is wrong → everything collapses
The fix: Verify each step before continuing, especially the foundational ones (schema, architecture, data structures).
Pitfall 2: Piling up irrelevant context
# The conversation started on auth, then you moved to payments,
# then to notifications. Claude has it all in context — and the
# auth context can "poison" the notification decisions.
# The fix: /clear or a new session when you change topic significantly
Pitfall 3: Prompts that override earlier decisions without saying so
You: "Use PostgreSQL for the database."
Claude: [implements with pg]
# ... 5 messages later ...
You: "Now connect to MongoDB."
# Do you want to replace PostgreSQL or add MongoDB? Claude doesn't know.
# Better:
You: "Replace PostgreSQL with MongoDB. Migrate all the data-access
code we created earlier."
Pitfall 4: Not using CLAUDE.md for repetitive patterns
If you keep telling Claude "use async/await, not callbacks" or "put the tests next to the file, not in a separate folder" — that belongs in CLAUDE.md, not in every conversation.
Pitfall 5: Endless conversations
After ~15-20 messages, quality tends to degrade. The context window fills up and Claude loses detail from the early messages. If you reach that point:
- Use
/compactto compress - Or start a new session with a clear summary
Complete worked example
Scenario: Build a search endpoint with filters
Say you have an Express + TypeScript + PostgreSQL project and you want to add a GET /api/products/search endpoint.
# ─── Message 1: Exploration ───
You: "Look at how the existing endpoints are implemented in
src/routes/ and src/controllers/. I want to understand the pattern before
adding a new one."
Claude: [analyzes the codebase]
Claude: "Pattern found:
- Routes in src/routes/{resource}.routes.ts
- Controllers in src/controllers/{resource}.controller.ts
- Services in src/services/{resource}.service.ts
- Validation with Zod in src/validators/{resource}.validator.ts
- They all use the asyncHandler wrapper for errors"
# ─── Message 2: Specification ───
You: "Create GET /api/products/search with these filters:
- q (free text, searches name and description)
- category (enum: electronics, clothing, food)
- minPrice, maxPrice (numbers)
- sortBy (name, price, createdAt)
- sortOrder (asc, desc)
- page, limit (offset pagination)
Only create the validator and the types for now."
Claude: [creates product-search.validator.ts with a Zod schema]
# ─── Message 3: Correction ───
You: "Two adjustments:
1. category should accept multiple values (array), not just one
2. limit should have a max of 100 and a default of 20"
Claude: [adjusts the validator]
# ─── Message 4: Service layer ───
You: "Now implement the service. The text search should use
PostgreSQL's ILIKE, not full-text search (we'll add that later)."
Claude: [creates product-search.service.ts with a query builder]
# ─── Message 5: Review and improvement ───
You: "The query builder has potential SQL injection in the sortBy field.
Don't use direct interpolation — validate against a whitelist."
Claude: [fixes it with a whitelist of allowed columns]
# ─── Message 6: Controller + Route ───
You: "Now the controller and the route. Follow the asyncHandler pattern
the other endpoints use."
Claude: [creates the controller and the route]
# ─── Message 7: Tests ───
You: "Tests with Vitest:
- Text search (match and no match)
- Category filter (one and multiple)
- Price range
- Pagination (first page, second page, last page)
- Sort in both directions
- Validation: limit > 100, negative price"
Claude: [creates the full test suite]
# ─── Message 8: Final verification ───
You: "Run the tests."
Claude: [runs vitest, shows the results]
8 messages. Result: A complete endpoint, validated, tested, following the project's conventions. Every step verified.
Practice exercises
Exercise 1: Incremental development of a module
Open Claude Code in an existing project (or create one with npm init). Build a logging module across 4+ messages, using the incremental development pattern:
- Ask for the interface/types only, first
- Ask for the basic implementation (console.log with formatting)
- Add levels (debug, info, warn, error)
- Add file output on top of the console
- Add file rotation when it goes over 10MB
Verify each step before moving to the next.
Guided solution
# Message 1
"Create a logging module in src/logger/. For now I only want
the TypeScript interface: a LogLevel type (debug, info, warn, error),
a LogEntry interface (timestamp, level, message, optional context),
and the Logger class signature with the methods debug(), info(), warn(), error()."
# Message 2
"Implement Logger. For now just console.log with this format:
[2026-02-28T10:30:00Z] [INFO] message — {context}"
# Message 3
"Add configuration: Logger should accept { minLevel: LogLevel }
in the constructor. If minLevel is 'warn', debug and info are ignored."
# Message 4
"Add file output. The constructor accepts { file?: string }.
If it's passed, also write to that file with fs.appendFile."
# Message 5
"Add rotation: if the file goes over 10MB, rename it to
app.log.1 and create a new one. Keep at most 3 rotated files."
Notice how each message is verifiable and builds on the previous one.
Exercise 2: Giving effective feedback
Ask Claude Code to create a formatCurrency(amount, locale) function. Deliberately give very little detail in the first message. Then practice the 5 types of feedback:
- Correction: "The MXN format should be $1,000.00, not $1000"
- Redirection: "Don't use Intl.NumberFormat, I want a manual implementation"
- Refinement: "Add support for cryptocurrencies (BTC with 8 decimals)"
- Confirmation: "Perfect. Now the tests."
- Exploration: "How do we handle currencies with no decimals (JPY)?"
Guided solution
# First message (deliberately vague):
"Create a formatCurrency function that formats numbers as currency."
# Claude will do something generic. Now practice feedback:
# Correction:
"The result for formatCurrency(1000, 'es-MX', 'MXN') should be
'$1,000.00' but I get '$1000'. Add a thousands separator."
# Redirection:
"You're using Intl.NumberFormat. I'd rather have a manual implementation
because I need full control over the format. Rewrite it without Intl."
# Refinement:
"It works well for fiat currencies. Add support for cryptocurrencies:
BTC with 8 decimals, ETH with 18 decimals (but show max 6)."
# Confirmation + next:
"Perfect, that's exactly what I needed. Now create tests
for USD, MXN, EUR, JPY, BTC and ETH."
# Exploration:
"How should we handle very small crypto amounts? For example,
0.00000001 BTC — scientific notation or all the decimals?"
The goal is to notice the quality difference in Claude's answers when you give specific vs vague feedback.
Exercise 3: The debugging pattern
Deliberately introduce a bug into your code (or use an existing one). Start a debugging conversation with Claude Code:
- Describe the symptom, not the cause
- Let Claude investigate
- Ask it to explain the root cause
- Ask for the fix
- Ask for a regression test
Guided solution
# Introduce a bug. Example: an off-by-one in pagination:
# In your code, page 2 repeats the last item from page 1.
# Message 1: Symptom
"I have a bug in the GET /products pagination. When I request
page=2&limit=10, the first item on page 2 is the same as
the last item on page 1. The endpoint is in
src/controllers/product.controller.ts."
# Message 2: Ask it to investigate
"Check the offset calculation in the service and in the SQL query."
# Claude should find something like:
# offset = (page - 1) * limit ← correct
# vs
# offset = page * limit - limit - 1 ← subtle bug
# vs
# OFFSET $1 with the wrong value
# Message 3: Confirm the diagnosis
"Can you confirm it by running a query with page=1,limit=3 and
page=2,limit=3 to see the overlap?"
# Message 4: Fix
"Fix it."
# Message 5: Regression test
"Create a test that specifically checks there's no overlap
between consecutive pages."
Exercise 4: /clear vs /compact
Run this experiment to understand the difference:
- Start a conversation. Ask Claude to analyze a file
- Send 3-4 refinement messages
- Run
/compact. Ask it "what have we done?" - Run
/clear. Ask it "what have we done?"
Compare the answers. After /compact, Claude has a summary. After /clear, it knows nothing.
What to watch for
After /compact:
- Claude remembers the general topic
- It can refer to the decisions that were made
- It loses specific code details
- CLAUDE.md is still available
After /clear:
- Claude does NOT remember anything from the conversation
- It only has CLAUDE.md as context
- It's like starting over (but without closing the terminal)
- Useful when the conversation got "poisoned"
Takeaway: Use /compact when you want to preserve context but free up space. Use /clear when you want a clean slate.
Exercise 5: Productive vs unproductive conversation
Do this exercise with any real task. First, try to complete it the worst way possible (vague prompts, no feedback, no direction). Note how many messages it took and whether the result was any good. Then do it the best way possible (incremental, specific feedback, verification). Compare:
- Number of messages
- Quality of the result
- Total time
- Frustration
What to expect
Typically:
- Bad approach: 8-12 messages, mediocre result, lots of rework, high frustration
- Good approach: 4-6 messages, good result, clean progression, satisfaction
The good approach usually takes FEWER messages for a BETTER result. Being specific isn't slower — it's faster.
Exercise 6: Multi-turn with CLAUDE.md
- Create or update CLAUDE.md with 5 conventions specific to your project
- Start a new Claude Code session
- Ask it to create something without mentioning the conventions
- Check that Claude followed the CLAUDE.md conventions
- Run
/clear - Ask for the same thing again — check that the conventions still apply
What to watch for
This demonstrates that CLAUDE.md is persistent context. It doesn't matter whether you run /clear or /compact or start a new session — CLAUDE.md is always there.
If Claude did NOT follow a CLAUDE.md convention, it could be because:
- The convention is worded ambiguously
- There's a conflict with another instruction
- CLAUDE.md is too long and the convention gets "lost"
That's feedback for improving your CLAUDE.md.
Summary
- Multi-turn conversations are the natural way to work with Claude Code. Don't try to cram everything into a single prompt.
- Feedback is your main tool. Be specific, give direction, correct with context.
- 5 types of feedback: Correction, redirection, refinement, confirmation, exploration.
- Key patterns: Incremental development, iterative refinement, conversational debugging, explore before acting.
- One message vs many: If you need to verify a step before the next one, use separate messages.
/clearwipes everything./compactsummarizes and compresses. Use them strategically.- CLAUDE.md is persistent context that survives
/clearand/compact. Put there what always applies. - Verify the foundational steps (schema, architecture) before building on top of them.
- ~15-20 messages is the practical limit. After that, compact or start a new session.
Additional resources
- Claude Code Best Practices — Anthropic Docs — Recommended workflows and context management
- Claude Code Interactive Mode — Shortcuts, task management, and session management
- Claude Code CLI Reference — Available commands, flags, and options
- Claude Code Memory System — CLAUDE.md, auto memory, the 6-level hierarchy
- Prompting Best Practices — Anthropic Docs — How to structure instructions for Claude
- Claude Code Overview — What it is, how it works, available platforms