Writing Clear, Actionable Instructions
Write precise, actionable prompts that give AI assistants the specific direction needed for quality code generation.
Writing Clear, Actionable Instructions for AI Code Assistants
The difference between frustrating AI coding sessions and productive ones often comes down to one thing: how clearly you communicate what you want. Vague instructions produce vague code. Precise, actionable prompts produce code that works.
Think of AI code assistants like extremely talented junior developers who know syntax perfectly but need clear direction. They won't guess your requirements—they'll interpret your words literally. This lesson will teach you how to write instructions that get results on the first try.
Why Clarity Matters in AI-Assisted Coding
When you write unclear prompts, you create a debugging loop before you've even written code. The AI generates something that's technically valid but doesn't match your intent. You clarify. It tries again. You iterate. Minutes turn into hours.
Clear instructions cut through this waste. They help you:
- Get working code faster: Less back-and-forth means more building
- Reduce errors: Specific constraints prevent common mistakes
- Build confidence: Predictable results make AI tools feel reliable
- Scale your work: Good prompting patterns become reusable templates
Let's look at what makes instructions clear and actionable.
The Three Components of Clear Instructions
Every effective prompt contains three elements:
- What you want built (the outcome)
- How it should work (the behavior)
- Why it matters (the constraints)
Missing any of these creates ambiguity.
Vague vs. Clear: A Real Example
Vague:
Create a login function
Clear:
Create a login function that:
- Accepts email and password as parameters
- Returns a JWT token on success
- Throws a ValidationError for invalid email format
- Throws an AuthenticationError for wrong credentials
- Uses bcrypt for password comparison
- Logs failed attempts for security monitoring
The vague version leaves everything to interpretation. The clear version specifies behavior, error handling, security approach, and observability. The AI now has a complete picture.
Start with the Outcome
Always begin by stating exactly what you're building. Use concrete nouns and action verbs.
Weak opening:
I need something for users
Strong opening:
Create a UserProfile component that displays user information
The strong version names the artifact (UserProfile component) and its purpose (displays user information). This immediately frames everything that follows.
Be Specific About the Artifact Type
Different code artifacts have different expectations. Specify:
- Functions: "Create a pure function that..."
- Classes: "Build a class that implements..."
- Components: "Create a React component that renders..."
- APIs: "Design a REST endpoint that handles..."
- Tests: "Write unit tests that verify..."
This tells the AI what patterns and conventions to follow.
Define the Behavior Precisely
After stating what you want, describe how it should work. Break complex behaviors into bullet points.
Example: Creating a data validation function
Create a validateUserInput function that:
Input:
- Accepts an object with username, email, and age properties
Validation rules:
- username: 3-20 characters, alphanumeric only
- email: valid email format
- age: number between 13 and 120
Output:
- Returns { valid: true, errors: [] } if all checks pass
- Returns { valid: false, errors: ['error messages'] } if any check fails
- Each error message should specify which field failed and why
Behavior:
- Check all fields even if one fails (don't short-circuit)
- Return errors in the order: username, email, age
Notice how this leaves nothing to chance. Input format, each validation rule, output structure, and execution behavior are all specified. Related to this approach is the context-constraints lesson, which covers how to set boundaries that prevent unwanted behaviors.
Use Examples to Eliminate Ambiguity
Sometimes the best way to communicate intent is to show, not tell. Include example inputs and expected outputs.
Create a formatCurrency function.
Examples:
formatCurrency(1234.5) → "$1,234.50"
formatCurrency(999) → "$999.00"
formatCurrency(0.5) → "$0.50"
formatCurrency(1234567.89) → "$1,234,567.89"
Requirements:
- Always show 2 decimal places
- Use comma as thousands separator
- Prefix with dollar sign
- Handle numbers up to billions
Examples act as test cases and specification simultaneously. The AI can pattern-match against them. For more on this technique, see few-shot-patterns.
Specify Your Constraints
Constraints are requirements that limit how something should be built. They're as important as features.
Technical Constraints
Create a file upload handler that:
- Accepts only .jpg, .png, and .pdf files
- Rejects files larger than 5MB
- Uses streaming to handle large files without loading into memory
- Works with Node.js streams API (not third-party libraries)
These technical boundaries prevent the AI from using approaches that won't work in your environment.
Performance Constraints
Write a search function that:
- Searches through up to 10,000 product records
- Returns results in under 100ms
- Uses binary search (assume sorted array)
- Prioritizes performance over code brevity
Performance requirements influence algorithm choice and implementation details.
Style and Convention Constraints
Create a data fetching hook following these conventions:
- Use TypeScript with explicit return types
- Follow React hooks naming (useXxx)
- Use async/await (not .then chains)
- Include JSDoc comments for public functions
- Match our existing error handling pattern with Result types
This ensures generated code fits your codebase style. For larger contexts, check out codebase-aware-prompting.
Action Verbs Matter
Different verbs signal different levels of completeness and quality.
Low-quality verbs:
- "Make a function..." (too casual)
- "Do something that..." (vague)
- "Help me with..." (unclear outcome)
High-quality verbs:
- "Create a function that..." (build from scratch)
- "Refactor this code to..." (improve existing)
- "Add error handling that..." (extend functionality)
- "Implement the algorithm that..." (specific approach)
- "Generate tests that verify..." (create verification)
Match your verb to your intention.
Break Down Complex Requests
Don't ask for everything at once. Complex features need decomposition.
Too complex:
Build a complete authentication system with login, signup,
password reset, email verification, and session management
Better approach:
First, let's create the user registration function:
- Accept email, password, and username
- Validate input format
- Hash password with bcrypt (10 rounds)
- Store user in database
- Return the created user object (without password)
- Handle duplicate email error
We'll add login and session management in subsequent steps.
This focused request gets better results. You can iterate toward the complete system. The breaking-down-projects lesson covers this strategy in depth.
Provide Context When Needed
Sometimes the AI needs background to make good decisions.
Without context:
Create a caching function
With context:
Create a caching function for our API client.
Context:
- Our API rate limits to 100 requests/minute
- Data freshness matters more than speed
- We're building a dashboard that refreshes every 30 seconds
- Cache should expire after 25 seconds to ensure fresh data
Create a function that:
- Caches GET request results by URL
- Invalidates cache after 25 seconds
- Bypasses cache if force=true parameter is passed
Context explains the "why" behind your constraints, helping the AI make aligned decisions. Learn more about managing what information to include in context-window-management.
Use Step-by-Step Instructions for Algorithms
When you need specific logic flow, spell out the steps.
Create a function that processes a shopping cart checkout.
Follow these steps in order:
1. Validate that cart is not empty
2. Check that all items are still in stock
3. Calculate subtotal from item prices
4. Apply any discount codes (if valid)
5. Calculate tax based on shipping address
6. Calculate shipping cost
7. Generate final total
8. Return breakdown object with all amounts
If any step fails, return an error object with:
- step: which step failed
- reason: why it failed
- data: relevant context
This algorithmic clarity prevents the AI from reordering logic or skipping steps. The chain-of-thought lesson explores this reasoning pattern further.
Specify Error Handling Explicitly
Don't assume the AI will handle errors the way you want.
Create a fetchUserData async function that:
Happy path:
- Fetches user data from /api/users/:id
- Returns parsed JSON response
Error handling:
- If user not found (404): throw NotFoundError with user ID
- If network fails: throw NetworkError with original error
- If response isn't JSON: throw ParseError with response text
- If request times out after 5s: throw TimeoutError
All custom errors should:
- Extend Error class
- Include timestamp
- Include request ID if available
Explicit error handling prevents silent failures and makes debugging easier. The error-resolution lesson covers strategies for handling AI-generated error scenarios.
Include Expected Edge Cases
Good code handles edge cases. Tell the AI what they are.
Create a function that merges two sorted arrays.
Edge cases to handle:
- One or both arrays are empty → return the non-empty array or empty array
- Arrays contain duplicate values → keep all duplicates in sorted order
- Arrays are different lengths → merge completely
- Input contains negative numbers → sort correctly
- Very large arrays (10,000+ items) → maintain O(n) time complexity
By naming edge cases, you ensure they're tested and handled.
Request the Right Level of Abstraction
Be explicit about whether you want low-level implementation or high-level design.
For implementation:
Write the complete implementation of a LRU cache class with:
- get(key) method
- put(key, value) method
- Maximum capacity of 100 items
- O(1) time complexity for both operations
- Use a Map and doubly-linked list
For design:
Describe the high-level architecture for a LRU cache system.
Include:
- Data structures needed
- Key methods and their signatures
- Time complexity analysis
- Memory considerations
Don't write the implementation yet.
This prevents the AI from over- or under-delivering. Related techniques appear in ai-architecture.
Common Clarity Mistakes to Avoid
Mistake 1: Assuming Shared Context
Bad:
Add the validation we discussed
The AI has no memory of previous conversations unless you provide context.
Good:
Add email and password validation:
- Email must match regex: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
- Password must be at least 8 characters with 1 number and 1 special char
Mistake 2: Using Domain Jargon Without Definition
Bad:
Implement the standard OAuth flow
"Standard" is ambiguous. OAuth has multiple flows.
Good:
Implement OAuth 2.0 Authorization Code flow:
1. Redirect to /authorize endpoint
2. Receive callback with code
3. Exchange code for access token
4. Store token securely
Mistake 3: Conflicting Requirements
Bad:
Make it fast and handle all edge cases thoroughly
Speed and thoroughness can conflict.
Good:
Prioritize performance for the happy path (90% of requests).
Edge case handling can be slower but must be correct.
Target: <50ms for valid input, <200ms for invalid input.
Mistake 4: Asking for Opinions
Bad:
What's the best way to structure this?
This invites bikeshedding.
Good:
Structure this using the repository pattern with:
- Interface defining data operations
- Concrete implementation for PostgreSQL
- Separate file for each
Make decisions, then ask for implementation. The top-mistakes lesson covers more pitfalls.
Putting It All Together: A Complete Example
Here's a well-structured prompt that combines all these principles:
Create a rate limiting middleware for Express.js.
Purpose:
Protect our API from abuse by limiting requests per user.
Requirements:
- Track requests by IP address
- Allow 100 requests per 15-minute window
- Use sliding window algorithm (not fixed window)
- Store request counts in Redis
- Return 429 status when limit exceeded
Response headers:
- X-RateLimit-Limit: maximum requests allowed
- X-RateLimit-Remaining: requests left in current window
- X-RateLimit-Reset: timestamp when limit resets
Error response format:
{
"error": "Rate limit exceeded",
"retryAfter": <seconds until reset>
}
Edge cases:
- If Redis is unavailable: log error and allow request (fail open)
- If IP address is missing: use 'unknown' as identifier
- Reset counter properly when window slides
Code style:
- Use async/await
- Add TypeScript types
- Include JSDoc comments
- Export as default function
This prompt leaves no ambiguity. The AI has everything needed to generate production-quality code.
Practice: Improving Your Prompts
The best way to develop this skill is deliberate practice. After each AI interaction:
- Review the output: Did it match your intent?
- Identify gaps: What was unclear in your prompt?
- Refine and retry: Rewrite the prompt more clearly
- Save patterns: Keep prompts that worked well
Over time, you'll develop prompt templates for common tasks. These become part of your workflow, making you faster and more consistent.
Next Steps
Clear instructions are the foundation of effective prompting, but there's more to learn:
- context-constraints: Set boundaries that prevent unwanted behaviors
- few-shot-patterns: Use examples to teach the AI your patterns
- iterating-output: Refine generated code through follow-up prompts
- code-gen-best-practices: Apply broader strategies for AI code generation
Master clear instructions first. Everything else builds on this foundation. The difference between developers who struggle with AI and those who thrive often comes down to communication clarity. You're now equipped to be in the latter group.