Prompt Engineering for Developers — Get Better Code from AI
Master the art of writing effective prompts for AI coding assistants. Learn techniques that will transform your AI interactions.
Why Prompt Engineering Matters
The difference between a mediocre AI response and an excellent one often comes down to how you ask. Good prompt engineering can:
- Reduce iterations — Get working code in fewer tries
- Improve quality — Get cleaner, more maintainable code
- Save time — Less back-and-forth fixing issues
- Unlock capabilities — Access advanced features you didn’t know existed
This guide covers practical techniques you can use immediately with any AI coding assistant.
The Basic Structure
A Good Prompt Has Four Parts
- Context — What you’re working on
- Task — What you want the AI to do
- Constraints — Any specific requirements
- Format — How you want the output
Template
[Context]
I'm working on a React e-commerce app with TypeScript and Tailwind.
[Task]
Create a shopping cart component that displays added items.
[Constraints]
- Show product image, name, price, and quantity
- Include +/- buttons to adjust quantity
- Show total price at the bottom
- Use the existing theme colors
[Format]
React functional component with TypeScript
Technique 1: Provide Context
Bad Prompt
Create a button component
Good Prompt
Create a button component for a SaaS dashboard. The design system uses:
- Primary color: #4F46E5 (indigo-600)
- Border radius: 8px
- Font: Inter
- Sizes: sm (32px), md (40px), lg (48px)
- States: default, hover (darken 10%), active (darken 15%), disabled (50% opacity)
Why It Works
The AI understands your constraints and generates code that fits your existing design system.
Technique 2: Show, Don’t Just Tell
Bad Prompt
Add error handling to this API call
Good Prompt
Add error handling to this API call. Follow this pattern from our codebase:
// In src/lib/api.ts, we handle errors like:
try {
const data = await fetchData();
return { success: true, data };
} catch (error) {
if (error instanceof ValidationError) {
return { success: false, error: error.message };
}
console.error('API Error:', error);
return { success: false, error: 'Something went wrong' };
}
Apply this same pattern to the userService.ts file
Why It Works
Providing examples of your existing patterns ensures the AI generates consistent, idiomatic code.
Technique 3: Break Down Complex Tasks
Bad Prompt
Build a complete authentication system with email/password login, OAuth with Google and GitHub, password reset, email verification, session management, and protected routes
This is too much! The AI will either fail or produce shallow code.
Good Prompt (Iterative)
Prompt 1:
Create a basic authentication system with:
- User model with email and password_hash fields
- Register endpoint that validates email format and password strength
- Login endpoint that returns a JWT token
Prompt 2:
Add OAuth login with Google. Use NextAuth.js with these constraints:
- Use the existing user table
- Link OAuth accounts to existing users by email
- Store provider info in a separate table
Prompt 3:
Add password reset functionality:
- Forgot password page with email input
- Send reset email (mock the email sending for now)
- Reset password page with new password input
- Validate password strength (min 8 chars, 1 number, 1 special char)
Why It Works
Breaking complex tasks into smaller, focused prompts produces higher quality code.
Technique 4: Specify the “Don’ts”
Bad Prompt
Make this function faster
The AI might make changes you don’t want.
Good Prompt
Optimize this function for performance. Do NOT:
- Remove type safety
- Change the public API
- Use unsafe methods like innerHTML
- Remove error handling
DO:
- Use memoization where appropriate
- Optimize database queries
- Add caching
Why It Works
Explicitly stating constraints prevents unwanted changes and guides the AI toward solutions that fit your needs.
Technique 5: Use Role Playing
Bad Prompt
Write tests for this component
Good Prompt
Act as a senior React developer and TDD practitioner. Write tests for this component:
1. First, analyze the component's functionality
2. Write tests that cover:
- Happy path (normal usage)
- Edge cases (empty state, loading state)
- Error states
3. Use Vitest and React Testing Library
4. Follow our testing conventions in src/__tests__/
Why It Works
Assigning a role helps the AI adopt the right mindset and follow appropriate patterns.
Technique 6: Reference Specific Files
Bad Prompt
Add form validation
Good Prompt
Look at src/components/LoginForm.tsx and add form validation:
- Validate email format
- Validate password (min 8 chars)
- Show inline error messages below each field
- Disable submit button while validation fails
- Use the existing error message component from src/components/ErrorMessage.tsx
Why It Works
Direct file references give the AI exact context, reducing hallucinations.
Technique 7: Chain of Thought
Bad Prompt
Fix this bug in the payment processing
Good Prompt
The payment is being charged twice when the user clicks the submit button rapidly. Trace through the code flow:
1. First, explain how the payment flow currently works
2. Identify where the double-charge happens
3. Implement a fix that:
- Disables the button during processing
- Uses idempotency keys to prevent duplicate charges
- Shows appropriate loading states
Explain your reasoning at each step
Why It Works
Asking for explanation ensures the AI thinks through the problem and produces better solutions.
Technique 8: Use Constraints to Guide
Bad Prompt
Refactor this file
Good Prompt
Refactor this file to improve readability. Constraints:
- Keep all existing functionality
- Maximum function length: 20 lines
- Extract repeated code into utilities
- Add JSDoc comments to exported functions
- Use early returns for error cases
- Keep the same performance characteristics
Why It Works
Clear constraints give the AI a framework to work within.
Real-World Prompt Examples
Code Generation
Generate a TypeScript interface for a GitHub repository:
- id (number)
- name (string)
- full_name (string - "owner/repo")
- description (string | null)
- owner (object with login and avatar_url)
- stargazers_count (number)
- forks_count (number)
- language (string | null)
- created_at (Date)
- updated_at (Date)
Bug Fixing
Fix the memory leak in this React component. The issue is that the event listener added in useEffect is never cleaned up. Show me:
1. The problem in the current code
2. The corrected code
3. How the cleanup function should work
Code Review
Review this PR for:
- Security vulnerabilities
- Performance issues
- Code smells
- Missing edge cases
- Inconsistents with our coding standards
Focus on the files in src/api/
Refactoring
Convert this class component to a functional component with hooks. Maintain:
- All existing functionality
- Prop types
- Default props
- Same render output
Then explain what changed and why each change was made.
Common Mistakes to Avoid
1. Being Too Vague
❌ "Make this better"
✅ "Refactor to use async/await instead of callbacks"
2. Missing Context
❌ "Add authentication"
✅ "Add JWT-based authentication using our existing user table"
3. Too Many Changes at Once
❌ "Rewrite the entire app with new UI and add login"
✅ (Break into multiple prompts)
4. Not Reviewing Output
Always review AI-generated code before accepting. Check for:
- Security issues
- Logic errors
- Performance problems
- Missing edge cases
Key Insight: The best prompts are specific, structured, and iterative. Don’t try to get everything in one go — build up your application piece by piece.
Practice Exercise
Take one of your existing projects and try to:
- Generate a component using a vague prompt — note the issues
- Regenerate with a detailed, structured prompt — compare results
- Iterate with follow-up prompts — see how much better the result gets
You’ll be surprised at the difference good prompting makes!
Summary
Good prompt engineering is a skill that improves with practice. Remember:
- Provide context — Tell the AI about your project
- Show examples — Reference existing patterns
- Break it down — Tackle complex tasks incrementally
- Be specific — Clear prompts yield better results
- Iterate — Build up gradually, refine often
The time you invest in writing better prompts will pay dividends in code quality and development speed.