Python Prompts That Actually Work: Code Generation, Refactoring, and Testing in 2026
Most developers using AI assistants in 2026 have the same story: the tool is brilliant on the first prompt and useless on the fifth. The model isn't the bottleneck — the prompt is. A vague request like "write a function to process this data" produces generic code you rewrite anyway. A structured prompt that states the input type, the failure modes, the constraints, and the expected output shape produces code you can merge.
This collection is 12 prompts that survive real production work: FastAPI endpoints, SQL queries, pytest suites, and performance tuning. Each one targets a specific task, includes the reasoning behind its structure, and shows what a real result looks like.
The Anatomy of a Prompt That Works
Before the list, the pattern. Across all 12 prompts you'll see four recurring ingredients:
| Element | Why it matters | Example fragment |
|---|---|---|
| Role and context | Sets the domain and quality bar | "You are a senior Python engineer reviewing production code" |
| Concrete input/output | Removes guesswork about types and shapes | "Input: list[dict] from a REST response; Output: dict[str, float]" |
| Constraints | Forces real-world tradeoffs | "No external dependencies, Python 3.12, handle None" |
| Failure modes | Makes the model reason about edge cases | "What happens on empty input, duplicate keys, timeouts?" |
The official OpenAI prompt engineering guide and Anthropic's prompt engineering documentation both emphasize exactly this: specificity, structure, and explicit output format. Everything below follows that guidance.
1. Generating a Typed Function From a Spec
Task: Turn a written requirement into a fully typed, documented function.
Prompt:
Write a Python 3.12 function `normalize_emails(records: list[dict]) -> list[dict]`.
Each record has keys: id (int), email (str | None).
Rules:
- Lowercase and strip whitespace from email.
- If email is None or empty, set it to None (do not invent a value).
- Preserve original order and all other keys.
- Raise ValueError if a record is missing the 'id' key.
Include type hints, a docstring with Args/Returns/Raises, and no external dependencies.
Result: A complete function with validation, plus a short note that the caller should decide whether duplicate emails are acceptable — the model flags the ambiguity instead of silently guessing.
Best for: Junior-to-mid tasks where the spec exists but the boilerplate is tedious.
2. The "Explain Before You Refactor" Prompt
Task: Refactor legacy code without breaking behavior.
Prompt:
Here is a function I wrote two years ago. Before changing anything:
1. Explain in 3 bullets what it does and any hidden side effects.
2. List the specific code smells (nested conditionals, mutable defaults, etc.).
3. Then propose a refactor that keeps the public signature identical.
Do not change behavior. Flag anything that looks like a bug instead of fixing it silently.
<paste code>
Why it works: Forcing the model to explain first prevents the classic failure where the assistant "improves" your code by changing its semantics. The explicit instruction to flag rather than fix bugs is the key line.
3. Generating pytest Suites With Edge Cases
Task: Write tests that catch real bugs, not just coverage numbers.
Prompt:
Write pytest tests for the function below using only the standard library and pytest.
Requirements:
- Cover the happy path, empty input, None values, and boundary values.
- Use @pytest.mark.parametrize for the input variations.
- Use pytest.raises for each documented exception.
- Name each test so the failure message reads like a sentence.
- Do NOT mock anything unless I ask.
<paste function>
Result: A parametrized suite that usually surfaces at least one unhandled edge case in the original function — which is the point.
4. Debugging From a Traceback
Task: Diagnose an error without pasting the whole codebase.
Prompt:
I got this traceback. Tell me:
1. The most likely root cause (one sentence).
2. Three candidate fixes, ranked by likelihood.
3. A minimal reproduction I can run to confirm.
Ask me for one more file only if it is strictly necessary.
<paste traceback>
Why it works: Limiting the model to one follow-up question keeps the loop tight. Requesting a minimal reproduction converts a vague answer into something testable.
5. FastAPI Endpoint With Validation
Task: Scaffold a production-shaped endpoint.
Prompt:
Create a FastAPI endpoint POST /items that accepts a Pydantic v2 model
with fields: name (str, 1-100 chars), price (Decimal, > 0), tags (list[str], max 10).
Return 201 with the created item, 422 on invalid input (default FastAPI behavior),
and 409 if the name already exists (assume a `repo` object with `exists(name)`).
Use dependency injection for the repo. Add response_model and status_code.
Result: A router that follows FastAPI conventions, uses Annotated dependencies, and returns correct status codes — the kind of code that passes review without edits.
6. Optimizing a Slow Function
Task: Improve performance with measurable reasoning.
Prompt:
This function processes ~1M rows and takes 40 seconds. Profile the likely hotspots
by reading the code, then propose optimizations in order of impact.
For each: estimate the complexity change (e.g., O(n^2) -> O(n)) and show the diff.
Do not suggest numpy or external libraries. Target pure Python 3.12.
Why it works: Asking for complexity estimates forces the model to reason rather than pattern-match. The library restriction keeps suggestions deployable.
7. SQL Query Generation and Review
Task: Write SQL, then have it critiqued.
Prompt:
Schema: orders(id, user_id, created_at, total), users(id, email, created_at).
Write a query returning, for each user created in the last 90 days,
their total order count and revenue, including users with zero orders.
Then review your own query for: missing indexes, N+1 risks,
and behavior when totals are NULL.
Result: A LEFT JOIN with COALESCE, plus a self-review note recommending an index on orders(user_id, created_at) — exactly the kind of detail that prevents a slow query in production.
8. Docstrings and Documentation
Task: Document code so the next developer doesn't have to read it.
Prompt:
For each public function in this module, write a Google-style docstring
with Args, Returns, Raises, and one realistic usage example.
Keep examples runnable. Do not document private helpers.
Why it works: The "runnable example" constraint turns documentation into something you can copy into a REPL and verify. See the Google Python Style Guide for the docstring format.
9. Converting Scripts to CLI Tools
Task: Turn a one-off script into a reusable tool.
Prompt:
Convert this script into a CLI using argparse (standard library only).
Add: --input (required), --output (default stdout), --verbose flag.
Include --help text written for a non-technical user.
Keep the core logic in a separate function so it stays testable.
Result: A clean separation between I/O and logic — the pattern that makes scripts testable and reusable.
10. Code Review Prompt
Task: Get a second pair of eyes before opening a PR.
Prompt:
Review this diff as a senior engineer. Focus on:
1. Correctness and edge cases.
2. Security issues (injection, unsafe deserialization, secrets in logs).
3. Readability and naming.
Give a verdict: approve, approve with comments, or request changes.
Be specific — cite line numbers, not general advice.
Why it works: The verdict forces a decision, and "cite line numbers" eliminates vague feedback.
11. Explaining Unfamiliar Code
Task: Onboard into a codebase quickly.
Prompt:
Explain this code to a developer who knows Python but not this domain.
Structure: (1) what problem it solves, (2) data flow, (3) non-obvious decisions,
(4) what would break if I changed X. Use plain language, no jargon without a definition.
12. The "Teach Me" Prompt for New Libraries
Task: Learn a library by building something small.
Prompt:
I want to learn <library> by building a minimal working example.
Give me: a 20-line script that demonstrates the core concept,
then three progressively harder exercises with hints (not solutions).
Assume I know Python but not this library. Cite the official docs page for each concept.
Why it works: Hints instead of solutions keep you in the driver's seat, and doc citations let you verify claims — critical, since model knowledge of fast-moving libraries can lag.
What Separates a Good Prompt From a Great One
Across all twelve, three habits repeat. First, state constraints explicitly — Python version, dependencies, output format. Second, ask for reasoning before output — explanations catch bad assumptions early. Third, request verifiable artifacts — line numbers, runnable examples, doc citations — so you can check the answer instead of trusting it.
Treat every prompt as a specification, not a wish. The model is a fast, tireless junior engineer with no memory of your codebase: the clearer your brief, the better the result. Start with prompts 3 and 10 — tests and review — because they catch problems early and cost the least to fix. Then adapt the rest to your stack, and keep the ones that consistently save you a review cycle.
Comments