The Art of the Technical Prompt: Getting Code That Doesn't Suck
Why Most Prompts Fail
A developer asks an AI tool: "Write a function to check if a password is strong." Thirty seconds later, they have code. They ship it. Six months later, they discover it allows some obviously weak passwords, enforces arbitrary rules that break legitimate use cases, and misses industry standards.
The problem wasn't the AI. The problem was the prompt. It was vague. It didn't specify what "strong" means. It didn't mention edge cases. It didn't reference standards or constraints. The AI did its best with almost no information, and then the developer took it on faith.
This happens constantly. Developers underestimate how much of their thinking needs to get into the prompt. They assume the AI will fill in obvious gaps. The AI does its best. Often that's not good enough.
Mastering prompts means understanding that the quality of code output is determined entirely by the quality of the specification. You can't blame the AI for generating mediocre code when you handed it a vague, half-thought-through problem description. That's like blaming a contractor for a bad job when you didn't give them a blueprint.
The Anatomy of a Great Technical Prompt
A great technical prompt has five components. Most bad prompts have one or two. That's why they fail.
1. Context (What You're Building and Why)
The AI needs to understand the bigger picture. What system is this function part of? What problem does it solve? What constraints exist?
Weak: "Write a function to parse CSV files."
Strong: "We're building an import system for customer data. We need to parse large CSV files (potentially 100K+ rows) from varied sources. The files may have inconsistent formatting, missing values, and custom headers. We need to handle errors gracefully, report which rows failed, and continue processing valid rows. The parsed data will be validated against our customer schema, so output should be structured as dictionaries with consistent field names."
The strong prompt tells the AI: scale matters, error handling matters, this is part of a larger system, structure matters. The weak prompt tells the AI nothing.
2. Specification (What the Code Must Do)
Be explicit about what the code should accomplish. List the requirements. Be specific about inputs and outputs.
Weak: "Create an authentication function."
Strong: "Create an authentication function that:
- Accepts username and password
- Validates against a PostgreSQL user table
- Returns a JWT token valid for 24 hours if credentials match
- Returns a specific error message if the user doesn't exist
- Returns a different error message if the password is wrong (security: don't leak whether user exists in production, but we do here for testing)
- Rate-limits failed attempts to 5 per hour per IP
- Logs all attempts (success and failure) to an audit table
- Handles database connection failures gracefully"
The weak prompt leaves everything to guesswork. The strong prompt removes ambiguity.
3. Constraints (What You Can't Do)
Tell the AI what the boundaries are. This prevents bad solutions that technically work but violate your architecture.
Example constraints:
- "Must not make external API calls (network not available during init)"
- "Must run in less than 100ms (part of hot path)"
- "Cannot use additional dependencies (this is vendored code)"
- "Must be thread-safe for concurrent access"
- "Must work in Python 3.8+ (legacy system constraint)"
- "Must not load the entire file into memory (streaming required)"
- "Data must be encrypted at rest"
Constraints guide the AI away from bad solutions. Without them, the AI might generate something that's technically correct but practically impossible to use.
4. Edge Cases and Error Handling
Specify what happens when things go wrong or hit unusual conditions.
Example:**
"Handle these cases:
- Empty input files
- Files with no valid rows
- Missing required columns
- Files with 10x more columns than expected
- Null or empty values in required fields
- Invalid data types
- Duplicate rows (log as warning, process first occurrence)"
Without this, the AI will handle the happy path and hope edge cases don't exist. When you're specific about edge cases, the AI bakes them into the solution.
5. Example Output (Show Don't Tell)
If your spec involves transforming data, show an example of input and expected output. A concrete example is worth a thousand words of description.
Example:****
"Input:
```
customer_id,email,signup_date,lifetime_value
C001,[email protected],2024-01-15,2500.00
C002,[email protected],2024-01-16,
```
Output:
```
{
"customer_id": "C001",
"email": "[email protected]",
"signup_date": "2024-01-15",
"lifetime_value": 2500.00
},
{
"customer_id": "C002",
"email": "[email protected]",
"signup_date": "2024-01-16",
"lifetime_value": null
}
```
Note: missing values become null in JSON, not empty strings."
One example often clarifies more than paragraphs of description.
The Core Principle: Your prompt should contain everything a human developer would need to understand the problem. If you wouldn't hire a developer to build this without explaining what's in your prompt, your prompt isn't good enough.
The Prompt Writing Process
Great prompts aren't accidents. They're written deliberately. Here's a process that works:
Step 1: Write the Requirements as If for a Human Developer
If you were hiring someone to build this, what would you write in the job description? That's your prompt foundation. Explain the problem, context, and constraints.
Step 2: Add Specificity to Every Ambiguity
Read your requirements. Find every place where you made an assumption or left something open to interpretation. Fix it.
"Fast" becomes "must complete in under 100ms for 1000 items."
"Handles errors" becomes "returns {success: false, error: 'specific_code', message: 'human readable'} and logs to syslog."
Step 3: Add Examples
For any transformation or complex behavior, show an example. Input and expected output. Make it concrete.
Step 4: List Edge Cases
What breaks the obvious solution? Empty input? Null values? Very large inputs? Wrong data types? List them and specify the behavior.
Step 5: Set Constraints
What's off the table? What dependencies can you not use? What performance requirements exist? Spell it out.
Step 6: Ask for What You Want
Be direct: "Implement a function that..." or "Write a class that..." or "Create a test suite that..."
Be clear about scope: "Include error handling" or "Add comprehensive comments explaining the algorithm" or "Write this as a single function, not a class."
Common Patterns That Work
Over time, you develop patterns for different types of tasks. Here are some that reliably produce great code:
Pattern 1: The Data Transformation Prompt
"Transform [input format] to [output format]. Here's an example:
Input: [concrete example]
Output: [concrete example]
Handle these cases: [list]
Requirements: [constraints]"
Pattern 2: The Architecture Prompt
"Design [system/component] that handles [use cases]. Requirements:
- [requirement 1]
- [requirement 2]
- [requirement 3]
Constraints:
- [constraint 1]
- [constraint 2]
Provide the high-level design, describe key components, and explain how they interact."
Pattern 3: The Refactoring Prompt
"Refactor this function to:
- [improvement 1]
- [improvement 2]
Constraints:
- Must maintain the same API
- Must handle the same inputs/outputs
Here's the current code:
[code]"
Pattern 4: The Test Suite Prompt
"Write comprehensive tests for this function:
[function code]
Test these cases:
- [case 1]
- [case 2]
- [edge case 1]
- [edge case 2]
Use [testing framework]. Each test should have a clear description of what it verifies."
Real Examples of Prompt Evolution
Attempt 1 (Bad Prompt):
"Generate a user authentication middleware for Express.js"
Result: Generic, doesn't handle your specific requirements, might use technologies you don't want.
Attempt 2 (Better Prompt):
"Create Express.js middleware for JWT authentication. It should check for a Bearer token, verify it, and attach the user to req.user. Return 401 if no token or invalid token."
Result: Better. Still missing context, error handling details, token format specs.
Attempt 3 (Strong Prompt):
"Create Express.js middleware called authenticateUser that:
1. Extracts JWT from 'Authorization: Bearer [token]' header
2. Verifies token using HS256 with our secret key (process.env.JWT_SECRET)
3. Extracts user ID and permissions from the token payload
4. Attaches {userId, permissions} to req.user
5. Returns 401 with JSON {error: 'unauthorized', message: 'Invalid or missing token'} if token is invalid
6. Returns 401 with message 'Token expired' if token is expired
7. Does not allow tokens valid for more than 24 hours from creation
8. Logs failed authentication attempts to the audit table
Assume token payload has {userId, permissions, exp} fields. The function should be production-ready with proper error handling and logging."
Result: Specific, clear expectations, no ambiguity. The generated code will be reliable.
Key Insight
A great prompt is essentially a detailed requirements specification written for the AI instead of a developer. The effort you put into clarity directly translates to code quality. Vague prompts produce vague code. Specific prompts produce solid code.
Iterating on Prompts
You won't get the prompt perfect on the first try. That's normal. When the generated code isn't what you wanted:
Diagnose the problem:
- "The code didn't handle edge case X" → your prompt missed that case, add it
- "The API design is awkward" → your specification wasn't clear enough, redesign it
- "It uses a library I don't want" → add "do not use [library]" to constraints
- "Performance is bad" → add performance requirements
Iterate the prompt, not the code:
Rather than manually fixing the generated code, refine your prompt and regenerate. This is faster and trains you to be better at specifying requirements. It also gives you the opportunity to see different solution approaches.
What to Do Monday Morning
- Take something your team asked you to code recently. Write a detailed prompt for it (context, spec, constraints, edge cases). Ask your AI tool to generate code. Compare to what you wrote. See how much effort better prompting would have saved.
- Create a "prompt template" for your most common task type. Save it to a doc or README. Share with your team. Have them use it and add feedback.
- This week, use concrete examples in every prompt. If you're transforming data, show input and output. Notice how much better the results are.
- Pick one vague prompt your team uses regularly. Rewrite it with the five components. Use it consistently. Measure improvement.
FAQ
Q: Doesn't writing a detailed prompt take longer than just coding it myself?
A: Initially, yes. You're learning. After two weeks, no. You'll write detailed prompts faster than you'd code the solution. Plus, the AI generates it in seconds. You're trading your time for AI time. That's a good trade when you're a bottleneck.
Q: What if I don't know all the requirements upfront?
A: State what you know and what you're uncertain about. "We're not sure about X yet, but plan for flexibility here." The AI will generate defensive, adaptable code. Then when you know more, refine the prompt and iterate.
Q: How much detail is too much?
A: You can't really over-specify. Add more detail if the generated code is wrong. Keep adding constraints and examples until the output is what you want. There's no ceiling on good specification.
Q: Does the AI actually read all that detail?
A: Yes. Modern language models process the entire prompt. Every constraint, example, and edge case influences the output. More context always produces better results (up to context length limits).
Q: How do I know if my prompt is good before I ask the AI?
A: Ask yourself: "If I printed this prompt and handed it to a contractor with no other information, could they build what I'm imagining?" If yes, the prompt is good. If no, keep refining.
Q: Should I iterate on prompts or the generated code?
A: Always iterate on the prompt first. When code is wrong, ask: "Was my specification unclear?" Fix the prompt, regenerate. You'll get different solutions and better code faster. Manual code fixes are a short-term band-aid. Better prompts are a long-term win.
Q: How specific should I be about the tech stack?
A: Very specific if you have constraints. "Use only built-in Python libs, no pip packages" vs. "Use any reasonable library" produce completely different code. Be explicit about dependencies, framework versions, and compatibility requirements. Version constraints matter, Python 3.8 vs. 3.11 changes what's available. Same with npm versions, Java versions, etc.
Case Study: Refining a Real-World Prompt
A fintech company needed to parse regulatory filing data. Here's how their prompt evolved:
Version 1 (Vague): "Write a function to extract data from SEC filings in HTML format."
Result: Generic parser that grabbed all text. Didn't understand SEC document structure. Extracted noise along with signal. Accuracy: 38%.
Version 2 (Better): "Parse 10-K SEC filings (HTML format) and extract: company name, fiscal year, revenue, net income. Return as JSON."
Result: Improved. Found some fields correctly. Missed fields that weren't labeled consistently across filings. Accuracy: 61%.
Version 3 (Strong): "Parse 10-K SEC filings in HTML format. Extract these fields with their exact values:
- company_name: from the document title and header
- fiscal_year_end: YYYY-MM-DD format, from the cover page
- total_revenue: numeric value in dollars, from Consolidated Statements of Operations
- net_income: numeric value in dollars, same statement
- total_assets: numeric value from Consolidated Balance Sheet
Important: SEC filings use specific table structures. Revenue is ALWAYS in a table with rows for 'Net revenues' or 'Total revenues'. Net income is in a row labeled 'Net income (loss)'. Don't guess, extract from the actual document sections.
Handle these cases:
- Multiple fiscal years in one filing (extract only the current year)
- Revenue shown in thousands or millions (preserve the value as-is, add a 'unit' field: 'thousands' or 'millions')
- Missing values (return null, not empty string)
- Filing might use variations like 'Net revenues' or 'Total net revenues' (handle both)
Constraints:
- Pure HTML parsing (no external APIs or web scraping)
- Must work for filings from 2020-2026
- Return JSON with fields: company_name, fiscal_year_end, total_revenue, total_revenue_unit, net_income, net_income_unit, confidence_score (0-1 estimate of accuracy)
Here's an example output:
{
'company_name': 'Apple Inc.',
'fiscal_year_end': '2024-09-30',
'total_revenue': 391035,
'total_revenue_unit': 'millions',
'net_income': 93736,
'net_income_unit': 'millions',
'confidence_score': 0.97
}"
Result: Much better. Parser understood document structure. Correctly extracted 91% of fields. Confidence scores let analysts know when to manually review borderline cases.
Why the improvement?** The third version gave the AI: domain context (what an SEC 10-K is), specific requirements (exact fields and locations), variations to handle (multiple formats for same data), constraints (HTML only, no APIs), concrete examples (output format), and a scoring mechanism (confidence). It treated the problem as a specification instead of a casual request.
Financial impact:** The company processed 5,000 filings annually. At version 1, 62% failure rate meant 3,100 manual corrections ($200K/year in analyst time). At version 3, 9% failure rate meant 450 corrections ($35K/year). Investment in better prompting: 8 hours. Savings: $165K/year.
Testing Your Prompts Before Production
Professional teams don't just ship prompts. They test them. Here's how:
Create a test set: Take 10-20 representative examples of what your prompt needs to handle. Run them through the prompt. Evaluate: Are outputs correct? Are they consistent? Are edge cases handled?
Measure accuracy: For data extraction (like our SEC filing example), accuracy = correct fields / total fields. For code generation, accuracy = code runs without errors + produces correct output + handles edge cases. For text generation, accuracy = human reviewer says "this is good enough to use."
Set a threshold:** "We won't deploy until accuracy is >85%" (adjust based on tolerance for errors). Don't deploy prompts that fail frequently. That damages trust and increases operational burden.
Version your prompts: Treat them like code. "v1.0," "v1.1" (fixed edge case X), "v2.0" (major redesign). Keep the versions. When v2.0 performs worse than v1.0, revert. When v1.1 is better, commit to it.
Monitor in production: Log every output. Occasionally sample outputs to verify they're still correct. If accuracy drops (concept drift), retrain the prompt or update it. Don't assume it will work forever.
On This Page
Watch the Lecture
Why Most Prompts Fail
The Anatomy of a Great Technical Prompt
The Prompt Writing Process
Common Patterns That Work
Real Examples of Prompt Evolution
What to Do Monday Morning
FAQ
Chapter Details
Part of
Skill.re