Loop-Engineering: Iterative Goal Development with Claude Code
1. The Problem: Why “Ask-and-Forget” Doesn’t Work
Classic workflow:
"Write me a REST API endpoint with validation"
→ Claude generates code
→ 60% usable, 40% needs reworking
→ Frustration: wrong abstractions, missing error handling, not testable
The Loop concept solves this: Instead of “just asking”, we follow a structured Plan-Implement-Evaluate-Refine cycle that ensures the generated code actually meets your requirements.
2. What Is Loop-Engineering?
Loop-Engineering = Iterative goal development with AI-assisted code.
The anatomy of a loop:
- Goal Definition – Explicit, measurable, precise (e.g.
/goalin Claude Code) - Implementation – Claude generates code based on the goal
- Evaluation – You test, verify, and identify gaps
- Feedback & Refinement – You provide structured feedback; the next loop begins
Claude Code’s /goal Feature:
/goal Write an email validator with regex
Requirements:
- Recognise valid standard emails
- Reject: no @, multiple @, invalid TLDs
- At least 5 unit tests with pytest
This is better than “write a validator”, because it gives the AI a clear framework.
3. DOs & DON’Ts: Practical Guidelines
✅ DOs
DO: Formulate SMART goals
- Poor: “Add a feature”
- Good: “Write a validator for email addresses with unit tests that validates RFC 5322 basic format”
DO: Test immediately after implementation
- Run the generated tests
- Test edge cases manually
- Verify against your requirements
DO: Make feedback explicit
- Poor: “That’s wrong”
- Good: “Test fails on null input. Error handling is too generic — errors must be distinguishable”
DO: Keep loops iterative and modular
- Loop 1: Validation logic
- Loop 2: Unit tests
- Loop 3: Integration into existing system
❌ DON’Ts
DON’T: Vague requirements
- Vague: “Do something with error handling”
- Be specific about exception types, logging levels, etc.
DON’T: Accept code without verification
- Don’t simply take the code as “done”
- Unit tests must be green
- Your review is part of the loop
DON’T: Interrupt the loop with off-topic requests
- Loop: “Write a validator”
- Mid-way: “Oh, and optimise my database query”
- → New loop! Finish the current one first.
DON’T: Goals that are too large for a single loop
- Too large: “Write an entire REST API with auth, validation, logging, and monitoring”
- Better: 4 separate loops for the complexity
4. How-To: Practical Recipes with Code
Recipe 1: Python – Email Validator (Beginner)
Goal: Simple email validator with tests
Goal Definition:
/goal Write an email validator in Python
Requirements:
- Validates standard email format (RFC 5322 simplified)
- Rejects: no @, multiple @, no domain after @
- Returns True/False
- At least 5 unit tests with pytest
- Error handling for None/empty strings
Claude Code Workflow:
- Start Claude Code with
/goal - Claude generates
email_validator.py+test_email_validator.py - You run:
pytest test_email_validator.py - If errors occur: provide feedback, start new loop (Loop 2)
Expected Output:
# email_validator.py
import re
def validate_email(email: str) -> bool:
if not email or not isinstance(email, str):
return False
pattern = r'^[^@]+@[^@]+\.[^@]+$'
return bool(re.match(pattern, email))
# test_email_validator.py
import pytest
from email_validator import validate_email
def test_valid_email():
assert validate_email("user@example.com") == True
def test_no_at_symbol():
assert validate_email("userexample.com") == False
# ... further tests
Feedback Cycle (Loop 2): If tests fail, e.g.:
/goal Improve the email validator
Problem: Test for international domains fails
Requirement: Support for umlauts in the local part
✓ Loop successful when:
- ✅ All tests are green
- ✅ Your requirements are met
- ✅ Code is readable and maintainable
Recipe 2: React – Todo App State Management (Intermediate)
Goal: Redux/Context-based todo state with add/delete/filter
Why multiple loops are needed:
- Loop 1: State schema + actions
- Loop 2: Reducer logic + tests
- Loop 3: UI integration + LocalStorage
Loop 1 – State Schema:
/goal Design a Redux state schema for a todo app
Requirements:
- Todos (array): { id, title, completed, createdAt }
- UI State: { filter: 'all' | 'active' | 'completed' }
- Actions: ADD_TODO, DELETE_TODO, TOGGLE_TODO, SET_FILTER
- Use Redux Toolkit (createSlice)
Expected Output:
// todoSlice.js
import { createSlice } from '@reduxjs/toolkit';
const initialState = {
todos: [],
filter: 'all'
};
const todoSlice = createSlice({
name: 'todos',
initialState,
reducers: {
addTodo: (state, action) => {
state.todos.push({
id: Date.now(),
title: action.payload,
completed: false,
createdAt: new Date()
});
},
deleteTodo: (state, action) => {
state.todos = state.todos.filter(t => t.id !== action.payload);
},
// ... further reducers
}
});
Loop 2 – Tests & Edge Cases:
/goal Write comprehensive Jest tests for the todoSlice
Requirements:
- Test ADD_TODO with various inputs
- Test DELETE_TODO for non-existent IDs
- Test FILTER_TODOS
- Edge cases: duplicate IDs, null titles, etc.
Loop 3 – UI Integration:
/goal Integrate the Redux state into the React component
Requirements:
- TodoList component (read from store)
- AddTodo input + button
- Filter buttons (All / Active / Completed)
- Persist state to localStorage
- Tests with React Testing Library
Feedback process here: After Loop 3: “The filter works, but localStorage is not loaded on init” → Loop 4: Fix for initialisation.
Recipe 3: Rust – WebSocket Handler (Advanced)
Goal: Async WebSocket message handler with error handling
Why it’s complex:
- Architecture (tokio runtime)
- Async/await patterns
- Error handling (multiple error types)
- Broadcasting between clients
- Graceful shutdown
Loop 1 – Architecture & Basic Handler:
/goal Design an async WebSocket handler in Rust with tokio
Requirements:
- Use tokio for async runtime
- WebSocket library: tokio-tungstenite
- Basic handler for a single connection
- Logging with tracing
- Error handling with thiserror
- Structured with Result<T, AppError>
Expected Output:
use tokio::net::{TcpListener, TcpStream};
use tokio_tungstenite::accept_async;
use futures::{StreamExt, SinkExt};
#[tokio::main]
async fn main() {
let listener = TcpListener::bind("127.0.0.1:8080").await.unwrap();
while let (stream, _) = listener.accept().await.unwrap() {
tokio::spawn(handle_connection(stream));
}
}
async fn handle_connection(stream: TcpStream) {
match accept_async(stream).await {
Ok(ws_stream) => {
let (mut ws_sender, mut ws_receiver) = ws_stream.split();
while let Some(msg) = ws_receiver.next().await {
// Handle message
}
},
Err(e) => eprintln!("WS error: {}", e),
}
}
Loop 2 – Broadcasting & Message Routing:
/goal Extend the handler for multi-client broadcasting
Requirements:
- Use Arc<Mutex<Vec<...>>> for client management
- Broadcast all messages to all connected clients
- Handle disconnects gracefully
- Use tokio::sync::broadcast for efficiency (optional: extend)
Loop 3 – Comprehensive Error Handling:
/goal Harden the error handling
Requirements:
- Custom error type with thiserror
- Distinguish: connection errors, message errors, handler panics
- Logging: info for connects/disconnects, error for failures
- Graceful degradation (one client crash ≠ server crash)
- Unit tests for error paths
Feedback loop example: After Loop 3: “When a client disconnects, the server hangs briefly” → Loop 4: “Optimise client removal with tokio::select! for timeout”
5. Best Practices & Pitfalls
Avoiding Hallucinations
- Problem: Claude generates a “safe” solution that fails during testing
- Solution: Specific, testable requirements in the loop
- Good: “Must handle all RFC-5322 cases, validated with these 5 test cases”
- Poor: “Write a good validator”
Loop Breakdown
- When does a loop stop working?
- Goal becomes too large (~5+ requirements at once)
- Feedback is too multi-layered (“And also logging, and also DB refactoring”)
- → Split into smaller loops
Token Efficiency
- Long conversations accumulate weight through history
- Best practice: Start a new conversation after ~10 loops or when the context threshold is reached
- Export key insights to a gist/file for the next conversation
Team Context & Documentation
- Loops as documentation:
- Each loop explicitly defines “what must this code do”
- Ideal for onboarding new developers
- PR reviews can be validated against loop requirements
Debugging Loops
- When Claude gets “stuck” in a loop:
- Redefine the goal (more precise, smaller)
- Add a constraint (“Maximum 100 lines of code”)
- Or: provide a backup strategy (“If that fails, try approach B”)
6. Integration into Your Workflow
Claude Code & Windsurf
- Claude Code:
/goalfeature for explicit loop definition - Windsurf: Similarly usable, but less structured; define loops manually
Comparison with Other Agentic Patterns
| Pattern | Approach | Use Case |
|---|---|---|
| Loop-Engineering | Iterative, goal-focused, feedback-driven | Individual features, code quality critical |
| GSD (Graph State Decomposition) | Planning graph, multi-step, structured | Complex multi-file refactorings |
| Conductor | Orchestration of multiple agents | Team scenarios, parallel tasks |
Loop-Engineering is ideal for:
- Teaching at university of applied sciences level (feedback loops = learning)
- Individual, well-defined features
- Code quality without the need for an explicit planning tool
- Rapid iteration with a high degree of control
7. Resources & Next Steps
Anthropic Documentation:
- Claude Code Official Docs –
/goalFeature & Best Practices - API Documentation – Batch Processing, Tool Use for loops
- Prompt Engineering Guide – Optimising goal definition
Your next steps:
- Pick a recipe: Start with Recipe 1 (Email Validator)
- Try Loop-Engineering: Follow the
/goalstructure - Document your feedback: What works, what doesn’t
- Scale up: Integrate loops into your FFHS modules
Conclusion
Loop-Engineering is not a complex agentic architecture — it is a pragmatic shift in mindset:
- From “ask the AI and hope” to “define a goal, evaluate, learn, refine”
- Feedback is not a disruption, but the core mechanism
- Scalable from beginner (Recipe 1) to advanced developer (Recipe 3)
For FFHS students, this means: they learn to work with AI tools as they would with a code review partner — not as a magic box, but as an iterative sparring partner.
Author: Daniel Senften, Talent Factory GmbH
Last Updated: June 2026