Prompt Patterns & Templates

50 min advanced Lesson 9

Learning Outcomes

  • Apply the scaffold pattern to break large tasks into structured phases
  • Use the critic pattern to generate-then-review for higher quality output
  • Iterate toward quality using the refine pattern
  • Convert between formats, languages, and paradigms with the transform pattern
  • Build and organise a personal prompt template library

Lesson Plan

Segment Duration Topic
Intro 3 min Why prompt patterns matter
Demo 10 min The scaffold pattern
Demo 10 min The critic pattern
Demo 8 min The refine pattern
Demo 8 min The transform pattern
Explain 6 min Building your prompt library
Demo 3 min Organising and sharing templates
Wrap-up 2 min Key takeaways

Before You Begin

Pre-work:

  • Complete Lesson 8 on Multi-Turn Conversations
  • Identify 2-3 tasks you do repeatedly when coding
  • Think about what "good output" looks like for those tasks

Shopping List:

  • An AI coding tool (Claude Code, Cursor, Codex, or similar)
  • A project to practice patterns on
  • A place to save your templates (notes app, repo file, or dedicated document)
  • Familiarity with your most common development workflows

1 The Scaffold Pattern

The scaffold pattern separates structure from content. First you ask the AI to create the skeleton, then you fill in the details piece by piece.

Why it works: Large tasks overwhelm AI when attempted all at once. By scaffolding first, you establish structure that guides the detailed work.

The basic scaffold flow:

Step 1: Generate structure only (no implementation)
Step 2: Review and adjust the structure
Step 3: Fill in one section at a time
Step 4: Review the whole

Example: Scaffolding an API module

Step 1 — Generate structure:

> I need a complete user management module. 
> First, just give me the file structure and key interfaces —
> no implementation yet.
>
> The module needs: CRUD operations, role-based access, 
> password management, and activity logging.

AI produces:

src/users/
├── user.types.ts         (User, CreateUserDTO, UpdateUserDTO)
├── user.controller.ts    (CRUD endpoints + role assignment)
├── user.service.ts       (business logic + password hashing)
├── user.repository.ts    (database queries)
├── user.middleware.ts     (auth checks, role guards)
├── user.validator.ts     (Zod schemas for input)
└── user.test.ts          (integration tests)

Key interfaces:
- User { id, email, name, role, createdAt, updatedAt }
- CreateUserDTO { email, name, password, role? }
- UpdateUserDTO { name?, email?, role? }

Step 2 — Adjust:

> Good structure. Two changes:
> - Split tests into user.unit.test.ts and user.integration.test.ts
> - Add user.events.ts for activity logging events
> Now implement user.types.ts

Step 3 — Fill in piece by piece:

> Now implement user.repository.ts using the types we defined
> Now user.service.ts — it should call the repository and handle 
> the password hashing and role validation logic

Example: Scaffolding a complex function

> I need a function that processes a CSV upload: validates format, 
> parses rows, transforms data, handles errors per-row, and returns 
> a summary.
>
> First, write just the function signature, the main steps as comments, 
> and the return type. No implementation yet.

AI produces:

interface ProcessResult {
  successful: number;
  failed: number;
  errors: Array<{ row: number; field: string; message: string }>;
  data: TransformedRecord[];
}

async function processCSVUpload(
  file: Buffer,
  schema: ValidationSchema
): Promise<ProcessResult> {
  // Step 1: Validate file format (encoding, headers, size)
  // Step 2: Parse CSV into raw rows
  // Step 3: Validate each row against schema
  // Step 4: Transform valid rows into target format
  // Step 5: Collect errors from invalid rows
  // Step 6: Return summary with results and errors
}

Then you fill each step:

> Implement step 1 and 2 — file validation and CSV parsing

When to use the scaffold pattern:

  • Building a new module or feature from scratch
  • Creating complex functions with many steps
  • Writing documentation with multiple sections
  • Generating test suites (scaffold all test cases, then implement)
  • Any task where getting the structure right is half the battle
TIP
Tip
The scaffold pattern works beautifully with multi-turn conversations. The structure becomes a shared reference you can point to: 'implement step 4 from the scaffold'.

2 The Critic Pattern

