Skip to content

Prompt Engineering for Developers: Writing Better AI Instructions

Learn the art and science of writing effective prompts for AI coding assistants. Transform vague requests into precise instructions that deliver better results.

Reading time: 9 minutes Category: Getting Started Published: January 7, 2026

Introduction

The difference between “AI doesn’t understand my code” and “AI just saved me 4 hours” often comes down to one thing: how you write your prompts.

Prompt engineering isn’t magic - it’s a skill you can learn. This guide teaches you the patterns, techniques, and principles that turn vague requests into precise instructions that consistently deliver great results.

The Anatomy of a Great Prompt

Every effective prompt has these components:

  1. Context: What the AI needs to know
  2. Intent: What you want to achieve
  3. Constraints: Rules and limitations
  4. Format: How you want the output

Bad Prompt Example

Make this code better

Good Prompt Example

Refactor this authentication function to:
- Use async/await instead of callbacks
- Add TypeScript types for all parameters
- Implement proper error handling with specific error classes
- Maintain backwards compatibility with existing callers
- Add JSDoc comments explaining the authentication flow
Current constraints:
- Must work with Node.js 18+
- Cannot change the function signature
- Must maintain existing error codes
[paste code here]

Pattern 1: The Context-Intent-Constraint (CIC) Pattern

Template:

[CONTEXT]: I'm working on [project type] using [tech stack]
[INTENT]: I need to [specific goal]
[CONSTRAINTS]:
- Must [requirement 1]
- Cannot [limitation 1]
- Should [preference 1]
[CODE/DETAILS]

Example:

CONTEXT: I'm building a REST API with Express and PostgreSQL
INTENT: I need to implement pagination for the /users endpoint
CONSTRAINTS:
- Must support both offset and cursor-based pagination
- Cannot break existing clients (maintain backwards compatibility)
- Should include total count in response headers
- Must handle edge cases (empty results, invalid params)
Current endpoint:
[paste code]

Pattern 2: The Specification-Driven Prompt

Use this when you need precise output matching specific requirements.

Template:

Create a [component/function/class] that:
Functionality:
1. [Feature 1]
2. [Feature 2]
3. [Feature 3]
Technical Requirements:
- Language/Framework: [specific version]
- Dependencies: [allowed/required libraries]
- Performance: [specific goals]
- Security: [specific considerations]
Input: [describe inputs]
Output: [describe outputs]
Error Handling: [describe error scenarios]
Example Usage:
[show how it should be used]

Example:

Create a React hook for debounced search that:
Functionality:
1. Accepts search query and delay time
2. Debounces API calls to prevent excessive requests
3. Handles loading states and errors
4. Cancels pending requests on unmount
Technical Requirements:
- React 18 with TypeScript
- Use fetch API (no axios)
- Return type-safe results
- Handle AbortController for cancellation
Input:
- searchTerm: string
- delay: number (default 300ms)
- fetchFn: (term: string) => Promise<T>
Output:
- data: T | null
- loading: boolean
- error: Error | null
Error Handling:
- Network errors
- Aborted requests
- Invalid JSON responses
Example Usage:
const {data, loading, error} = useDebounce Search(query, 300, fetchUsers)

Pattern 3: The Example-Driven Prompt

Show the AI what you want by providing examples.

Template:

Transform this code from [style A] to [style B].
Example of what I want:
Before:
[example before]
After:
[example after]
Now apply the same transformation to:
[actual code]

Example:

Convert these callback-based functions to async/await.
Example of what I want:
Before:
function getUser(id, callback) {
db.query('SELECT * FROM users WHERE id = ?', [id], (err, result) => {
if (err) return callback(err);
callback(null, result);
});
}
After:
async function getUser(id) {
const result = await db.query('SELECT * FROM users WHERE id = ?', [id]);
return result;
}
Now apply the same transformation to:
[paste your callback-based code]

Pattern 4: The Iterative Refinement Pattern

Start broad, then refine based on output.

Round 1:

Create a user authentication system with JWT tokens

Round 2 (after reviewing output):

Good start. Now add:
- Refresh token rotation
- Token blacklisting on logout
- Rate limiting on login attempts (max 5 per 15 minutes)
- Password requirements (min 12 chars, special chars, numbers)

Round 3 (after further review):

