Prompt Engineering Patterns for Software Development
Not all prompts produce good code. These battle-tested patterns will help you get better results from AI assistants every time you open a chat.
Prompt engineering has become one of the most valuable skills in modern software development. The difference between a prompt that produces working, maintainable code and one that generates a broken mess is often subtle — a few words of context, a constraint stated early, or an example that frames the problem correctly. This article collects the patterns that consistently produce good results, organized by the type of task you are solving.
These patterns are not theoretical. They are distilled from thousands of real interactions with AI coding assistants across production codebases. Each pattern includes a concrete example and an explanation of why it works, so you can adapt it to your own tools and workflows.
Pattern 1: The Context Sandwich
The most common mistake developers make when prompting AI is providing too little context. They ask for a function without describing the surrounding system, the constraints, or the conventions they expect. The AI then makes reasonable assumptions that are wrong for their specific codebase, and the result needs heavy editing.
The Context Sandwich fixes this by structuring every prompt into three layers: what you are building, what constraints apply, and what success looks like.
- Top layer — The goal: Describe what you want in one clear sentence. This is the only part most developers include.
- Middle layer — The constraints: List the non-negotiables. Library versions, style conventions, performance requirements, edge cases that must be handled.
- Bottom layer — The verification: Describe how you will know the result is correct. A test case, an expected output format, or a specific behavior to confirm.
// Weak prompt
const result = await ai.generate("Write a function that sorts an array of users by name");
// Context Sandwich prompt
const result = await ai.generate([
"Create a compare function for sorting an array of user objects by lastName, then firstName.",
"Constraints: case-insensitive, handles missing names, TypeScript, no external libraries.",
"Verify: users.sort(compare).map(u => u.lastName) should return ['Adams', 'adams', 'Brown'].",
].join("\n\n"));The Context Sandwich works because it reduces ambiguity at every level. The AI knows what you want, what you will not accept, and how you will evaluate the result. This single pattern eliminates most of the back-and-forth that wastes time in AI-assisted development.
Pattern 2: The Negative Constraint
AI models are generally good at understanding what you want. They struggle more with understanding what you do not want. A prompt like write a login form will produce something reasonable, but it might include password complexity rules your design does not use, or a remember-me checkbox your product manager removed last sprint.
The Negative Constraint pattern explicitly states what the AI should avoid. It is more effective than hoping the model will guess your exclusions correctly.
// Without negative constraints
"Build a user settings page with tabs for profile, security, and notifications."
// With negative constraints
"Build a user settings page with tabs for profile, security, and notifications.
DO NOT include: email verification flows, password strength meters, or social login buttons."Negative constraints work best when placed at the end of the prompt, after the positive description. This mirrors how humans process instructions — we need to understand what is asked before we can understand what is excluded. The AI follows the same pattern: it builds the mental model from the positive description, then applies the exclusions as filters on top.
Pattern 3: The Example Template
When you need consistent output across multiple AI interactions — generating documentation, writing test cases, or formatting API responses — telling the AI what to do is less effective than showing it. The Example Template pattern provides one or two complete examples of the desired output and then asks for more in the same format.
This is the closest thing to few-shot learning available through chat interfaces. A well-chosen example communicates format, tone, level of detail, and implicit conventions that would take paragraphs to describe explicitly.
// Example Template for generating error messages
const example = {
code: "ERR_AUTH_TOKEN_EXPIRED",
message: "Your session has expired. Please log in again.",
severity: "warning",
action: "redirect:/login",
};
const result = await ai.generate([
"Generate 5 more error objects in the same format as this example:",
JSON.stringify(example, null, 2),
"Cover: rate limiting, permission denied, not found, validation error, server error.",
].join("\n\n"));The key to this pattern is choosing the right example. It should be representative but not edge-case-heavy — you want the AI to generalize from your example, not replicate its quirks. Two examples are better than one when the output format has multiple variations, but more than three examples rarely add value and start consuming your context window.
Pattern 4: The Iterative Refinement Loop
Asking an AI for a final, perfect result in one prompt is like asking a junior developer to ship production code without any iteration. It sometimes works, but it usually produces something that needs substantial rework. The Iterative Refinement Loop treats the AI interaction as a conversation, starting broad and narrowing down.
- Round 1 — Generate a broad solution or skeleton. Do not ask for details yet.
- Round 2 — Review the output and give specific direction. This part is good, change that part, add this case.
- Round 3 — Polish details. Error handling, edge cases, naming, documentation.
Each round gives the AI more context about what you actually want, because your feedback is specific to the code it just produced. This is far more efficient than trying to anticipate every detail in the first prompt. The AI also benefits because it can focus on one level of the problem at a time — first the structure, then the specifics, then the polish.
Pattern 5: The Role Prefix
AI models are trained on text from many domains, and they can adopt different personas when prompted. The Role Prefix pattern explicitly assigns a role and perspective to the AI before asking for code, which primes it to produce output appropriate to that context.
// Generic
"Review this function for security issues."
// Role-prefixed
"You are a security engineer reviewing a pull request. Focus on:
- Injection vulnerabilities in the SQL query construction
- Missing input validation on the user ID parameter
- Hardcoded credentials or secrets
Review this function:"The Role Prefix pattern is especially powerful for review tasks, refactoring, and documentation generation. When you need code that follows accessibility standards, prefix with You are an accessibility specialist. When you are optimizing for performance, prefix with You are a performance engineer. The AI adjusts its reasoning and output to match the perspective you request.
Pattern 6: The Output Contract
One of the most frustrating experiences with AI coding is getting a response that looks correct but subtly changes the output format, adds explanatory text you did not ask for, or omits critical parts. The Output Contract pattern prevents this by specifying exactly what the response should contain and, just as importantly, what it should not contain.
"Generate a React component for a file upload button.
Output contract:
- Return ONLY the component code in a single code block
- No explanation text before or after
- Include TypeScript props interface
- Include CSS module import
- Do NOT include usage examples or test files
- File name: FileUpload.tsx"The Output Contract is particularly valuable when you are piping AI output into another tool or workflow. If the response always follows the same structure, you can parse it reliably. This pattern is the foundation for building automated AI-assisted pipelines that require consistent formatting.
Pattern 7: The Context Window Budget
Every AI model has a limited context window. When you paste in too much code, documentation, or conversation history, the model starts forgetting or hallucinating. The Context Window Budget pattern treats the context window as a scarce resource and allocates it deliberately.
- Allocate 60 % of your budget to the most relevant code and task description.
- Allocate 20 % to examples and constraints that clarify the task.
- Reserve 20 % for the AI's response space — if the window is full, the output will be truncated.
When you have more context than fits in the window, prioritize the most specific and relevant parts. A focused prompt with one carefully chosen file is more effective than a broad prompt with five loosely related files. The AI can always ask for more context if it needs it — give it permission to do so early in the conversation.
A focused prompt with one carefully chosen file consistently outperforms a scattered prompt with five files the AI can barely see.
Pattern 8: The Scaffold-Then-Detail Sequence
Complex features have many moving parts. Asking an AI to generate them all at once produces a tangled result where the state management, API calls, and UI rendering are hard to separate. The Scaffold-Then-Detail sequence breaks the problem into two phases.
First, ask the AI to generate a scaffold: the type definitions, interfaces, and high-level structure of the feature without implementation details. Review this scaffold and confirm it matches your mental model. Only then ask the AI to fill in the details — implementation, error handling, edge cases — one layer at a time.
// Phase 1 — Scaffold
"Design the type structure for a multi-step checkout flow. List the interfaces, enums, and state machine transitions. Do not write implementation yet."
// Phase 2 — Detail (after reviewing scaffold)
"Implement the checkout state machine from the types we agreed on. Include transition validation and error states."This pattern prevents the most expensive kind of rework: changing the structure after the implementation is done. By agreeing on the scaffold first, you ensure the AI builds on a foundation that matches your architecture, not an imagined one.
Putting the patterns together
These eight patterns work best when combined. A typical session might start with the Context Sandwich to frame the request, use Negative Constraints to exclude unwanted approaches, follow the Iterative Refinement Loop to converge on the right solution, and close with an Output Contract to get a clean, parsable result.
The more you practice these patterns, the more natural they become. Within a few days, you will find yourself instinctively structuring prompts as Context Sandwiches and adding Negative Constraints without thinking. That is when AI-assisted development shifts from a sometimes-helpful tool into a consistently reliable part of your workflow.
Each interaction with an AI assistant is an investment. A well-structured prompt takes thirty extra seconds to write but saves hours of back-and-forth. Over a hundred interactions, those thirty seconds compound into days of saved time — and the difference between frustration and flow.