The critic pattern has two phases: generate, then critically review. Instead of hoping for perfect output on the first try, you explicitly ask the AI to evaluate its own work and fix issues.

Why it works: AI is often better at spotting problems in existing code than avoiding them during generation. The critic phase catches issues the generation phase missed.

The basic critic flow:

Step 1: Generate the code/solution
Step 2: "Now review what you just wrote — what's wrong with it?"
Step 3: "Fix those issues"

Example: Generating and critiquing an API endpoint

Step 1 — Generate:

> Write a POST /api/orders endpoint that creates an order from 
> a shopping cart. Include inventory check and payment processing.

Step 2 — Critique:

> Now review the code you just wrote. Look for:
> - Race conditions (what if two users buy the last item?)
> - Error handling gaps (what if payment succeeds but DB write fails?)
> - Security issues (input validation, authorisation)
> - Performance concerns (N+1 queries, unnecessary DB calls)
>
> List every issue you find.

AI responds with issues:

Issues found:
1. Race condition: No lock on inventory check → two orders could 
   claim the same item
2. No transaction: if payment succeeds but order insert fails, 
   money is charged without an order record
3. Missing auth check: any user could create an order for any cart
4. N+1: loading each cart item's product info individually
5. No idempotency key: retrying a failed request could double-charge

Step 3 — Fix:

> Fix all 5 issues. Use a database transaction, optimistic locking 
> for inventory, and add an idempotency check.

The "devil's advocate" variation:

> You proposed using Redis for our session store. Now argue against 
> that choice. What could go wrong? What are we not considering? 
> What would a sceptic say?

This forces the AI to stress-test its own recommendations.

The "senior review" variation:

> Pretend you're a senior engineer reviewing this code in a PR. 
> What comments would you leave? Be critical — I want to improve 
> this before it ships.

The "security audit" variation:

> Assume a malicious user is looking at this endpoint. 
> What would they try? What inputs would they send? 
> Where are the weak points?

Chaining critic passes:

For important code, run multiple critic passes with different lenses:

Pass 1: "Review for correctness — any logic bugs?"
Pass 2: "Review for security — any vulnerabilities?"
Pass 3: "Review for performance — any bottlenecks?"
Pass 4: "Review for maintainability — what's hard to understand or change?"

Each pass catches different categories of issues.

WARNING
Watch Out
Don't use the critic pattern for trivial code. If you're generating a simple utility function, the overhead of generate-then-review isn't worth it. Reserve this for complex logic, security-sensitive code, or anything that will be hard to fix later.

3 The Refine Pattern

The refine pattern starts with a rough first attempt and iteratively improves it through targeted feedback. Instead of trying to get perfection in one shot, you shape the output through successive passes.

Why it works: It's easier to improve something that exists than to specify everything perfectly upfront. Each refinement pass addresses one concern, keeping changes focused and controllable.

The basic refine flow:

Step 1: Generate a first draft (accepting imperfection)
Step 2: Identify the most important improvement
Step 3: Request that specific improvement
Step 4: Repeat until satisfied

Example: Refining a function

Draft:

> Write a function that searches users by name, email, or role.
> Just get something working, don't worry about perfection.

Refine pass 1 — Performance:

> Good start. Now optimise: the search should be case-insensitive 
> and support partial matching without loading all users into memory.

Refine pass 2 — Edge cases:

> Now handle edge cases: empty search string, special characters 
> in the query, and what happens when the database is unavailable.

Refine pass 3 — Interface:

> Clean up the interface: add pagination support, allow combining 
> filters (name AND role), and return the total count alongside results.

Each pass makes the function better while keeping changes scoped.

Example: Refining an error message

Draft: "Write user-facing error messages for our form validation"
Refine 1: "Make them friendlier — less technical, more conversational"
Refine 2: "Add specific fix suggestions for each error"
Refine 3: "Keep them under 100 characters each for mobile display"

The "dimension" refinement technique:

Name specific dimensions to improve along:

> Here's my current code. Refine it for readability — 
> I want a junior developer to understand it without comments.
> Now refine for testability — I should be able to unit test 
> each part in isolation without mocking the universe.
> Finally, refine for error handling — every failure mode 
> should produce an actionable error message.

Example: Refining documentation

