You write code for a living. AI can make you faster — but only if you prompt it like a developer, not like a casual user. This guide covers the patterns that actually work for code generation, debugging, architecture, testing, and review.
Why Developer Prompting Is Different
Generic prompting advice — "be specific," "provide context," "define a role" — applies to developers too. But writing code has unique requirements that most prompt engineering guides ignore:
- Code must be correct, not just plausible. A blog post that's 90% right is useful. A function that's 90% right has a bug.
- Code has dependencies. The right answer depends on your language, framework, version, existing codebase patterns, and runtime environment.
- Code must be testable. An answer you can't verify is worse than no answer.
- Code has security implications. A prompt that produces code with SQL injection is a liability, not a time-saver.
This guide treats AI as what it is for developers: a very fast, moderately reliable junior developer who reads documentation faster than you but doesn't understand your system. Your job is to brief it well enough to produce useful output, and to verify everything it generates.
The Developer Prompting Framework
Every effective coding prompt has four components. Miss one and quality drops sharply.
1. Technical Context
Tell the AI exactly what you're working with:
Language: TypeScript 5.4
Framework: Next.js 15 (App Router)
Database: PostgreSQL via Prisma ORM
Runtime: Node.js 22
Relevant packages: zod for validation, next-auth for authentication
Without this, the AI defaults to the most common patterns from its training data — which might be React class components, Express.js, or JavaScript without types. Explicit context prevents these mismatches.
2. Codebase Conventions
Your team has patterns. State them:
Conventions:
- Use server actions for mutations, not API routes
- Error handling: return Result<T, Error> types, no throwing
- Naming: camelCase for functions, PascalCase for components
- All database queries go through the repository layer
- Use zod schemas for all external inputs
This is the difference between getting generic code and getting code that fits your codebase.
3. The Specific Task
Be precise about what you need:
Vague: "Write a user authentication function"
Precise: "Write a verifySession server action that: (1) reads the session cookie, (2) validates it against the sessions table, (3) returns the user object if valid or redirects to /login if expired, (4) handles the case where the session exists but the user has been deleted."
The precise version prevents the AI from making assumptions about your auth flow, session storage, and error handling strategy.
4. Constraints and Quality Requirements
Explicitly state what the code must handle:
Requirements:
- Handle null/undefined inputs gracefully
- Include input validation with zod
- Add JSDoc comments for public functions
- No console.log — use structured logging via our logger utility
- Must pass strict TypeScript (no 'any' types)
- Include at least one edge case in the implementation
Code Generation Patterns
Pattern 1: The Specification Prompt
The most reliable pattern for generating substantial code. Provide a specification, get an implementation.
Implement a rate limiter middleware for our Express.js API.
Specification:
- Algorithm: Sliding window counter
- Storage: Redis (ioredis client, already configured as `redisClient`)
- Default limit: 100 requests per 15-minute window per IP
- Allow per-route override: `rateLimit({ limit: 10, window: '1m' })`
- Response on limit exceeded: 429 status with JSON `{ error: 'Rate limit exceeded', retryAfter: <seconds> }`
- Headers: Include X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
- Skip rate limiting for requests with a valid API key in X-API-Key header
Existing types to use:
interface RateLimitConfig {
limit: number;
windowMs: number;
keyGenerator?: (req: Request) => string;
}
Do not use external rate limiting packages — implement the Redis logic directly.
Include error handling for Redis connection failures (fail open, log the error).
This works because the specification removes ambiguity. The AI knows exactly what to build, what patterns to follow, and what edge cases to handle.
Pattern 2: The Transform Prompt
When you have existing code that needs modification:
Here is our current authentication middleware:
[paste existing code]
Refactor it to:
1. Replace the JWT verification with our new `verifyToken` function from `@/lib/auth`
2. Add support for API key authentication as a fallback (check X-API-Key header against the api_keys table)
3. Add a `requireRole` option that checks the user's role before proceeding
4. Keep the existing error response format
Do not change the function signature or the way it's mounted in the router.
Show me only the modified code — not the unchanged parts.
Key detail: "Do not change the function signature" prevents the AI from "improving" things you didn't ask for. Scope control is critical.
Pattern 3: The Test-First Prompt
Generate tests before implementation — this is often more valuable than generating the code itself.
Write comprehensive tests for a `calculateShipping` function with this signature:
function calculateShipping(params: {
weight: number; // kg
dimensions: { l: number; w: number; h: number }; // cm
destination: string; // country code
method: 'standard' | 'express' | 'overnight';
}): { cost: number; estimatedDays: number; carrier: string }
Use Vitest. Include tests for:
- Standard cases for each shipping method
- Volumetric weight calculation (when dimensional weight > actual weight)
- International vs domestic shipping
- Edge cases: zero weight, oversized packages, unsupported countries
- Boundary values: exactly at weight tier thresholds
- Error cases: negative dimensions, empty destination
Use descriptive test names that explain the business rule being tested.
Do not mock anything — test the pure function directly.
Pattern 4: The Bug Fix Prompt
When debugging, context is everything. Don't just paste the error — provide the full picture:
Bug: Users intermittently get a 500 error when updating their profile.
Error message:
PrismaClientKnownRequestError: Unique constraint failed on the fields: (email)
at Object.request (prisma-client/runtime/library.js:...)
The relevant code:
[paste the update function]
The database schema:
model User {
id String @id @default(cuid())
email String @unique
name String
// ...
}
What I know:
- This only happens when two users try to update their profile at the same time
- It happens more on Monday mornings (high traffic)
- The email field is pre-filled and users rarely change it
Why does updating a profile cause a unique constraint violation on email if the email hasn't changed? Walk through the possible causes and suggest a fix.
The AI now has enough context to reason about the problem — it's likely a race condition or an upsert that's unnecessarily including email in the update.
Debugging Patterns
The Rubber Duck++ Method
The classic rubber duck debugging technique — explaining the problem to find the solution — works exceptionally well with AI because the "duck" can actually respond.
I'm debugging a memory leak in our Node.js service. Here's what I know:
Symptoms:
- Heap usage grows ~50MB/hour under normal load
- Process restarts every 4-6 hours when it hits the 512MB limit
- No leak under zero traffic — only when handling requests
- Started after last week's deployment (commit range: abc123..def456)
What changed in that deployment:
- Added WebSocket support for real-time notifications
- Switched from `node-fetch` to native `fetch`
- Added a new middleware for request timing
My investigation so far:
- Heap snapshot shows growing Map entries in what looks like the notification store
- The WebSocket connections seem to be properly cleaned up on disconnect
- The timing middleware stores request durations in a Map keyed by request ID
Based on this evidence, what are the most likely root causes, ranked by probability? For each, suggest a specific diagnostic step I can take to confirm or rule it out.
This is far more effective than "my Node.js app has a memory leak." The AI can reason about the specific evidence and guide your investigation.
The Error Message Decoder
When you hit an error you don't understand:
I'm getting this error and I don't understand what's causing it:
TypeError: Cannot read properties of undefined (reading 'map')
at UserList (app/users/page.tsx:24:31)
at renderWithHooks (react-dom/cjs/react-dom-server.js:...)
The component:
[paste the component code]
The data fetching:
[paste the data fetching code]
This is a Next.js 15 App Router page with server-side data fetching.
Explain: (1) What exactly is undefined and why, (2) The root cause, (3) The fix, (4) How to prevent this class of error in the future.
The Performance Profiler
This database query is slow (1.2s average, 3s p99):
[paste the query]
Table sizes: users (500K rows), orders (2M rows), products (10K rows)
Existing indexes: [list them]
Database: PostgreSQL 16
Analyze:
1. What's making this slow? (explain the query plan issues)
2. What indexes would fix it?
3. Can the query itself be restructured for better performance?
4. Are there any caching strategies that would help?
Show the EXPLAIN ANALYZE output you'd expect before and after the fix.
Architecture and Design Prompts
System Design Review
Review this architecture for a [type of system]:
[describe or diagram the architecture]
Evaluate:
1. Single points of failure
2. Scalability bottlenecks (what breaks at 10x current load?)
3. Data consistency issues
4. Security vulnerabilities
5. Operational complexity (what's hardest to monitor and debug?)
For each issue found, suggest a specific fix with trade-offs.
Assume we're a team of [size] and we can't over-engineer.
API Design
Design a REST API for [feature description].
Entities involved: [list]
Key operations: [CRUD + any special operations]
Authentication: [method]
Consumers: [who calls this API]
For each endpoint, specify:
- Method + path
- Request body/params with types
- Response body with types (success and error)
- Status codes
- Rate limiting considerations
- Pagination approach (if applicable)
Follow our conventions: plural nouns for resources, snake_case for JSON fields, ISO 8601 for dates, cursor-based pagination.
Database Schema Design
Design a PostgreSQL schema for [feature].
Requirements:
[list business requirements]
Constraints:
- Expected data volume: [numbers]
- Read/write ratio: [estimate]
- Must support [specific queries]
- Must handle [specific edge cases]
Include:
- Table definitions with types and constraints
- Indexes for common queries
- Foreign key relationships
- Any check constraints or triggers needed
- Migration SQL
Do not over-normalize. Prioritize query performance for the common read path over write normalization.
Code Review Prompts
The Security Reviewer
Review this code for security vulnerabilities:
[paste code]
Check specifically for:
- SQL/NoSQL injection
- XSS (stored, reflected, DOM-based)
- CSRF vulnerabilities
- Authentication/authorization bypasses
- Insecure data exposure (logs, error messages, API responses)
- Path traversal
- Race conditions
- Insecure deserialization
- Missing input validation
For each finding: explain the attack vector, show a proof-of-concept exploit, rate the severity (critical/high/medium/low), and provide the fixed code.
The Refactoring Advisor
This function works but it's become unmaintainable:
[paste the messy function]
It currently handles: [list what it does]
Refactor it following these principles:
1. Single responsibility — each function does one thing
2. Pure functions where possible — no side effects
3. Descriptive names — the code should read like documentation
4. Error handling — make failure modes explicit
5. Testability — each extracted function should be independently testable
Show the refactored code with brief comments explaining each extraction decision. Preserve the existing behavior exactly — this is a refactor, not a rewrite.
Documentation Prompts
API Documentation
Generate API documentation for this endpoint:
[paste the route handler / controller]
Format as a markdown API reference including:
- Endpoint (method + path)
- Description (what it does and when to use it)
- Authentication requirements
- Request parameters (path, query, body) with types and validation rules
- Response schema (success + error cases)
- Example request (curl)
- Example response (JSON)
- Error codes with explanations
- Rate limiting details
Infer validation rules from the code. If the code doesn't validate something it should, note that as a finding.
README Generation
Generate a README.md for this project:
Project: [name and one-line description]
Language: [language/framework]
Purpose: [what it does]
The README should include:
1. One-paragraph description
2. Prerequisites (language version, tools needed)
3. Quick start (clone, install, run — in 3 commands or fewer)
4. Configuration (environment variables with descriptions)
5. Project structure (key directories and what's in them)
6. Common tasks (test, lint, build, deploy)
7. Contributing guidelines (branch naming, commit format, PR process)
Base the project structure section on this actual file tree:
[paste output of tree -L 2]
Keep it concise. Developers read READMEs to get unblocked, not to be entertained.
Tool-Specific Tips
Different AI coding tools have different strengths. Use the right tool for the right task.
ChatGPT (GPT-4o)
Best for: Explaining concepts, designing systems, brainstorming approaches, generating boilerplate.
ChatGPT excels at higher-level tasks where you need reasoning and explanation alongside code. Use it for architecture discussions, exploring trade-offs, and understanding unfamiliar codebases or libraries.
Weakness: Can generate plausible-looking code with subtle bugs, especially for complex logic. Always test.
Claude (Sonnet / Opus)
Best for: Long code files, refactoring, careful analysis, following complex constraints.
Claude's large context window (200K tokens) means you can paste entire files or multiple files and ask for cross-cutting analysis. It also follows constraints more literally than GPT-4o — if you say "don't change the function signature," Claude is more likely to comply.
Use XML-style tags for structure:
<codebase>
[paste multiple files]
</codebase>
<task>
Refactor the authentication flow to use the new token format.
</task>
<constraints>
- Do not modify the User model
- Keep backward compatibility with existing tokens for 30 days
- All changes must be in the auth/ directory
</constraints>
GitHub Copilot
Best for: Inline code completion, writing implementations from function signatures, and generating repetitive patterns.
Copilot works best when your code provides strong signals:
- Descriptive function names —
calculateVolumetricShippingWeightgives better suggestions thancalc - Type annotations — fully typed function signatures produce dramatically better completions
- Comments before functions — a JSDoc comment describing the function's behavior is the strongest signal
- Test file patterns — Copilot excels at generating additional test cases once it sees the pattern from your first 2-3 tests
Cursor
Best for: Codebase-aware refactoring, multi-file changes, and working with context from your actual project.
Cursor's advantage is codebase indexing — it reads your entire project and uses it as context. This means:
- It knows your types, your patterns, your naming conventions
- It can make changes across multiple files consistently
- It understands your import paths and project structure
Use Cursor for refactors that touch many files. Use ChatGPT or Claude for design discussions and learning.
Anti-Patterns to Avoid
Anti-Pattern 1: Blind Copy-Paste
Never paste AI-generated code into production without reading and understanding every line. The code might:
- Use a deprecated API you don't notice
- Have a subtle off-by-one error
- Import a package you don't have installed
- Handle errors by silently swallowing them
- Use a pattern that contradicts your codebase conventions
Rule: If you can't explain what the code does line-by-line, don't ship it.
Anti-Pattern 2: Over-Relying on AI for Core Logic
AI is excellent at boilerplate, tests, documentation, and standard patterns. It is unreliable for:
- Complex business logic with many edge cases
- Performance-critical algorithms
- Security-sensitive code (auth, encryption, access control)
- Concurrent/async code with subtle race conditions
Use AI to generate a first draft, then carefully review and test these areas.
Anti-Pattern 3: Prompt Scope Creep
Don't ask the AI to do too much in one prompt. "Implement a complete user management system with authentication, authorization, CRUD operations, admin panel, audit logging, and password reset flow" will produce mediocre code across the board.
Break it into focused tasks:
- Design the database schema
- Implement the authentication flow
- Add role-based authorization
- Build the CRUD endpoints
- Add audit logging
Each focused prompt produces higher quality output.
Anti-Pattern 4: Not Providing Error Context
"My code doesn't work" is the worst possible prompt. Always include:
- The exact error message (full stack trace)
- The code that's failing
- What you expected to happen
- What actually happens
- What you've already tried
Anti-Pattern 5: Ignoring the Training Data Cutoff
AI models have a knowledge cutoff date. They may not know about:
- The latest version of your framework
- Recently introduced APIs or deprecated ones
- New security vulnerabilities
- Breaking changes in recent releases
When working with bleeding-edge tech, always specify the version and consider linking to relevant documentation in your prompt.
Measuring AI's Impact on Your Development
Track these metrics to quantify whether AI is actually helping:
| Metric | How to Measure | Good Signal |
|--------|---------------|-------------|
| Time to first commit | Track from task start to first PR | 20-40% reduction |
| Test coverage | Compare before/after AI-assisted testing | Coverage increases without slowing velocity |
| Bug rate | Track bugs per sprint/release | Should not increase despite faster shipping |
| Documentation freshness | Audit doc accuracy monthly | Documentation keeps pace with code changes |
| Code review turnaround | Time from PR open to merge | AI-assisted PRs need fewer revision cycles |
The goal is not to write more code. It's to ship better software faster.
Start Today
- Pick one pattern from this guide that matches your current work
- Try it on a real task (not a toy example)
- Compare the output to what you would have written manually
- Iterate on the prompt until the output quality matches your standards
- Save the prompt as a template for reuse
For quick prompt creation, the SurePrompts builder has coding-specific templates for code generation, debugging, review, and documentation. Select a template, add your technical context, and get a production-ready prompt in seconds.
The developers who get the most from AI aren't the ones who use it for everything. They're the ones who know exactly which tasks AI accelerates and which tasks still require human judgment. Build that instinct by experimenting deliberately, and always verify the output.