Article 3 of 6

Prompt Engineering for Developers (Not Researchers)

Practical prompt patterns that engineers actually use every day.

13 minIntermediate
Key Takeaway

Prompt engineering for developers is not a research discipline — it's just clear communication. The same skills that make you good at writing Jira tickets, design docs, and code review comments make you good at prompting. Vagueness is the enemy. Five practical patterns cover 90% of daily developer use cases: the Context Sandwich, Incremental Refinement, the Rubber Duck, the Specification Prompt, and the Review Request. Build a personal prompt library, and you'll stop thinking about prompting within a month — it becomes muscle memory.


Last year, I watched a senior engineer on my team spend twenty minutes crafting a single prompt. It was a work of art — carefully structured, paragraphs of context, specific output formatting instructions, explicit constraints. He hit enter, got back a database migration script that slotted directly into our codebase, and saved himself two hours of tedious work.

The next desk over, another engineer typed "write me a migration script" and got back something that wouldn't compile against our schema. He declared AI "useless for real work" and went back to writing it by hand.

Same tool. Same underlying model. Completely different outcomes. The difference wasn't intelligence or seniority. It was one practical skill: knowing how to communicate with AI so it actually helps you.

Kill the Academic Baggage First

When most developers hear "prompt engineering," they picture researchers fine-tuning token probabilities. They think of papers with titles like "Chain-of-Thought Prompting Elicits Reasoning in Large Language Models." They tune out — reasonably — because that sounds like it has nothing to do with shipping features on a Tuesday.

Here's the reality: prompt engineering for developers is just communication. It's the same skill you use when writing a clear Jira ticket, drafting a useful code review comment, or explaining a bug to a colleague over Slack. You're giving instructions to a capable system that has zero context about your specific situation.

The engineers who get the most out of AI tools didn't memorise research papers. They learned five or six practical patterns and use them reflexively — the same way you reflexively write defensive code or add error handling. That's all this is.

The Number One Mistake: Vagueness

Before we get to the patterns, let's be direct about the single biggest mistake I see developers make consistently.

They are too vague.

This is a terrible prompt:

"Write me a function to process orders."

What language? What framework? What does "process" mean in your domain? What's the order data structure? What are the error cases? You would never walk up to a contractor and say "build me a room" without specifying dimensions, materials, and location. But developers do the equivalent with AI constantly — and then wonder why the output is useless.

A marginally better version:

"Write a TypeScript function that takes an Order object with fields orderId, items, and totalAmount, validates that totalAmount matches the sum of item prices, and throws a ValidationError if it doesn't."

Now the AI knows the language, the data shape, the business logic, and the expected failure mode. The output goes from "random guess" to "useful starting point." But we can do significantly better than that — and that's where the patterns come in.

Pattern 1: The Context Sandwich

This is the pattern I use most often — probably ten times a day. The structure is simple: context on top, task in the middle, constraints on the bottom.

Weak prompt:

"Write a retry function."

Context Sandwich version:

"I'm working on a Node.js microservice that calls an external payment API. The API occasionally returns 503 errors during peak hours, and we currently have no retry logic, which causes order failures.

Write a retry utility function that wraps async API calls.

Constraints: Use exponential backoff starting at 200ms. Maximum 3 retries. Must work with our existing Axios instance. Should log each retry attempt using our Winston logger. TypeScript, no external retry libraries."

The context tells the AI why this code exists. The task tells it what to build. The constraints tell it how to build it within your actual environment. Every piece of information you add reduces ambiguity, and ambiguity is what turns AI output into a rewrite project.

I think of it as a sandwich because the meaty task — the actual thing you want — is held between two pieces of information that keep it grounded in your real world, not in a generic demo.

Pattern 2: Incremental Refinement

Not every problem is clear enough to specify upfront. Sometimes you're exploring, and you genuinely don't know what you want yet. The Incremental Refinement pattern embraces that uncertainty instead of fighting it.

Step 1 — Start broad:

"What are the common approaches for implementing real-time notifications in a React app with a Django backend?"

Step 2 — Narrow based on what you learned:

"WebSockets with Django Channels looks like the right fit. Walk me through the architecture for handling 10,000 concurrent connections on a single server."

Step 3 — Get specific:

"Write the Django Channels consumer class for handling notification subscriptions. We use Redis as the channel layer and authenticate via JWT tokens passed in the WebSocket handshake."

Each step builds on the previous response. You're having a conversation, not issuing a single command. This is how you use AI for architectural exploration without ending up with a pile of irrelevant code that doesn't fit your system.

The critical insight: you don't need to know exactly what you want before starting the conversation. You just need to refine iteratively rather than accepting the first generic answer and moving on.

Pattern 3: The Rubber Duck

Every developer knows rubber duck debugging — you explain your problem out loud to a rubber duck on your desk, and halfway through the explanation, the solution clicks. AI is the most sophisticated rubber duck ever built.

The pattern: describe your problem to the AI as if explaining it to a smart new team member. Don't ask for a solution. Just describe what's happening precisely.

"I have a React component that renders a list of 500 items. Each item has a checkbox. When the user checks a checkbox, the entire list re-renders with a visible 200ms lag. The component receives items as a prop from a Redux store. Each checkbox dispatches an action that updates a selectedItems array in the store. I'm using React 18 with the default reconciler. I've tried React.memo on the list items, but it didn't help because the selectedItems array reference changes on every dispatch."