Draft: "Write API docs for the /tasks endpoints"
Refine 1: "Add request/response examples for each endpoint"
Refine 2: "Include error responses — what does a 400 look like vs 404 vs 500?"
Refine 3: "Add a 'common mistakes' section based on the validations we have"

When to stop refining:

  • When the output meets your quality bar
  • When refinements start contradicting earlier ones
  • When you're polishing for marginal gains (diminishing returns)
  • When you've been refining for more passes than it would take to just edit manually

The "one thing at a time" rule:

Each refinement pass should target ONE dimension:

  • Good: "Make it more performant"
  • Bad: "Make it more performant, add error handling, rename variables, and add types"

Multiple concerns in one refinement pass leads to confusion and regression.

TIP
Tip
The refine pattern pairs well with your own editing. Generate with AI, refine 2-3 times, then do a final manual edit for the finishing touches that only you can specify.

4 The Transform Pattern

The transform pattern converts existing code or content from one form to another. It leverages the AI's ability to understand structure and re-express it differently.

Why it works: Transformation tasks have clear input and output, making them highly predictable. The AI can maintain semantics while changing syntax, structure, or paradigm.

Common transformations:

From To Example
JavaScript TypeScript Add types to existing JS code
REST API GraphQL Convert endpoints to schema + resolvers
Class-based Functional React classes to hooks
Callbacks Async/Await Modernise legacy patterns
SQL ORM queries Raw SQL to Knex/Prisma
One framework Another Express routes to Fastify
Config format Another YAML to JSON, JSON to TOML
Imperative Declarative Loops to map/filter/reduce
Test framework Another Mocha to Jest

Example: JavaScript to TypeScript

> Transform this JavaScript module to TypeScript. 
> Add proper types (no 'any'). Infer types where obvious, 
> create interfaces where the shape is complex.
> Keep the logic identical — only add types.
>
> [paste JS code]

Example: Converting paradigms

> Convert this class-based React component to a functional component 
> with hooks. Map lifecycle methods as follows:
> - componentDidMount → useEffect with empty deps
> - componentDidUpdate → useEffect with relevant deps
> - componentWillUnmount → useEffect cleanup
> - this.state → useState
> - this.props → function parameters
>
> Preserve all behaviour. Don't "improve" the logic — just transform it.

Example: Converting between formats

> Convert this Express route configuration to an OpenAPI 3.0 spec:
> - Extract path parameters, query parameters, and body schemas
> - Infer response types from the handler return values
> - Add appropriate status codes based on the error handling
>
> [paste Express routes]

The "preserve semantics" instruction:

Always be explicit about whether you want pure transformation or transformation plus improvement:

Pure transformation:

> Convert this to TypeScript. Don't change any logic, algorithms, 
> or structure. Only add types.

Transformation plus improvement:

> Convert this to TypeScript and also:
> - Replace 'any' with proper types
> - Extract magic strings to constants
> - Add null checks where the types reveal potential nulls

Example: Transforming between test styles

> Transform these Mocha/Chai tests to Jest syntax:
> - describe/it blocks stay the same
> - expect(x).to.equal(y) → expect(x).toBe(y)
> - expect(x).to.deep.equal(y) → expect(x).toEqual(y)
> - before/after → beforeAll/afterAll
> - beforeEach/afterEach stay the same
> - sinon stubs → jest.fn()
> 
> Keep test descriptions and assertions identical.

Batch transformations:

> Apply this transformation to all files in src/controllers/:
> - Add return type annotations to all controller methods
> - Wrap response sends in a standard formatResponse() helper
> - Add request validation using the matching schema from src/schemas/
NOTE
Key Insight
The transform pattern is most reliable when you specify exactly what should change and what must stay the same. Without this boundary, the AI may 'helpfully' refactor things you didn't ask to change.

5 Building Your Prompt Library

The best prompts deserve to be reused. Here's how to build and organise a personal library of prompt templates.

What belongs in your library:

  • Prompts you've used more than 3 times
  • Prompts that took multiple iterations to get right
  • Prompts that consistently produce good output
  • Prompts for your most common tasks

Template format:

Create a consistent format for your saved prompts:

## Template: [Name]

**When to use:** [Situation description]
**Pattern:** [scaffold/critic/refine/transform/other]
**Works best with:** [types of tasks]

