Skip to content

How to Write Effective Requirements for AI Coding Tools: A Prompt Structure Guide

I spent three hours debugging code that an AI assistant generated from my “simple” request. The feature worked on the surface, but it violated every convention in our codebase, used a database pattern we’d deprecated months ago, and failed silently on edge cases I hadn’t thought to mention.

The problem wasn’t the AI. The problem was my prompt: “Add user authentication to the API.”

When I handed the same requirements to a human developer, I wrote a two-page specification document. But with AI, I got lazy. And that’s the key insight from developers who consistently get good results from AI coding tools: treat prompts as formal requirements documents, not casual conversation.

The Prompt Quality Spectrum

After analyzing discussions from developers who’ve had both success and failure with AI coding tools, a clear pattern emerges. The quality of AI output directly correlates with prompt quality:

Prompt TypeExampleResult
Vague”Add authentication”AI guesses, often wrong
Partial”Add JWT auth to login”AI fills gaps with assumptions
Specific”Add JWT auth to POST /login endpoint, return 401 for invalid credentials, follow existing error handler pattern”AI produces accurate code
CompleteFull spec with context, constraints, patterns, acceptance criteriaAI produces production-ready code

A product manager on Reddit put it perfectly: “I wouldn’t hand requirements to AI that don’t meet the same standards as what I’d hand off to a software engineer. If there are gaps, things may not get built correctly.”

Why Vague Prompts Fail

When you give an AI a vague prompt, it doesn’t ask clarifying questions. It makes assumptions:

Your prompt: "Add authentication"
AI assumption 1: "Authentication" = JWT (could be OAuth, session-based...)
AI assumption 2: Store tokens in localStorage (could be cookies, session storage...)
AI assumption 3: 24-hour expiry (could be 1 hour, 7 days...)
Result: Code that compiles but doesn't match your actual requirements

Each assumption compounds. By the time you’re debugging the third wrong decision, you’ve lost the time you thought you were saving.

The CRCA Framework

The developers who get consistent results from AI coding tools follow a structured approach. I call it CRCA: Context, Requirements, Constraints, Acceptance Criteria.

This mirrors how you’d brief a human developer. Each element addresses a specific failure mode.

C - Context: The “Where We Are” Layer

Context tells the AI what it needs to know about your project before it writes a single line of code:

## Context
This is an Express.js REST API for an e-commerce platform.
Tech stack: Express, TypeScript, PostgreSQL (via pg), Zod for validation.
Relevant files:
- src/controllers/orderController.ts (to be modified)
- src/services/orderService.ts (reference for business logic pattern)
- src/middleware/errorHandler.ts (reference for error handling)
- src/schemas/orderSchema.ts (reference for validation pattern)
Do NOT modify:
- src/models/Order.ts (database model is frozen during migration period)

Without context, the AI has no choice but to make assumptions about patterns, conventions, and architecture.

R - Requirements: The “What To Build” Layer

Requirements should be specific enough that there’s no room for interpretation:

## Requirements
Add a bulk order cancellation endpoint:
1. Endpoint: POST /api/orders/bulk-cancel
2. Request body: `{ orderIds: string[] }` (max 100 IDs)
3. Behavior:
- Cancel all orders with status 'pending' or 'processing'
- Skip orders with other statuses (don't fail the batch)
- Return summary: `{ cancelled: number, skipped: number, errors: string[] }`
Technical requirements:
- Use existing orderService.cancelOrder() for each order
- Wrap in database transaction
- Log each cancellation attempt

Notice the specificity: exact endpoint, request format, max IDs, return format, which statuses to cancel, what to skip, and what service method to use.

C - Constraints: The “Guardrails” Layer

Constraints are what the AI must NOT do, which is often more important than what it should do:

## Constraints
MUST:
- Validate orderIds array is not empty and contains valid UUIDs
- Check user has permission to cancel each order
- Use existing authMiddleware on the route
- Return 403 if user lacks permission for any order
MUST NOT:
- Delete order records (mark as cancelled instead)
- Send notification emails (handled by separate service)
- Modify the Order model schema
- Use raw SQL queries (use parameterized queries via pg)

Constraints prevent the AI from choosing approaches that “work” but violate your architecture or create technical debt.

A - Acceptance Criteria: The “Definition of Done” Layer

Acceptance criteria give you a checklist to verify the AI’s output:

## Acceptance Criteria
Success cases:
- [ ] Returns 200 with correct summary when all orders cancelled
- [ ] Returns 200 with summary when partial cancellation succeeds
- [ ] Response time under 500ms for 100 orders
Error cases:
- [ ] Returns 400 if orderIds is empty or missing
- [ ] Returns 400 if any orderId is not a valid UUID
- [ ] Returns 401 if authMiddleware fails
- [ ] Returns 403 if user lacks permission for any order
- [ ] Returns 500 if database transaction fails
Edge cases:
- [ ] Handles duplicate orderIds in request (deduplicate silently)
- [ ] Handles empty orderIds array (return 400)
- [ ] Handles orderIds exceeding 100 limit (return 400)

When the AI finishes, you have a concrete checklist: does it pass all these criteria?

The Comparison: Before and After

Here’s what my authentication prompt looked like before:

Add user authentication to the API

Result: 3 hours of debugging, wrong patterns, deprecated approaches.

Here’s what it should have been:

## Context
Express.js API with TypeScript, PostgreSQL database.
Current auth system uses session-based auth with express-session.
User model in src/models/User.ts has fields: id, email, password_hash, created_at.
Files to reference:
- src/middleware/authMiddleware.ts (existing session auth pattern)
- src/controllers/authController.ts (login/register endpoints)
Do NOT modify:
- src/models/User.ts (schema is locked during ORM migration)
## Requirements
Add session-based authentication:
1. POST /auth/register
- Accepts email, password, name
- Validates email format, password strength (min 8 chars)
- Hashes password with bcrypt (cost factor 12)
- Creates user record
- Returns 201 with user object (no password_hash)
2. POST /auth/login
- Accepts email, password
- Validates credentials
- Creates session (24-hour expiry)
- Returns 200 with user object
3. POST /auth/logout
- Destroys session
- Returns 200
## Constraints
MUST:
- Use existing session configuration from express-session
- Follow error handling pattern in authMiddleware.ts
- Validate all inputs with Zod schemas
MUST NOT:
- Store passwords in plain text
- Return password_hash in any response
- Use JWT tokens (we use session-based auth)
## Acceptance Criteria
- [ ] Register creates user with hashed password
- [ ] Login returns correct user for valid credentials
- [ ] Login returns 401 for invalid credentials
- [ ] Logout destroys session
- [ ] Password is bcrypt hashed with cost factor 12
- [ ] password_hash is never in responses
- [ ] Tests for all success and error cases

With this prompt, the AI generated correct code on the first attempt. The time I spent writing the detailed spec (10 minutes) was less than the time I would have spent debugging vague-prompt output (hours).

Common Mistakes

Skipping Context

# BAD
"Add a search endpoint for users"
# GOOD
"This is a REST API with Express.js and PostgreSQL.
Reference src/controllers/productController.ts for the search pattern.
Do not modify the User model."

Vague Requirements

# BAD
"Make it secure"
# GOOD
"Add rate limiting: 100 requests/minute per IP.
Return 429 with Retry-After header when exceeded.
Log blocked requests using existing logger."

Missing Constraints

# BAD
"Implement the feature however works best"
# GOOD
"Use the existing OrderService, not a new service.
Do not add new database tables.
Do not modify the public API contract."

No Acceptance Criteria

# BAD
"When it works, we're done"
# GOOD
"Done when:
- Tests pass for success and error cases
- Manual test shows correct behavior
- No TypeScript errors
- Code follows existing patterns"

One Giant Prompt

Breaking large features into focused prompts dramatically improves accuracy:

# BAD
"Build the entire user management system with CRUD, search, permissions, and tests"
# GOOD
Prompt 1: "Read the attached spec for user management and create an implementation plan"
Prompt 2: "Implement the User model following the spec"
Prompt 3: "Implement the user service following the spec and existing patterns"
Prompt 4: "Add controller endpoints for the user service"
Prompt 5: "Write tests for the user management feature"

The Workflow That Works

One developer described their successful workflow: “I usually write extensive specifications about what I want to have before starting Claude Code and then let Claude first read the full specs and then make plans and milestones.”

This is the key difference: successful developers do the thinking before the prompting. They clarify requirements, define constraints, and write acceptance criteria upfront. Then the AI executes on a complete specification.

Prompt Template

Here’s a template you can use for any coding task:

# Feature: [FEATURE NAME]
## Context
**Project:** [Brief description of what this project does]
**Stack:** [Framework, database, key libraries]
**Relevant Files:**
- [File to modify] - [what it does]
- [File to reference] - [pattern to follow]
- [File to NOT modify] - [why it's frozen]
## Requirements
[Detailed description of what to build]
**Endpoints/Functions to Add:**
| Name | Method | Path | Purpose |
|------|--------|------|---------|
| [name] | [method] | [path] | [purpose] |
## Constraints
**Must:**
- [Constraint 1]
- [Constraint 2]
**Must Not:**
- [Forbidden approach 1]
- [Forbidden approach 2]
## Acceptance Criteria
- [ ] [Success case 1]
- [ ] [Success case 2]
- [ ] [Error case 1]
- [ ] [Tests written and passing]

Key Takeaways

The insight from successful AI coding users is consistent: if you give it a vague prompt, let it make too many product and technical decisions on its own, and then trust the output without checking it properly, of course it will go sideways.

The CRCA framework eliminates the gaps that lead to wrong assumptions:

  • Context tells the AI where it is and what patterns exist
  • Requirements tell the AI exactly what to build
  • Constraints tell the AI what NOT to do
  • Acceptance Criteria give you a checklist to verify correctness

Treat every prompt like a requirements document you’d hand to a human engineer—with the same completeness, specificity, and clarity—and your AI coding success rate will dramatically improve.

The 10 minutes you spend writing a complete specification is less than the hours you’ll spend debugging code generated from a vague prompt.


Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments