# Prompt Patterns for Developers
Most developers treat prompts like magic spells—type something vague, hope for lightning. That’s not engineering. That’s guessing.
After two years of building AI-powered features into production apps, I’ve learned that prompt engineering is really just API design for language models. The same principles apply: be explicit, handle edge cases, version your inputs, and test the outputs.
This article covers six prompt patterns I use daily. No hype. Just patterns that work.
## The Role Pattern: Define the Persona Explicitly
The most basic pattern, and the one most people get wrong. Don’t just say “you are a helpful assistant.” That’s useless. Define what the model should sound like and what it should know.
“`python
SYSTEM_PROMPT = “””You are a senior backend engineer reviewing code.
– Focus on performance, security, and maintainability
– Assume the codebase is Python/Django
– Never suggest premature optimization
– Point out specific line numbers when referencing issues
– Reply with actionable feedback, not general advice”””
“`
The key insight: specificity breeds quality. “Senior backend engineer” tells the model to use technical terminology. “Assume Python/Django” sets context without forcing you to repeat it. “Never suggest premature optimization” is a constraint that shapes every response.
You’ll notice I put constraints in the negative (“never,” “assume the codebase is”). Models often ignore positive instructions but respect negative constraints. Use both.
## Chain-of-Thought: Make the Model Show Work
When you need accurate reasoning—not just correct answers—ask the model to walk through its logic. This is chain-of-thought prompting, and it’s essential for anything beyond simple Q&A.
“`python
def analyze_code_with_cot(code_snippet: str) -> dict:
prompt = f”””Analyze this code for bugs. Show your reasoning step by step.
Code:
“`
{code_snippet}
“`
First, identify what the code is trying to do.
Second, trace the execution flow.
Third, list potential issues with specific line references.
Finally, rate severity (critical/high/medium/low).
Output your final answer in JSON format with keys: analysis, issues[], severity_rating”””
response = client.chat.completions.create(
model=”gpt-4o”,
messages=[
{“role”: “system”, “content”: “You are a code reviewer. Always show reasoning before giving final answers.”},
{“role”: “user”, “content”: prompt}
],
temperature=0.1 # Lower temperature for more consistent reasoning
)
return json.loads(response.choices[0].message.content)
“`
Three things make this work:
1. **Explicit steps** — “First, identify… Second, trace… Third, list…”
2. **Lower temperature** — 0.1 keeps the model focused rather than creative
3. **Structured output request** — asking for JSON forces the model to organize thoughts
The model won’t always follow your steps perfectly. But when it deviates, you’ll notice—and can adjust the prompt.
## Few-Shot Learning: Show, Don’t Just Tell
Sometimes explaining what you want takes more words than just showing an example. Few-shot learning means providing input-output pairs that demonstrate the pattern you want.
“`python
FEW_SHOT_EXAMPLES = “””
Example 1:
Input: “function to calculate fibonacci”
Output: “def fibonacci(n):\n if n <= 1:\n return n\n return fibonacci(n-1) + fibonacci(n-2)"
Example 2:
Input: "function to reverse a string"
Output: "def reverse_string(s):\n return s[::-1]"
Example 3:
Input: "function to check if list is sorted"
Output: "def is_sorted(lst):\n return all(lst[i] <= lst[i+1] for i in range(len(lst)-1))"
"""
prompt = f"""Given a function description, output only the Python code.
{FEW_SHOT_EXAMPLES}
Now generate for:
Input: "function to find max value in dictionary"
Output:"""
```
The model learns the format from examples. This is especially useful for:
- Enforcing specific output formats
- Getting consistent code style
- Handling domain-specific transformations
Three to five examples usually suffice. More than that and you're just adding token cost without improving quality.
## The Template Pattern: Parameterize Your Prompts
Build prompts like you build functions—parameterize the variable parts. This makes prompts testable, versionable, and reusable.
```python
def build_code_explanation_prompt(code: str, language: str, level: str) -> str:
“””Template for explaining code at different detail levels.”””
return f”””Explain this {language} code to a {level} developer.
Code:
“`{language}
{code}
“`
Requirements:
– Explain what each line does
– Highlight any potential issues
– Suggest improvements if any exist
– Use {level}-appropriate terminology”””
# Usage
explain_prompt = build_code_explanation_prompt(
code=”def foo(x): return x*2″,
language=”python”,
level=”junior”
)
“`
This pattern becomes powerful when you:
– A/B test prompts with different parameters
– Track which prompt versions perform better
– Build prompt libraries for your team
Store prompts in version control. Treat them like code, because they are.
## Structured Output: Force the Schema
Never parse free-text responses with regex if you can avoid it. Ask for structured output directly.
“`python
from pydantic import BaseModel
class CodeReview(BaseModel):
issues: list[dict] # [{“line”: 5, “severity”: “high”, “description”: “…”}]
suggestions: list[str]
overall_quality: str # “excellent”/”good”/”needs_work”
prompt = “””Review this code and return structured feedback.
Code:
“`python
def process_user_data(users):
for user in users:
print(user.name)
# missing error handling
“`”””
response = client.chat.completions.create(
model=”gpt-4o”,
messages=[{“role”: “user”, “content”: prompt}],
response_format={“type”: “json_object”} # Forces JSON
)
review = CodeReview.model_validate_json(response.choices[0].message.content)
“`
This works because:
– You define exactly what you need
– The model has less room to wander
– Pydantic validates the output automatically
– Your code becomes type-safe
Not all models support structured output. GPT-4o and Claude 3.5 support `response_format`. For other models, ask for JSON explicitly and parse carefully.
## The Iterative Refinement Pattern
First drafts are rarely final drafts. Build feedback loops into your prompts.
“`python
def refined_code_generation(initial_request: str, max_iterations: int = 3) -> str:
“””Generate code, then refine based on feedback.”””
current_code = generate_code(initial_request)
for i in range(max_iterations):
issues = check_code_quality(current_code)
if not issues:
return current_code
feedback_prompt = f”””The following code has issues:
Code:
“`python
{current_code}
“`
Issues found:
{chr(10).join(f”- {issue}” for issue in issues)}
Fix these issues while preserving the original functionality.”””
current_code = generate_code(feedback_prompt)
return current_code # Return best effort after max iterations
“`
This pattern mirrors how developers actually work:
1. Generate initial solution
2. Identify problems
3. Apply fixes
4. Repeat until acceptable
The loop prevents prompt bloat. Instead of stuffing every possible constraint into one prompt, you layer feedback.
—
## Key Takeaways
– Treat prompts as API design: be explicit, version them, test outputs
– Chain-of-thought works best with explicit step numbering and lower temperature
– Few-shot examples beat verbose instructions for format enforcement
– Parameterize prompts like functions—makes them reusable and testable
– Always request structured output when possible; parsing free text is fragile
– Build iterative refinement into prompts rather than cramming everything into one request
## Next Steps
1. **Audit your current prompts** — Pull whatever prompts you’re using and look for vagueness. Replace “helpful assistant” with actual role definitions.
2. **Pick one pattern to try** — If you’re not using structured output, start there. It’s the highest-impact change for production systems.
3. **Build a prompt library** — Create a shared repo or module for your team’s prompts. Version control them. Test the outputs.
4. **Measure, then optimize** — Track success rates on AI-generated outputs. If 30% of code suggestions fail, your prompt needs work—not the model.
Prompt engineering isn’t a magic skill. It’s engineering. Treat it accordingly.

