Blog · ← All posts
Prompt Patterns That Produce the Worst Code
Last updated: October 2026 · 6 min read
After auditing dozens of AI-generated codebases, we've started asking teams a new question: not "which model wrote this," but "what did you ask it?" The answers explain the code. Bad code rarely comes from bad models. It comes from bad prompts, repeated the same ways.
Here are the prompting habits that reliably produce the worst output.
1. The vague spec: "build me a dashboard"
The single most damaging prompt pattern. "Build me a dashboard" contains roughly zero information about data sources, users, tech stack, or constraints, and the model fills every gap with its most likely guess. Those guesses compound: a wrong guess about the database shapes the API, which shapes the frontend, which shapes the tests. By the end you have a coherent, fully working application that does the wrong thing confidently.
A 12-person startup showed us a billing dashboard built this way. It worked. It also assumed a single currency, a single tax rate, and that every customer paid monthly. The company had annual plans, two currencies, and EU customers. The dashboard was demo-ready and production-wrong. Rebuilding it properly took longer than writing it from scratch would have.
The fix is unglamorous: write the spec before you prompt. Ten lines of constraints (stack, data model, users, edge cases) produce dramatically better code than a thousand tokens of clever prompt engineering. The model doesn't need poetry. It needs the facts you were keeping in your head.
2. The missing constraints: everything you didn't say
Prompts describe what to build. They rarely describe what to avoid. And the model's defaults for "what to avoid" come from average training data, which is not your standard.
The defects we catalog most often trace to unsaid constraints:
- No mention of error handling → try/except pass blocks everywhere.
- No mention of code structure → one 400-line file, or one function per feature.
- No mention of testing → code that's structured in a way that's hard to test.
- No mention of performance → an N+1 query inside a loop over 10,000 records.
- No mention of the existing codebase → new code that duplicates what's already there.
That last one deserves emphasis. Teams prompt for features in isolation ("add user profiles") without telling the model what already exists. The model can't read your repo from a chat box (unless you're in an agentic IDE, and even then, partially). So it reinvents the auth check, the date formatter, the API client. Every isolated prompt is a new opportunity for duplication.
One sentence at the end of every prompt does more than any framework: "Follow the patterns in the existing code. Reuse before you create."
3. The "make it work" loop
This is the pattern we see in the messiest codebases, and it's a human habit, not a model failure. It goes like this:
- Prompt: "add search to the product page." Model produces code.
- It breaks. Prompt: "fix the error."
- It breaks differently. Prompt: "just make it work."
- It works. The code now contains three abandoned approaches, two dead functions, and a comment that says "TODO: clean this up."
Each "fix the error" prompt layers a patch on top of a design the model never fully understood, because each new prompt only describes the symptom, not the goal. The model can't step back and redesign. It's responding to the last message, optimizing for "no error," and "no error" is a terrible design standard.
We reviewed a payment integration that went through eleven "make it work" iterations. The final code handled the happy path, the card-decline path, and approximately nothing else. Webhooks were unhandled. Retries were unhandled. The code was 500 lines of accumulated patches with no coherent design underneath. The developer told us the model "kept fixing things." It did. That's the problem.
The rule: after three failed fix attempts, stop. Delete the generated code, write a clearer spec, and start the feature fresh. The sunk cost is an illusion: the mess costs more than the rewrite.
4. The kitchen-sink prompt
The opposite of the vague spec, equally bad: a 2,000-word prompt describing the entire application, all its features, the design system, the database schema, and the deployment setup, in one message.
Models don't handle this the way people hope. Attention spreads thin across a long prompt; the details at the end get less weight than the instructions at the beginning. The result is code that nails the first feature and gets progressively sloppier, exactly the pattern we see in generated monoliths, where the auth system is careful and the reporting module is a mess.
Break the work into pieces. One feature per prompt, in dependency order, each one small enough that you can review it in a sitting. Boring advice. It works.
5. No verification instruction
Most prompts end where they should begin. "Build X" is the whole instruction, and then the human accepts whatever comes out.
The prompts that produce the best code end with a verification step: "Now list the edge cases you didn't handle," or "What would break if the input were empty?" Models are surprisingly honest when asked to critique their own output. Not perfect, but honest enough to flag the swallowed exception, the hardcoded value, the missing null check.
It won't catch everything. The model can't see what it doesn't know, and it shares your blind spots. But in our audits, code generated with a self-review step in the prompt has roughly half the defect density of code generated without one. That's the cheapest quality improvement we know of.
6. The context-free refactor
"Refactor this for performance." "Clean up this module." "Modernize this code." These prompts fail because they describe a direction, not a destination, and the model can't see the constraints that should bound the change: which callers depend on the current behavior, which edge cases the ugly code was handling, why that weird-looking branch exists.
We audited a codebase where the model had "cleaned up" a date-handling module, removing what it called redundant branching. One of those branches handled daylight-saving transitions for a scheduling feature. The refactor was elegant, well-tested, and wrong twice a year. The original ugly code was ugly because reality is ugly. The model didn't know the constraint because the prompt never said "preserve all existing behavior for DST transitions," and the human never checked, because the diff looked so clean.
Refactor prompts need the guardrails spelled out: what must not change, which behaviors are load-bearing, what the tests must still prove. Better yet, paste the tests first and say "all of these must still pass, and here are the three behaviors with no tests that also must not change." If you can't name the behaviors that must not change, you're not ready to ask for the refactor.
What a good prompt actually looks like
Enough about failure. Here's the shape of a prompt that produces code we'd be happy to audit, using the dashboard example from section 1:
Build an admin billing dashboard (React + our existing /api client). Data: Stripe subscriptions via the /api/billing endpoints. Users: our support team, not customers. Constraints: multi-currency (USD, EUR), monthly AND annual plans, EU VAT display. Reuse: the existing DataTable component and the useCurrency hook — do not create new ones. Error handling: every fetch retries once, then shows the error state component; no silent failures. Then list the edge cases you didn't handle.
Count what's in there: stack, data source, users, the three constraints that broke the real dashboard, a reuse instruction, an error-handling standard, and a self-review step. About ninety words. It took the author maybe three minutes to write, because they already knew all of it. They just hadn't told the model.
That's the whole secret, and it's not a secret. The model doesn't need poetry. It needs the facts you were keeping in your head.
The uncomfortable truth
Every one of these patterns is a shortcut around thinking. The vague spec skips requirements. The missing constraints skip design. The "make it work" loop skips review. The kitchen sink skips planning. AI code generation doesn't remove the need for software engineering judgment. It punishes you faster for skipping it.
The teams with the cleanest AI-generated code aren't using better models or secret prompts. They're doing the boring parts: specs, constraints, small increments, verification. The model does the typing. The thinking is still yours.