The rate limiting needs improvement. Use Redis for distributed rate limiting
instead of in-memory Map. Include:
- Sliding window algorithm
- Different limits for successful vs failed attempts
- Admin bypass capability

Pattern 5: The Test-Driven Prompt

Start with tests, let AI implement to pass them.

Template:

Here are the tests that must pass:
[paste test code]
Implement the function/class to make these tests pass.
Requirements:
- [specific requirement 1]
- [specific requirement 2]

Example:

Here are the tests that must pass:
test('calculateDiscount applies percentage discount correctly', () => {
expect(calculateDiscount(100, 20)).toBe(80);
});
test('calculateDiscount handles minimum purchase threshold', () => {
expect(calculateDiscount(50, 20, {minPurchase: 100})).toBe(50);
});
test('calculateDiscount caps maximum discount', () => {
expect(calculateDiscount(100, 90, {maxDiscount: 50})).toBe(50);
});
Implement calculateDiscount to make these tests pass.
Requirements:
- TypeScript with strict mode
- Handle edge cases (negative values, invalid percentages)
- JSDoc comments

Common Prompt Mistakes to Avoid

❌ Mistake 1: Being Too Vague

Fix this bug

✅ Better:

This function throws "Cannot read property 'id' of undefined" when
user is not authenticated. Add proper null checks and return a clear
error message instead.

❌ Mistake 2: No Context

Add validation

✅ Better:

Add Zod validation for this API endpoint. Validate:
- email: valid email format, max 255 chars
- password: min 12 chars, must contain special char and number
- age: optional number between 13-120
Return 400 with field-specific error messages on validation failure

❌ Mistake 3: Assuming Knowledge

Make it work with our auth system

✅ Better:

Integrate with our JWT-based auth system. We store tokens in httpOnly
cookies with 15-minute expiry. Check for valid token in Authorization
header or cookies. Return 401 if missing/invalid. User object should be
attached to req.user for downstream middleware.

Advanced Techniques

Technique 1: Chain of Thought Prompting

Ask AI to explain its reasoning:

Before implementing, first explain:
1. What approach you'll take and why
2. What edge cases need handling
3. What tradeoffs exist in your approach
Then implement the solution.

Technique 2: Role Assignment

Frame the AI as an expert:

Act as a senior security engineer reviewing this authentication code.
Identify all security vulnerabilities and explain:
- Why each is a problem
- How it could be exploited
- How to fix it securely

Technique 3: Structured Output

Request specific formats:

Analyze this codebase and provide:
1. Architecture Overview (2-3 sentences)
2. Key Components (bullet list with descriptions)
3. Data Flow (Mermaid diagram)
4. Dependencies (table with name, version, purpose)
5. Potential Issues (numbered list with severity)

Building Your Prompt Library

Create reusable templates for common tasks:

File: prompts/refactoring.md

## Standard Refactoring Prompt
Refactor this [function/class/module] to improve [quality aspect].
Current issues:
- [issue 1]
- [issue 2]
Goals:
- [goal 1]
- [goal 2]
Constraints:
- Must maintain backwards compatibility
- Cannot change public API
- Should follow [coding standard]
[code here]

File: prompts/testing.md

## Comprehensive Test Generation
Generate [test framework] tests for this [component type].
Coverage requirements:
- Happy path scenarios
- Edge cases: [list specific edge cases]
- Error scenarios: [list error conditions]
- Performance: [any performance requirements]
Test organization:
- Use describe blocks for logical grouping
- Clear test names following "should [expected behavior]" pattern
- Setup/teardown for shared state
- Mock external dependencies: [list what to mock]
[code to test]

Measuring Prompt Effectiveness

Track what works:

Prompt TypeSuccess RateAvg. IterationsUse For
CIC Pattern85%1.2General tasks
Specification-Driven95%1.1Precise requirements
Example-Driven80%1.5Style transformations
Test-Driven90%1.3Function implementation

Conclusion

Great prompts are:

  • Specific: Clear requirements and constraints
  • Contextual: Relevant background information
  • Structured: Organized and easy to parse
  • Complete: All necessary details included

Start with these patterns, build your prompt library, and refine based on results. Within weeks, you’ll develop an intuition for what works.

Remember: The goal isn’t to write the perfect prompt every time - it’s to write prompts that get you 80% of the way in one shot, then iterate to 100%.

For a structured framework that helps organize AI-assisted development workflows, check out Claude Zen.