**Template:**
> [The actual prompt with [PLACEHOLDERS] for variable parts]

**Variables:**
- [PLACEHOLDER]: what to fill in here

**Example:**
> [A concrete example of the template in use]

**Tips:**
- [Things that make this template work better]

Example library entries:

## Template: New Feature Scaffold

**When to use:** Starting a new feature from scratch
**Pattern:** Scaffold

**Template:**
> I need to build [FEATURE_DESCRIPTION].
> Tech stack: [STACK]
> 
> First, give me:
> 1. File structure (what files to create)
> 2. Key interfaces/types
> 3. Implementation order (dependencies)
> 
> Don't implement anything yet — just the skeleton.

---

## Template: Code Critic

**When to use:** After generating complex or security-sensitive code
**Pattern:** Critic

**Template:**
> Review the [CODE_TYPE] you just wrote. Check for:
> - [CONCERN_1]
> - [CONCERN_2]  
> - [CONCERN_3]
> - Any issues I haven't thought of
>
> List every problem, then fix them all.

---

## Template: Legacy Modernisation

**When to use:** Upgrading old code patterns
**Pattern:** Transform

**Template:**
> Transform this [OLD_PATTERN] code to [NEW_PATTERN].
> Rules:
> - Preserve all behaviour (same inputs → same outputs)
> - Don't change public interfaces
> - Flag anything that can't be directly transformed
> 
> [PASTE CODE]

Where to store your library:

Option Best For
Markdown file in your dotfiles repo Personal use, version-controlled
Notion/notes app Easy search, tagging, mobile access
Project repo (docs/prompts/) Team-shared, project-specific
Snippets tool (Alfred, Raycast) Quick access during coding
Rules file Patterns you want AI to apply automatically
TIP
Start Simple
If you're unsure which to pick: start with a single markdown file (e.g., ~/prompts.md). One file, no folders, no tooling. Upgrade to something fancier only when you outgrow it — most people never need more than a well-organised markdown file.

Growing your library:

  1. Start with 5 templates — your most repeated tasks
  2. Add one per week — when you craft a prompt that works unusually well
  3. Prune quarterly — remove templates you haven't used in 3 months
  4. Refine continuously — update templates as you discover improvements

Combining patterns:

Your most powerful templates often combine multiple patterns:

Scaffold + Critic:
  "Create the structure, then review it for gaps before implementing"

Generate + Refine + Transform:
  "Write it in pseudocode, refine the logic, then transform to TypeScript"

Critic + Refine:
  "Review this code, identify issues, then fix each one iteratively"
TIP
Tip
Don't over-engineer your library at the start. Even a simple text file with 5-10 prompts you copy-paste from is valuable. The organisation can evolve as the library grows.

Questions & Answers

Q: Can I put prompt templates in my system prompt / rules file?
Yes, but selectively. Put patterns you want applied automatically ("always use the critic pattern for security-sensitive code") in your rules file. Keep patterns you choose situationally in your personal library.
Q: How do I know which pattern to use for a given task?
Start with: Is this creating something new? (scaffold) Is quality critical? (critic) Am I iterating toward something? (refine) Am I converting between formats? (transform) If none fit perfectly, combine them or just write a direct prompt.
Q: Do these patterns work for non-code tasks?
Absolutely. Scaffold works for writing documentation, critic works for reviewing proposals, refine works for polishing prose, and transform works for converting specs between formats. The patterns are universal thinking tools.
Q: Should I share my prompt library with my team?
Yes — with curation. Not every personal prompt is team-relevant. Create a shared library of templates for common team workflows (PR reviews, incident response, onboarding tasks) and let individuals maintain personal templates for their unique patterns.

Key Takeaways

  1. Scaffold: structure first, then fill — prevents overwhelm and ensures coherent architecture
  2. Critic: generate then review — catches issues that generation misses
  3. Refine: iterate along one dimension at a time — shapes output toward quality
  4. Transform: convert between forms while preserving semantics — reliable and predictable
  5. Build a library: save prompts that work well and reuse them systematically
  6. Combine patterns: the most effective workflows chain multiple patterns together

Next Steps: In Lesson 10 — Meta-Prompting & Self-Improvement, you'll learn how to use AI to improve your prompts themselves — and build an ever-improving feedback loop.