Advertisement

AI coding assistants have become part of the daily toolkit, and the gap between developers who get great results and those who get frustrating ones is rarely about the model — it is about how they ask. Prompting well is not incantation or secret phrases; it is applied communication and specification, the same skills that make a good bug report or ticket. Treat it like engineering and it becomes reliably useful.

The core insight: the assistant only knows what you tell it and what it can infer, so most bad output traces back to an underspecified request.

Give context and constraints

A prompt like "write a function to sort users" leaves everything to guesswork — which language, sorted by what, what a user looks like, what edge cases matter. A good prompt supplies the context: the language and version, the relevant data shape, the constraints ("must be stable", "handle empty input", "no external libraries"), and the surrounding conventions. The more the assistant knows about your actual situation, the closer the first answer lands.

Constraints are especially powerful because they narrow the space of acceptable answers. Telling the assistant what not to do — "do not use recursion", "match the existing error-handling style" — often improves output more than adding another instruction about what to do.

Advertisement

Show, iterate, and decompose

Examples communicate faster than descriptions. Showing a sample input and expected output, or a snippet of your existing code to match, conveys intent that paragraphs of prose would fumble. And treat the interaction as iterative: the first answer is a draft to refine, not a verdict. "Good, now handle the null case" and "that's too clever, make it more readable" steer it quickly toward what you want.

For anything substantial, decompose. Asking for a whole feature in one shot invites sprawling, hard-to-verify output; asking for one function, reviewing it, then the next builds something you actually understand. The assistant is a fast pair, not an autonomous contractor.

The fluency trap

The single most important habit is scepticism about confident wrongness. AI assistants produce fluent, authoritative-sounding code that can be subtly or completely wrong — a plausible API that does not exist, an off-by-one error, a security hole delivered with total confidence. Fluency is not correctness, and the calm, well-formatted tone is not evidence.

So review AI-written code as you would a stranger's pull request, or more carefully: read it, understand it, test it, and never ship what you cannot explain. Used this way — specific prompts, iterative refinement, decomposed tasks, and rigorous review — an AI assistant is a genuine multiplier. Used as an oracle whose output you paste unread, it is a fast way to ship bugs you do not understand.

Advertisement

Explicit constraints prevent a model from making silent, unwanted trade-offs

A request to 'write a function that fetches user data' leaves the model to silently decide error handling, caching, retry behavior, and timeout handling on its own, often making a reasonable-sounding but wrong-for-this-context choice — explicitly stating constraints ('no external dependencies,' 'must handle a timeout by returning null rather than throwing,' 'must not cache results') converts implicit assumptions the model would otherwise guess at into explicit requirements it can actually satisfy, closing the gap between what was silently wanted and what was actually asked for.

Few-shot examples teach a pattern faster than a paragraph of description

Providing one or two concrete input-output examples of the exact transformation wanted is frequently more effective at communicating intent than an equivalent paragraph of prose description, since examples pin down the exact expected shape, edge-case handling, and format directly and unambiguously, leaving far less room for the model to infer something subtly different from what a prose description alone might have left open to interpretation.

Advertisement

Why asking for the reasoning alongside the code surfaces hidden assumptions early

Requesting a brief explanation of why a particular approach was chosen, alongside the code itself, frequently surfaces an assumption the model made that would otherwise have gone unnoticed until it caused a problem later — a stated assumption like 'this assumes the input array is already sorted' is something a reviewer can immediately check against reality, whereas the same unstated assumption buried silently inside generated code is something a reviewer has to notice entirely on their own.

Why fluent, confident-sounding output is the single biggest trap in this whole practice

A model's output is stylistically indistinguishable whether it is correct or subtly, confidently wrong, since the same fluent, well-formatted, plausible-looking code is produced either way — this is precisely why the discipline covered at greater length elsewhere in this library's AI-code-review articles matters so much: fluency is not a reliable signal of correctness at all, and treating confident-sounding output as a proxy for verified-correct output is the exact trap that turns a productivity tool into a liability.

Why stating what NOT to do is sometimes more effective than only stating what to do

A prompt that only describes desired behavior leaves the model free to reach for a common but unwanted approach — introducing a new dependency where none was wanted, using a pattern that conflicts with the existing codebase's style — and explicitly ruling out specific unwanted approaches ('do not add any new dependencies', 'do not use recursion here') closes off exactly the space of common but wrong choices a purely positive description would leave open.

