Anatomy of a Good Prompt

40 min beginner Lesson 1

Learning Outcomes

  • Identify the 5 structural elements of an effective prompt
  • Distinguish between good and bad prompts using concrete criteria
  • Apply the specificity spectrum to calibrate prompt detail
  • Recognise common structural patterns used in production prompts
  • Rewrite vague prompts into structured, effective ones

Lesson Plan

Segment Duration Topic
Intro 3 min Why structure matters for AI coding
Explain 8 min The 5 elements: role, context, task, constraints, format
Demo 7 min Good vs bad prompt comparison
Explain 6 min The specificity spectrum
Demo 8 min Common structural patterns
Practice 6 min Rewrite bad prompts as good ones
Wrap-up 2 min Key takeaways, preview next lesson

Before You Begin

Pre-work:

  • Have your AI coding tool open and ready (Claude Code, Cursor, or Codex)
  • Think of a recent coding task where AI gave you an unhelpful answer
  • Review the installation lesson if you haven't set up your prompts folder

Shopping List:

  • Any AI coding tool (Claude Code, Cursor, or Codex CLI)
  • A code project to test prompts against (any language)
  • Your prompts folder from the installation lesson

1 The 5 Elements of a Prompt

Every effective prompt for code generation contains up to five structural elements. Not every prompt needs all five, but knowing them lets you add precision where it matters.

1. Role — Who the AI should be

You are a senior backend engineer specialising in REST API design.

2. Context — What the AI needs to know about the situation

I'm building a Node.js Express application that manages a library system.
We use PostgreSQL for storage and follow RESTful conventions.

3. Task — What you want the AI to do (the core instruction)

Create an endpoint that allows users to search books by title, author, or ISBN.

4. Constraints — Rules the output must follow

Use parameterised queries to prevent SQL injection.
Return paginated results (default 20 per page).
Handle the case where no results are found with a 200 and empty array, not a 404.

5. Format — How the output should be structured

Return the code as a single file with JSDoc comments on the handler function.
Include a curl example showing how to call the endpoint.

Here's what all five look like together:

You are a senior backend engineer specialising in REST API design.

I'm building a Node.js Express application that manages a library system.
We use PostgreSQL for storage and follow RESTful conventions.

Create an endpoint that allows users to search books by title, author, or ISBN.

Constraints:
- Use parameterised queries to prevent SQL injection
- Return paginated results (default 20 per page)
- Handle no results with a 200 and empty array, not a 404

Return the code as a single file with JSDoc comments on the handler function.
Include a curl example showing how to call the endpoint.
NOTE
Key Insight
You don't always need all five elements. A simple task like 'Add TypeScript types to this function' might only need the task and context (the function itself). The more complex or ambiguous the task, the more elements you should include.

2 Good vs Bad Prompts

Let's compare prompts that produce poor results with structured versions that get it right.

Example 1: Creating a function

Bad:

Write a validation function

Good:

Write a TypeScript function called validateEmail that:
- Takes a string input
- Returns { valid: boolean; error?: string }
- Checks for @ symbol, valid domain, and no spaces
- Does NOT use regex — use string parsing methods instead

Why the good version works: it names the function, specifies the language, defines the return type, lists the validation rules, and adds a constraint about the approach.

Example 2: Refactoring code

Bad:

Make this code better

Good:

Refactor this function to improve readability:
- Extract the nested if-else into early returns
- Name the magic numbers (30, 7, 365) as constants
- Split the date formatting into a separate helper function
- Keep the same input/output contract

[paste the function here]

Example 3: Debugging

Bad:

This doesn't work, fix it

Good:

This Express middleware returns a 500 error when the user object is null.
Expected behaviour: return a 401 with { error: "Unauthorized" }.
The function is called after the auth token is validated but before
the user is fetched from the database.

[paste the middleware code]

Fix the null handling and add appropriate error responses.
WARNING
Watch Out
The single most common mistake in AI prompting is being too vague about what 'correct' looks like. If you don't define success, AI will guess — and it might guess wrong in ways that are hard to spot.

The pattern: Good prompts are specific about the WHAT (task), the HOW (constraints), and the SHAPE (format). Bad prompts leave all three to guesswork.


3 The Specificity Spectrum

Not every prompt needs maximum detail. Think of specificity as a dial you adjust:

Low specificity ←————————————————→ High specificity
"Add tests"          "Add a unit test for calculateTax using Jest,
                      testing 0% bracket, middle bracket, and
                      top bracket, with edge case for negative input.
                      Mock the taxRateService dependency.
                      Follow AAA pattern (Arrange-Act-Assert)."

When to use LOW specificity:

  • Exploratory work ("Show me three approaches to caching here")
  • Simple, unambiguous tasks ("Add TypeScript types to this file")
  • When you want creative solutions
  • Brainstorming ("What patterns could simplify this module?")

When to use HIGH specificity:

  • Production code that must follow conventions
  • Complex logic with specific business rules
  • When you've already tried low specificity and got poor results
  • Security-sensitive or performance-critical code

The iterative approach:

Start at medium specificity. If the result is close but not right, add constraints. If the result is too narrow, remove them.

Attempt 1 (medium): "Write a caching layer for our API responses"
Result: Works but uses in-memory cache

Attempt 2 (higher): "Write a caching layer for our API responses using Redis,
with a TTL of 5 minutes for list endpoints and 1 hour for individual resources.
Include cache invalidation when a resource is updated via PUT or DELETE."
Result: Exactly what was needed
TIP
Tip
Think of specificity like a conversation. You wouldn't walk up to a colleague and provide every detail upfront. Start with the ask, then add detail in response to questions or incorrect assumptions.

4 Common Structural Patterns

Over time, certain prompt structures have emerged as reliably effective for code generation. Here are four patterns you can use as templates:

Pattern 1: The Specification

Best for: new features, API endpoints, components

## What
[One sentence describing what to build]

## Requirements
- [Requirement 1]
- [Requirement 2]
- [Requirement 3]

## Technical Details
- Language/Framework: [X]
- Must integrate with: [existing systems]
- Error handling: [approach]

## Example Usage
[Show how the finished code would be called]

Pattern 2: The Transformation

Best for: refactoring, migration, format conversion

Transform [this code/file/pattern] from [current state] to [desired state].

Rules:
- Keep [X] unchanged
- Change [Y] to [Z]
- Remove [A]
- Add [B]

Here is the code:
[paste code]

Pattern 3: The Fix

Best for: debugging, error resolution

Bug: [describe the observable symptom]
Expected: [what should happen]
Actual: [what currently happens]
Context: [relevant system info]

[paste relevant code]

Fix the issue. Explain what caused it.

Pattern 4: The Explanation + Implementation

Best for: learning new APIs, understanding then building

I need to [goal]. I'm using [tech stack].

First, explain the approach you'd recommend and why.
Then implement it with:
- [constraint 1]
- [constraint 2]
- Comments explaining non-obvious decisions

Each pattern addresses a different type of work, but all share the same DNA: clear task, relevant context, and explicit constraints.


5 Practice: Rewriting Bad Prompts

Let's practice transforming vague prompts into structured ones. For each bad prompt below, try rewriting it before reading the suggested improvement.

Exercise 1:

Bad: Make a login form

Improved:

Create a React login form component with:
- Email and password fields with labels
- Client-side validation (email format, password min 8 chars)
- A submit button that's disabled until both fields are valid
- Error message display below the form
- Accessible: proper aria labels, focus management on error
- Use controlled components (useState for form state)
- Call onSubmit(email, password) prop when the form is submitted
- Do not handle the API call — just pass values to the parent

Exercise 2:

Bad: Write some tests

Improved:

Write Jest unit tests for the attached UserService class. Cover:
1. createUser — happy path (valid input returns user object)
2. createUser — duplicate email throws ConflictError
3. getUser — returns null for non-existent ID
4. updateUser — partial update merges with existing fields
5. deleteUser — soft deletes (sets deletedAt, doesn't remove row)

Mock the database layer (UserRepository). Use describe/it blocks.
Each test should follow Arrange-Act-Assert structure.

Exercise 3:

Bad: This is slow, fix it

Improved:

This database query takes 3+ seconds for tables with over 10k rows.
It's called on every page load of the dashboard.

[paste query]

Optimise it for read performance. Consider:
- Adding appropriate indexes
- Restructuring JOINs if possible
- Whether pagination or a materialised view would help
- Keeping the same result format

Show the optimised query and explain what changed.
TIP
Tip
Save your best rewrites in your prompts/library folder. Over time you'll build a collection of proven patterns for tasks you do repeatedly — like writing tests, creating components, or debugging performance issues.

Your turn: Take that AI interaction you thought of in the pre-work — the one where AI gave an unhelpful answer. Rewrite your original prompt using the 5-element structure. Run it again. Notice the difference.


Questions & Answers

Q: Do I really need all 5 elements every time?
No. Simple tasks need fewer elements. "Add a TypeScript return type to this function" is perfectly fine — the task is clear and unambiguous. Use more elements when the task is complex, ambiguous, or when you've gotten poor results with a simpler prompt.
Q: How long is too long for a prompt?
There's no hard limit, but if your prompt is over 500 words, consider whether you're over-constraining. Long prompts work well when you're providing context (like pasting code). They work poorly when you're listing 30 constraints — that usually means the task should be split into smaller pieces.
Q: Does the order of elements matter?
Generally: role first (sets the frame), then context, then task, then constraints, then format. But the most important thing is that the task is clear. If you put constraints before the task, AI might get confused about what it's actually supposed to do.
Q: Should I include the programming language in every prompt?
If you're working in a file or project context, the AI usually knows the language. If you're starting fresh or asking a general question, specify the language. When in doubt, include it — it takes two words and eliminates ambiguity.

Key Takeaways

  1. Every prompt has up to 5 elements — role, context, task, constraints, and format. Use as many as the complexity demands.
  2. Good prompts define what "correct" looks like — don't leave success criteria to guesswork.
  3. Specificity is a dial, not a switch — start at medium, adjust based on results.
  4. Use patterns as templates — the Specification, Transformation, Fix, and Explanation patterns cover most coding tasks.
  5. Practice rewrites — taking a bad prompt and making it good is the fastest way to build the skill.

Next Steps: In Lesson 2 — Role & Context Setting, we'll dive deep into the first two elements and learn how setting the right role dramatically changes the quality of generated code.