DEV Community

pickuma
pickuma

Posted on • Originally published at pickuma.com

The 5-Part AI Prompt Formula That Actually Fixes Bugs

Paste a stack trace into Claude or Copilot and ask "what's wrong?" and you will get an answer. Whether that answer is useful depends almost entirely on what else you included. Most developers treat AI debugging as a search engine query — toss in the symptom and expect a cure. The result is a confident-sounding patch that addresses the surface error while the actual cause survives untouched.

The problem is not the model. It is information debt. AI assistants can only reason about what you give them. When you hand them a vague symptom and nothing else, they fill the gaps with plausible assumptions that may have nothing to do with your actual code path. This guide builds a five-part prompt structure that eliminates those gaps — not by adding length for its own sake, but by feeding the model the exact categories of information it needs to reason rather than guess.

Why weak prompts produce plausible-but-wrong fixes

Consider two prompts targeting the same bug:

Weak prompt:

My React component re-renders too many times. Can you fix it?
Enter fullscreen mode Exit fullscreen mode

Structured prompt (we'll build this below):

Environment: React 18, TypeScript 5, no Redux — local useState/useContext only.

Bug: ProductList re-renders on every keystroke in the parent SearchBar, even
when the product list data hasn't changed.

Expected: ProductList should only re-render when the `products` prop array changes.
Actual: React DevTools Profiler shows ProductList rendering 12x per second while
the user types. `products` comes from a useMemo that depends on `filterText`.

Relevant code: [minimal snippet with useMemo + prop drilling]

Steps already tried: Wrapped ProductList in React.memo — no effect.

Request: Before suggesting a fix, identify the root cause. Is the reference to
`products` changing on every render despite useMemo? If so, why?
Enter fullscreen mode Exit fullscreen mode

The second prompt produces a different class of answer. The model can now check whether useMemo dependencies are actually stable, whether the array reference is being reconstructed unnecessarily, and whether React.memo fails here because a new function reference is being passed as a prop alongside products. The first prompt gets you boilerplate about useCallback and React.memo — technically reasonable advice, but aimed at a problem the model invented.

The core failure of weak prompts is that they force the AI to invent a mental model of your code from scratch. That invented model is frequently wrong in ways that are hard to detect because the suggestions still sound like real React advice.

The five-part structure

Part 1: Environment and constraints

State your runtime, framework, version, and anything that rules out certain solutions. This does not need to be long — one to three lines is usually enough.

Environment: Node 22, Express 5, PostgreSQL 16 via pg 8.x.
Constraint: Cannot add new npm dependencies. Async/await only, no callbacks.
Enter fullscreen mode Exit fullscreen mode

This matters because AI models carry broad knowledge across many library versions. Without explicit pinning, they default to whatever patterns are most common in their training data, which may not match your stack. If you are on Python 3.9 and the model suggests match statements, that is a training-data assumption overriding your actual environment.

Part 2: Reproduction — what triggers the bug

Describe the precise sequence of inputs or conditions that surfaces the bug. If you can reduce it to a minimal reproducible case, do that before writing the prompt. The act of reduction often locates the bug on its own; when it does not, the minimal case gives the model exactly the surface it needs to reason about.

Reproduction: POST /api/orders with a valid body triggers the bug only when
the authenticated user's account has `role: 'guest'`. Admin accounts work.
The error does not appear in local dev because seed data only includes admin users.
Enter fullscreen mode Exit fullscreen mode

Without this, the model has to speculate about what conditions trigger the failure. It may fix the wrong branch entirely.

Part 3: Expected versus actual behavior

State these as separate, concrete observations — not a combined narrative. Include the exact error message or the exact observed output when you have it.

Expected: A 201 response with the created order ID.
Actual: 500 — "Cannot read properties of undefined (reading 'permissions')"
Stack trace: [paste the relevant frames, not the entire wall of text]
Enter fullscreen mode Exit fullscreen mode

Addy Osmani's writing on AI coding workflows makes the same point: feed the AI actual system behavior, not your interpretation of it. "The request fails" is an interpretation. The stack trace is data.

Part 4: Relevant code and what you have already tried

Paste the smallest slice of code that contains the bug. Do not paste entire files. A focused 300-400 token snippet — the function, its immediate callers, and the relevant data types — consistently outperforms a 4,000-token dump of the full module. The model's attention is finite and a smaller surface area means less chance of it latching onto unrelated patterns.

Also document what you have already tried. This is underused. If you have already added a null check, restarted the server, verified environment variables, and the bug persists, saying so prevents the model from suggesting those same steps as its first response.

Relevant code:
[orderController.ts  createOrder function, ~30 lines]
[authMiddleware.ts  where req.user.permissions is set, ~15 lines]

Already tried:
- Added optional chaining: req.user?.permissions  still fails
- Confirmed req.user is defined (console.log shows the full user object)
- The permissions property is present for admin users, missing for guests
Enter fullscreen mode Exit fullscreen mode

When trimming code for a prompt, start by removing imports, comments, and any branch that does not execute on the failing path. If the function calls three helpers but only one is involved in the bug, paste only that helper. The goal is the minimum code that would allow a human expert to understand the failure — that same minimum works for the model.

Part 5: An explicit request to diagnose before patching

This part is the one most developers skip, and it is the most important. Explicitly ask the model to identify the root cause before writing a fix. Without this instruction, the default behavior of most AI assistants is to head straight for a code change — because a code change looks like the useful output.

Request: Before suggesting any code changes, explain what you believe is the
root cause of this error. Why would req.user.permissions be undefined only for
guest accounts? Then, once you have identified the cause, propose a fix.
Enter fullscreen mode Exit fullscreen mode

This single sentence changes the model's output significantly. It shifts the task from "produce a patch" to "reason about the failure, then produce a patch." The reasoning step surfaces assumptions that would otherwise be hidden inside the code change, and it gives you a checkpoint to disagree before any code is written.

GitHub's Copilot documentation for debugging specifically recommends this approach — asking "Why is the output of this code higher than expected? Please explain in depth" rather than jumping straight to "fix this." The explanation-first mode catches cases where the model's patch would be technically valid but addressing the wrong problem.

Putting the formula together

Here is a complete example of the full structure as a single prompt, targeting a real class of bug:

Environment: Python 3.11, FastAPI 0.111, SQLAlchemy 2.x async sessions.
Constraint: ORM only  no raw SQL queries allowed by team policy.

Reproduction: PATCH /users/{id} succeeds and returns 200, but changes are not
persisted to the database. The issue occurs in staging (PostgreSQL) but not in
local dev (SQLite). Happens for all users, not a permissions issue.

Expected: The updated fields should be visible in a subsequent GET /users/{id}.
Actual: GET returns the original values. No errors or warnings in logs.

Relevant code:
[async def update_user  the route handler, ~25 lines]
[db.py  session dependency, ~15 lines]

Already tried:
- Added db.refresh(user) after commit  no change
- Verified the PATCH body is reaching the handler correctly (logged it)
- Confirmed the session is not being rolled back explicitly

Request: Before suggesting a fix, diagnose why changes committed in an async
SQLAlchemy session would not persist in PostgreSQL but would appear to work in
SQLite. Is this likely a transaction isolation issue, a session lifecycle
problem, or something about how async sessions handle commit?
Enter fullscreen mode Exit fullscreen mode

A prompt like this consistently gets a substantive diagnosis — in this case, the common async SQLAlchemy issue where session.commit() is not being awaited properly, or where the session context manager exits before the commit is reached. The model can reason to that conclusion because every relevant piece of evidence is present.

What this does not solve

The formula does not compensate for incorrect assumptions in parts 2 and 3. If your description of "expected behavior" is wrong — if the bug is actually that your mental model of the API is incorrect — the model will faithfully chase the wrong target. The structure helps the model reason; it does not verify that you have framed the problem correctly.

It also does not replace running the fix and checking the result. A well-reasoned diagnosis can still produce a patch that introduces a new edge-case failure. Treat the AI's output as a hypothesis and verify it against your test suite and your own review before merging. The Prompt Engineering Playbook from Addy Osmani's substack puts it plainly: the AI is a drafting tool, not a senior engineer — human review before deployment remains non-negotiable.

The five parts — environment, reproduction, expected vs. actual, relevant code plus what you tried, and an explicit diagnose-first request — are not a magic incantation. They are a checklist for the information that a thoughtful human debugger would want before sitting down with your code. The AI benefits from exactly the same information.


Originally published at pickuma.com. Subscribe to the RSS or follow @pickuma.bsky.social for new reviews.

Top comments (0)