The quality of your instructions directly impacts the quality of OpenHands’ output. This guide shows concrete examples of good and bad prompts, explains why some work better than others, and provides principles for writing effective instructions.
Concrete Examples of Good/Bad Prompts
Bug Fixing Examples
Bad Example
Why it’s bad:
- No information about what the bug is
- No indication of where to look
- No description of expected vs. actual behavior
- OpenHands would have to guess what’s wrong
Good Example
Fix the TypeError in src/api/users.py line 45.
Error message:
TypeError: 'NoneType' object has no attribute 'get'
Expected behavior: The get_user_preferences() function should return
default preferences when the user has no saved preferences.
Actual behavior: It crashes with the error above when user.preferences is None.
The fix should handle the None case gracefully and return DEFAULT_PREFERENCES.
Why it works:
- Specific file and line number
- Exact error message
- Clear expected vs. actual behavior
- Suggested approach for the fix
Feature Development Examples
Bad Example
Add user authentication to my app.
Why it’s bad:
- Scope is too large and undefined
- No details about authentication requirements
- No mention of existing code or patterns
- Could mean many different things
Good Example
Add email/password login to our Express.js API.
Requirements:
1. POST /api/auth/login endpoint
2. Accept email and password in request body
3. Validate against users in PostgreSQL database
4. Return JWT token on success, 401 on failure
5. Use bcrypt for password comparison (already in dependencies)
Follow the existing patterns in src/api/routes.js for route structure.
Use the existing db.query() helper in src/db/index.js for database access.
Success criteria: I can call the endpoint with valid credentials
and receive a JWT token that works with our existing auth middleware.
Why it works:
- Specific, scoped feature
- Clear technical requirements
- Points to existing patterns to follow
- Defines what “done” looks like
Code Review Examples
Bad Example
Why it’s bad:
- No code provided or referenced
- No indication of what to look for
- No context about the code’s purpose
- No criteria for the review
Good Example
Review this pull request for our payment processing module:
Focus areas:
1. Security - we're handling credit card data
2. Error handling - payments must never silently fail
3. Idempotency - duplicate requests should be safe
Context:
- This integrates with Stripe API
- It's called from our checkout flow
- We have ~10,000 transactions/day
Please flag any issues as Critical/Major/Minor with explanations.
Why it works:
- Clear scope and focus areas
- Important context provided
- Business implications explained
- Requested output format specified
Refactoring Examples
Bad Example
Why it’s bad:
- “Better” is subjective and undefined
- No specific problems identified
- No goals for the refactoring
- No constraints or requirements
Good Example
Refactor the UserService class in src/services/user.js:
Problems to address:
1. The class is 500+ lines - split into smaller, focused services
2. Database queries are mixed with business logic - separate them
3. There's code duplication in the validation methods
Constraints:
- Keep the public API unchanged (other code depends on it)
- Maintain test coverage (run npm test after changes)
- Follow our existing service patterns in src/services/
Goal: Improve maintainability while keeping the same functionality.
Why it works:
- Specific problems identified
- Clear constraints and requirements
- Points to patterns to follow
- Measurable success criteria
Key Principles for Effective Instructions
Be Specific
Vague instructions produce vague results. Be concrete about:
| Instead of… | Say… |
|---|
| ”Fix the error" | "Fix the TypeError on line 45 of api.py" |
| "Add tests" | "Add unit tests for the calculateTotal function covering edge cases" |
| "Improve performance" | "Reduce the database queries from N+1 to a single join query" |
| "Clean up the code" | "Extract the validation logic into a separate ValidatorService class” |
Provide Context
Help OpenHands understand the bigger picture:
Context to include:
- What does this code do? (purpose)
- Who uses it? (users/systems)
- Why does this matter? (business impact)
- What constraints exist? (performance, compatibility)
- What patterns should be followed? (existing conventions)
Example with context:
Add rate limiting to our public API endpoints.
Context:
- This is a REST API serving mobile apps and third-party integrations
- We've been seeing abuse from web scrapers hitting us 1000+ times/minute
- Our infrastructure can handle 100 req/sec per client sustainably
- We use Redis (already available in the project)
- Our API follows the controller pattern in src/controllers/
Requirement: Limit each API key to 100 requests per minute with
appropriate 429 responses and Retry-After headers.
Set Clear Goals
Define what success looks like:
Success criteria checklist:
✓ What specific outcome do you want?
✓ How will you verify it worked?
✓ What tests should pass?
✓ What should the user experience be?
Example with clear goals:
Implement password reset functionality.
Success criteria:
1. User can request reset via POST /api/auth/forgot-password
2. System sends email with secure reset link
3. Link expires after 1 hour
4. User can set new password via POST /api/auth/reset-password
5. Old sessions are invalidated after password change
6. All edge cases return appropriate error messages
7. Existing tests still pass, new tests cover the feature
Include Constraints
Specify what you can’t or won’t change:
Constraints to specify:
- API compatibility (can't break existing clients)
- Technology restrictions (must use existing stack)
- Performance requirements (must respond in <100ms)
- Security requirements (must not log PII)
- Time/scope limits (just this one file)
Common Pitfalls to Avoid
Vague Requirements
Make the dashboard faster.
The dashboard takes 5 seconds to load.
Profile it and optimize to load in under 1 second.
Likely issues:
- N+1 queries in getWidgetData()
- Uncompressed images
- Missing database indexes
Focus on the biggest wins first.
Missing Context
❌ No Context
✅ With Context
Add caching to the product catalog API.
Context:
- 95% of requests are for the same 1000 products
- Product data changes only via admin panel (rare)
- We already have Redis running for sessions
- Current response time is 200ms, target is <50ms
Cache strategy: Cache product data in Redis with 5-minute TTL,
invalidate on product update.
Unrealistic Expectations
❌ Unrealistic
✅ Realistic
Rewrite our entire backend from PHP to Go.
Create a Go microservice for the image processing currently in
src/php/ImageProcessor.php.
This is the first step in our gradual migration.
The Go service should:
1. Expose the same API endpoints
2. Be deployable alongside the existing PHP app
3. Include a feature flag to route traffic
Start with just the resize and crop functions.
The login is broken, fix it.
Users can't log in since yesterday's deployment.
Symptoms:
- Login form submits but returns 500 error
- Server logs show: "Redis connection refused"
- Redis was moved to a new host yesterday
The issue is likely in src/config/redis.js which may
have the old host hardcoded.
Expected: Login should work with the new Redis at redis.internal:6380
Best Practices
Structure Your Instructions
Use clear structure for complex requests:
## Task
[One sentence describing what you want]
## Background
[Context and why this matters]
## Requirements
1. [Specific requirement]
2. [Specific requirement]
3. [Specific requirement]
## Constraints
- [What you can't change]
- [What must be preserved]
## Success Criteria
- [How to verify it works]
Provide Examples
Show what you want through examples:
Add input validation to the user registration endpoint.
Example of what validation errors should look like:
{
"error": "validation_failed",
"details": [
{"field": "email", "message": "Invalid email format"},
{"field": "password", "message": "Must be at least 8 characters"}
]
}
Validate:
- email: valid format, not already registered
- password: min 8 chars, at least 1 number
- username: 3-20 chars, alphanumeric only
Define Success Criteria
Be explicit about what “done” means:
This task is complete when:
1. All existing tests pass (npm test)
2. New tests cover the added functionality
3. The feature works as described in the acceptance criteria
4. Code follows our style guide (npm run lint passes)
5. Documentation is updated if needed
Iterate and Refine
Build on previous work:
In our last session, you added the login endpoint.
Now add the logout functionality:
1. POST /api/auth/logout endpoint
2. Invalidate the current session token
3. Clear any server-side session data
4. Follow the same patterns used in login
The login implementation is in src/api/auth/login.js for reference.
Quick Reference
| Element | Bad | Good |
|---|
| Location | ”in the code" | "in src/api/users.py line 45” |
| Problem | ”it’s broken" | "TypeError when user.preferences is None” |
| Scope | ”add authentication" | "add JWT-based login endpoint” |
| Behavior | ”make it work" | "return 200 with user data on success” |
| Patterns | (none) | “follow patterns in src/services/“ |
| Success | (none) | “all tests pass, endpoint returns correct data” |
The investment you make in writing clear instructions pays off in fewer iterations, better results, and less time debugging miscommunication. Take the extra minute to be specific.