Vague vs Specific Coding Prompts: Why “Write a Function” Fails
The Function That Does Not Exist
You ask an AI to “write a function that validates an email” and you get back a regex you cannot trust, no tests, and no idea what edge cases it handles. The prompt was not wrong grammatically; it was wrong because it gave the model no constraints to aim at. Code is unforgiving: a function that works for “test@example.com” and crashes on “a@b.co.uk” or “null” is a bug, not a draft. Vague coding prompts produce plausible-looking code that fails in production.
This is a textbook Structure B case: vague vs specific, with context, constraints, and tests as the divider. For a full debugging flow that uses these principles, see our debug code workflow.
Wrong Prompt: No Context, No Rules
The bare request. It looks reasonable in a hurry and it is the source of most “the AI wrote broken code” complaints.
Write a function to validate an email address.What goes wrong: the model does not know the language, whether to return a boolean or throw, what counts as valid (RFC strictness), whether to handle international domains, or what to do with null input. It fills every gap with a default that may not match your stack, and you cannot tell which decisions it made.
Right Prompt: Language, I/O, Edge Cases, Tests
The fix is to specify the environment and the contract. Treat the model like a junior dev who needs a ticket, not a mind reader.
Language: Python 3.11, standard library only.
Task: write a function is_valid_email(value: str) -> bool.
Rules:
- Return False for None or non-string input (after a type check).
- Accept international domains (xn-- punycode and unicode).
- Do NOT attempt full RFC 5322 compliance; use a pragmatic regex.
- Reject strings without exactly one "@" and a dotted TLD of 2+ chars.
- Trim surrounding whitespace before testing.
Deliverables: the function plus a pytest test file covering: valid, missing @, null, unicode domain, trailing space, double @.
Show the test output as if run with pytest.Now the model knows the language, the signature, the contract, the boundaries, and that you expect tests. The output is reviewable and the tests tell you whether it actually works.
Side-by-Side Comparison
| Element | Vague prompt | Specific prompt |
|---|---|---|
| Language | Unsaid | Python 3.11, stdlib only |
| Input/output | Guessed | str -> bool, typed |
| Edge cases | None listed | Null, unicode, whitespace, double @ |
| Scope | Unbounded | Pragmatic, not full RFC |
| Tests | Absent | pytest with 6 cases |
| Reviewability | Low | High, contract is explicit |
Why the Right Version Works
Specific prompts work because they convert ambiguity into a spec. The model does not invent fewer things; rather, you have pre-decided the things that matter, so its inventions stay inside safe bounds. Naming the language avoids imports that do not exist in your stack. Naming edge cases forces the model to reason about them instead of happy-path code. demanding tests turns “looks right” into “provably handles the cases I care about.”
This is the same discipline behind our SQL generator prompt, where a schema and guardrails replace guesswork with a contract.
How to Make Your Own Coding Prompts Specific
Before sending any code request, answer these in the prompt:
- Language and version, plus any library constraints.
- The exact function or component signature (inputs, outputs, types).
- The edge cases and error behavior you care about.
- Whether you want tests, and in what framework.
- Anything explicitly out of scope (e.g., “no external packages”).
A compact template:
Language: [lang + version], [constraints].
Task: [what to build], signature: [inputs] -> [output].
Rules: [edge cases, error handling, scope limits].
Deliverables: [code + tests in framework X].
Show: [how you verified it].When the model returns code, paste the tests and run them yourself. The prompt got you close; your runner is the final judge.
What to Do When the Code Still Breaks
A specific prompt dramatically raises your hit rate, but it is not a guarantee. When the returned function fails its own tests, do not start over; iterate with the same contract. Paste the failing test output and point at the contract:
The test "unicode domain" failed: your regex rejected "user@münchen.de".
Our rule said to accept international domains. Fix only that case, keep the rest, and re-run all six tests. Show the diff.This works because the original prompt already fixed the language, signature, and scope. You are adjusting one edge case, not renegotiating the whole spec. The model has far less room to drift.
A Second Example: From Vague to Specific
Vague:
Make an API endpoint that saves user data.Specific:
Language: Node.js 20, Express, TypeScript.
Task: POST /users that validates and stores a user {email: string, name: string}.
Rules: 400 on missing fields, 409 on duplicate email, hash password with bcrypt, never log the password.
Deliverables: route handler + a Vitest test covering 200, 400, 409.
Show: test run output.Same shape as the email example: language, signature, rules, tests. Once you internalize the pattern, every coding request starts from the same four boxes.
Pre-Send Checklist for Coding Prompts
Before you hit enter, confirm these five boxes. If any is empty, the model will fill it with a guess, and guesses are where bugs hide.
- Language and version named, including library limits.
- Input and output types explicit, not implied.
- At least two edge cases spelled out.
- Error behavior decided (return value vs throw).
- Tests requested in a named framework.
Keep this list next to your editor. After a week you will write specific prompts by reflex, and your “the AI wrote broken code” rate will drop sharply. The point was never the model; it was the missing spec.
Sources & Notes
Examples use Python 3.11 and pytest conventions as of 2025. The pragmatic-email-validation approach intentionally avoids full RFC 5322 to keep the example readable; production systems should validate via a vetted library. Model behavior varies by version (GPT-4o, o-series, Claude). No external APIs or WordPress endpoints were used to create this article; test all generated code in your own environment before shipping.