I didn't ask "how do I fix this?" I described the situation precisely. The AI — like a good colleague — will identify the root cause and suggest targeted solutions. But half the time, I find the answer myself while writing out the explanation. The AI is just the excuse to think clearly out loud.

This pattern is underused. Most developers only reach for AI when they want code. The Rubber Duck works best when you want understanding.

Pattern 4: The Specification Prompt

This is my favourite pattern for keeping AI where it belongs — in the implementation layer, not the design layer.

The idea: you define the interface and types, you let AI fill in the implementation.

"Here is a TypeScript interface for a caching service:

interface CacheService {
  get<T>(key: string): Promise<T | null>;
  set<T>(key: string, value: T, ttlSeconds?: number): Promise<void>;
  invalidate(pattern: string): Promise<number>;
  getOrSet<T>(key: string, factory: () => Promise<T>, ttlSeconds?: number): Promise<T>;
}

Implement this interface using Redis with ioredis. The invalidate method should support glob patterns. Handle connection errors gracefully by returning null/0 rather than throwing, so the app degrades to cache-miss behaviour instead of crashing. Add JSDoc comments."

This pattern works brilliantly because you retain control over the design decisions that matter — the interface contract, the error model, the behaviour spec — while delegating the mechanical plumbing. You review the output against your specification, not against a vague mental model.

Engineers who are nervous about letting AI write their code should start here. You are still the architect. The AI is filling in the brickwork within the structure you've designed.

Pattern 5: The Review Request

This is the most underrated pattern, and the one that has changed my own code quality the most.

Instead of asking AI to write code, ask it to critique code you've already written.

"Review this function for edge cases, performance issues, and potential bugs. Be specific and harsh — I'd rather catch problems now than in production.

def calculate_discount(user, cart):
    if user.is_premium and cart.total > 100:
        discount = cart.total * 0.15
    elif cart.total > 200:
        discount = cart.total * 0.10
    elif len(cart.items) > 10:
        discount = cart.total * 0.05
    else:
        discount = 0
    return cart.total - discount
```"

A good AI reviewer will catch: What happens when a premium user has a cart total of exactly 100? What about negative cart totals? Floating-point precision on currency calculations? Is the elif ordering correct — could a user qualify for a smaller discount when the larger one should apply?

This is AI at its best. It doesn't get tired. It doesn't feel awkward pointing out problems in a senior engineer's code. It checks edge cases systematically and without ego. Since I started using this pattern before every PR, the frequency of review feedback I receive has dropped noticeably — because I'm catching my own issues first.

Anti-Patterns to Avoid

The Prompt Novel. Five paragraphs of company background and team structure when you just need a regex. Match prompt length to task complexity. A simple task deserves a simple prompt.

The Zero-Context Ask. "Fix this bug" with a code snippet and no error message, no expected behaviour, no actual behaviour. You wouldn't file a bug report this way. Don't prompt this way.

Using AI as Google. "What's the syntax for a Python list comprehension?" Just Google it. Use AI for synthesis, generation, and exploration — not for facts that live in documentation.

The One-Shot Miracle. Expecting a single prompt to produce production-ready code for a complex, multi-layered feature. Complex work requires the Incremental Refinement pattern. Break it down, iterate, and refine.

Build a Personal Prompt Library

The best developers I've worked with maintain a small collection of prompt templates they reuse. Nothing formal — just a notes file or Notion page with their go-to patterns, pre-filled with their stack details.

Starter categories worth building today:

  • Code generation: Your Context Sandwich template with your stack pre-filled (language, framework, libraries, conventions).
  • Code review: Your standard Review Request with your team's specific quality criteria.
  • Debugging: A template that forces you to include error message, expected behaviour, actual behaviour, and what you've already tried.
  • Architecture exploration: Your Incremental Refinement starter questions for new features.
  • Documentation: A prompt that generates doc comments matching your team's style and level of detail.

The prompt library pays compound interest. Every time you refine a template based on what worked, your future interactions get better. After two or three months, you stop thinking about prompting at all. It becomes instinctive — like how you don't think about keyboard shortcuts anymore, you just use them.

Start today. Save the next prompt that produces genuinely useful output. In a month, you'll have a collection that makes you measurably faster.

Key Takeaways

  • Prompt engineering is communication, not science. The same skills that make you good at design docs and code reviews make you good at prompting AI.
  • Vagueness is the enemy. Under-specifying context is the number one reason AI output is unusable. More specificity = more directly usable output.
  • Five patterns cover 90% of use cases. Context Sandwich, Incremental Refinement, Rubber Duck, Specification Prompt, and Review Request. Learn these five well before you look for anything else.
  • Use AI for review, not just generation. Asking AI to critique your code is often more valuable than asking it to generate code from scratch.
  • Build a prompt library. Save what works, refine it over time. Compounding returns make you measurably faster within months.
  • Your next step this week: Take your most common AI use case — the thing you ask AI about most often — and write a proper Context Sandwich prompt for it. Save it somewhere you can reuse it. Compare the output quality to what you've been getting.