Why treating an AI conversation as disposable, not as a persistent expert, avoids a specific trap

A model has no persistent memory of a codebase's evolution across separate conversations, and its confident tone in a fresh conversation can mislead a developer into treating it as a consistent, ongoing collaborator with continuity of understanding it does not actually have — providing the same essential context fresh in each new conversation, rather than assuming carryover from a previous one, avoids the specific mistake of expecting a memory the tool genuinely does not possess.

Why providing the actual error message verbatim beats paraphrasing it

Paraphrasing an error message from memory ('it said something about undefined') loses exactly the specific detail — the precise wording, the stack trace, the exact line number — that would let the model reason about the actual, specific failure rather than guessing at a generic category of error that vaguely matches the paraphrase; pasting the verbatim error output is one of the simplest, highest-leverage habits for getting a genuinely useful debugging response rather than a generic one.

Why specifying the target runtime or version avoids a subtle compatibility mismatch

A model with training data spanning many library and language versions can default to syntax or APIs from a version different than the one actually in use, producing code that looks correct but fails to run on the actual target environment — stating the exact language version, framework version, and runtime target explicitly closes this gap before it produces a confusing, hard-to-diagnose compatibility failure.

Why asking the model to list its own assumptions before writing code catches gaps early

Explicitly requesting a short list of assumptions the model is about to make, before it writes any actual code, surfaces exactly the kind of silent, unstated assumption discussed elsewhere in this article — reviewing that list takes seconds and catches a wrong assumption before any code is written around it, which is considerably cheaper than discovering the same wrong assumption buried inside a finished implementation.

Why asking for the code's own limitations alongside the code itself is worth the extra request

Explicitly asking 'what would break this function, and under what conditions would it behave unexpectedly' alongside a request for the implementation itself often surfaces genuine edge cases the model is aware of but would not have volunteered unprompted, turning an implicit blind spot into an explicit, reviewable list a human can then verify or test directly.

Why a shared, versioned repository of team prompting conventions pays off similarly to a style guide

Just as a team benefits from a shared coding style guide rather than each engineer inventing their own conventions independently, a shared, written set of prompting conventions — how much context to include, which constraints to always state explicitly — spreads the hard-won lessons covered throughout this article across the whole team rather than leaving each individual to rediscover them independently through their own trial and error.

Why the habits in this article transfer directly to writing better tickets and specs for humans too

Every discipline covered here — stating explicit constraints, providing concrete examples, surfacing assumptions before they cause problems — is equally valuable when writing a ticket for a human colleague to pick up, which is a useful, unintended benefit: practicing clear specification for an AI assistant tends to make someone a noticeably clearer communicator with human collaborators as well.

Why treating a prompt as a living document, refined across a session, beats writing one perfect prompt

Rather than aiming to write one perfect, exhaustive prompt on the first attempt, treating the conversation as an evolving specification — adding a missed constraint once it becomes clear it was needed, correcting a wrong assumption the moment it surfaces — mirrors how a real specification actually gets refined over a real engineering conversation, and is a more realistic, sustainable habit than expecting to anticipate every requirement perfectly in advance.

Why the goal is a productive working relationship, not a single flawless instruction

Every technique in this article serves the same underlying goal: establishing a productive, iterative working relationship with the tool, closer to collaborating with a fast, knowledgeable, but occasionally overconfident junior colleague than to issuing a single flawless command to an oracle — reframing the interaction this way changes what counts as success, from 'the first response was perfect' to 'the conversation converged on something genuinely correct.'

Why engineers who already write good specifications for other engineers adapt fastest

Someone already skilled at writing a clear ticket, a clear pull request description, or a clear design document tends to pick up effective prompting quickly, since the underlying skill — anticipating what a reader needs to know and stating it explicitly rather than assuming shared context — transfers directly; this article's practices are, in a real sense, that same existing skill applied to a new kind of reader.

Why reading the full response before acting on any part of it avoids a specific, common mistake

Acting on the first few lines of a response before reading the rest risks missing a caveat, a stated assumption, or a noted limitation the model included further down — reading the entire response fully before making any changes based on it is a small discipline that catches exactly the kind of qualification a partial read would miss entirely.

Why a short pause to reread a request before sending catches an easy but costly omission

A quick reread of a drafted prompt, checking specifically whether it states the constraints, examples, and context this article has emphasized, catches an easily-missed omission before it costs an entire wasted round trip on an underspecified request.