AIBites: Prompt Patterns

conversation patterns for effective AI-assisted development

1. Summary

Prompt patterns are recurring conversation structures that improve the quality and predictability of AI responses for code-development tasks. Each pattern names a technique, identifies when to use it, and distinguishes it from related patterns. The examples throughout use TextFinder and related projects as a common domain ‐ a regex search tool implemented across Rust, C++, C#, and Python. Six categories cover the main points of leverage in a development conversation:
Category Patterns
Structuring Decomposition, Planning, Spec-Driven, Scaffolding
Generating Options Brainstorming, Tree of Thoughts, Analogical Reasoning
Reasoning Quality Chain-of-Thought, Self-Critique, Chain-of-Verification, Devil’s Advocate
Iterative Loops Reflexion, ReAct, Test-Driven Prompting
Shaping the Output Few-Shot, Role/Persona Framing, Template Filling, Constraint-Based Prompting
Review/Retrospective Pre-Mortem, Retrospective/Postmortem

2. Structuring Patterns

Structuring patterns control how a task is broken apart and handed to the model. Each one constrains the problem differently before generation begins ‐ decomposing into subtasks, requiring an explicit plan, fixing the requirements as a spec, or providing the interface skeleton up front.

2.1 Decomposition

Split a task into ordered subtasks and present one at a time, rather than asking for the whole thing in a single prompt. Ask the model to stop at each checkpoint so you can review before the next step begins.
"I want to build a recursive text-finder in Rust.  Let's break it into steps and tackle them
one at a time:
  1. Define the CLI argument structure (pattern, directory, file extensions, case sensitivity)
  2. Implement directory traversal that yields candidate file paths
  3. Implement line-by-line pattern matching within a single file, returning line number + matched line
  4. Wire traversal and matching together, with one file's worth of results at a time
  5. Format and print results (grouped by file, with line numbers)
Start with step 1 only.  Show me the CLI struct, then stop so I can review before we move to traversal."
Each step has a checkable output ‐ compile and review step 1 before step 2 exists. Mistakes are caught early and locally instead of buried in a 200-line dump. The subtask list also serves as an explicit contract: the model cannot silently collapse two concerns into one or skip an item.

2.2 Planning

Have the model propose a full approach and pause for your approval before any implementation begins. The emphasis is on the approval gate, not just the breakdown.
"I want to add a --regex flag to TextFinder1 so it supports regex patterns instead of just plain
substrings.  Before writing any code, give me a plan covering:
  - Which crate you'd use and why
  - How the flag interacts with existing CLI args
  - What changes to RustCmdLine vs the matching logic in the core crate
  - Error handling if the user passes an invalid regex
Don't write any code yet -- just the plan.  I'll review and tell you to proceed."
Planning is distinct from Decomposition: a plan can be perfectly decomposed into steps and still be wrong ‐ wrong crate choice, wrong architectural boundary, missed edge case. Planning catches that by making the model commit to its reasoning in plain language before a single line of implementation exists.

2.3 Spec-Driven

Lock in what is being built and what counts as done before any planning or implementation begins. The spec is the contract; the plan is just one way to satisfy it.
"Before any planning or code, let's write a spec for multi-extension search in TextFinder1.
I want it to cover:
  - Goal: what problem this solves
  - Behavior: exact syntax for specifying extensions, and how it interacts with the existing flag
  - Edge cases: no extension given, malformed extension string, extension with/without leading dot
  - Out of scope: anything we're explicitly not doing
  - Acceptance criteria: how we'll know it's done correctly
Leave anything unclear as an open question instead of guessing.  Don't draft a plan yet."
A plan describes how; a spec describes what and why, independent of implementation approach. The same spec could be satisfied by completely different plans. The model’s open questions surface your implicit assumptions before any downstream work inherits them.

2.4 Scaffolding

Provide the interface skeleton ‐ signatures, struct layout, empty bodies ‐ and ask the model to fill in implementations only. The public contract is fixed ahead of time, not improvised during generation.
"Here's the skeleton for the new matcher module.  Fill in the function bodies only -- don't
change any signatures, don't add new public items, don't reorganize anything:"
pub enum MatchMode {
    Substring(String),
    Regex(Regex),
}

pub struct MatchResult {
    pub line_number: usize,
    pub line_text: String,
}

impl MatchMode {
    pub fn from_args(pattern: &str, use_regex: bool) -> Result<Self, MatchError> {
        todo!()
    }

    pub fn matches(&self, line: &str) -> bool {
        todo!()
    }
}

pub fn find_matches(mode: &MatchMode, file_path: &Path) -> Result<Vec<MatchResult>, MatchError> {
    todo!()
}
Scaffolding constrains the model’s freedom to implementation detail only. A plan decides the approach; a spec decides the requirements; scaffolding goes further and fixes the literal interface shape. It is especially useful when you already know how a piece should fit into the rest of the codebase and don’t want the model re-deciding those boundaries.

3. Generating Options

Generating-options patterns widen the solution space before committing to one direction. They differ in how options are generated: pure enumeration (Brainstorming), multi-branch reasoning (Tree of Thoughts), or structural import from another domain (Analogical Reasoning).

3.1 Brainstorming

Ask for many candidate ideas up front, with judgment deliberately deferred. The goal is breadth, not depth on any one idea yet.
"I want to add a new output mode to TextFinder1 beyond plain stdout -- give me 6-8 different ideas
for how results could be presented (formats, destinations, interactivity, whatever comes to mind).
Don't evaluate them yet, just generate a wide list."
No structure is imposed on the ideas ‐ they don’t need to be mutually exclusive, ranked, or fully baked. The point is surfacing options you wouldn’t have considered before any cost/benefit thinking narrows the field.

3.2 Tree of Thoughts

Generate multiple reasoning paths, explore each a few steps deep, and identify which branches dead-end. Useful when the right answer depends on reasoning that could go several plausible directions.
"TextFinder1 is slow on a 50,000-file repo.  Explore three different optimization branches in
parallel: (1) parallelizing file reads across threads, (2) skipping files via a fast pre-filter
before full scanning, (3) memory-mapping files instead of buffered reads.  For each branch,
reason two steps ahead -- what's the next bottleneck after this fix -- and tell me which branch
hits a dead end soonest."
Each branch is a reasoning chain followed forward to see where it leads. Branches are eliminated not because they are bad ideas but because tracing them reveals a ceiling or contradiction ‐ a distinction Brainstorming never makes.

3.3 Analogical Reasoning

Map the current problem onto a structurally similar one the model or you already understands well, and borrow its solution shape.
"TextFinder1 needs to support multiple simultaneous search patterns with different match rules per
pattern -- similar to how a build system runs multiple independent rules against a file set.
How would a build system's rule-dispatch model translate to a multi-pattern matcher?"
This pattern generates one option by importing the shape of a ready-made solution from a familiar domain, then checking which parts of the analogy hold and which break down. The non-transferable parts identify where the real design work lives.

4. Reasoning Quality

Reasoning-quality patterns check or improve the soundness of the model’s output. Chain-of-Thought makes the reasoning visible; Self-Critique and Chain-of-Verification check that reasoning’s output for quality and facts; Devil’s Advocate stress-tests the conclusion by deliberately arguing against it.

4.1 Chain-of-Thought

Ask the model to reason step-by-step before giving a final answer. Useful whenever the answer depends on several dependent considerations that a one-shot response would elide.
"TextFinder1 currently buffers entire files into memory before scanning for matches.  Should I
switch to streaming line-by-line reads instead?  Think through the tradeoffs step by step
before giving a recommendation."
The reasoning is visible, so if the model weighted something wrong you can correct that specific input rather than rejecting an unexplained answer. The chain often surfaces considerations you hadn’t named yet.

4.2 Self-Critique

Have the model produce an answer, then separately critique that answer against explicit criteria, then revise based on its own critique ‐ two passes with different postures.
"Draft the constitution.md naming conventions section for TextFinder2.  Then, separately,
critique your own draft: are any conventions ambiguous, contradictory, or missing a case a
developer would actually hit?  Revise based on what you find."
Chain-of-Thought reasons toward an answer; Self-Critique treats the finished answer as a separate object to be judged. The second pass adopts a skeptical reviewer posture instead of a drafter posture ‐ catching what the first pass produced without questioning.

4.3 Chain-of-Verification

After producing an answer, have the model generate specific verification questions about its own claims and answer each one honestly. This catches errors that survive a single pass because nothing forced a second look at any individual claim.
"You just told me Rust's regex crate compiles patterns lazily by default.  Before I rely on that,
generate 2-3 verification questions about that claim and answer each one honestly -- don't just
restate the original claim."
Self-Critique judges the answer holistically; Chain-of-Verification breaks it into discrete, checkable factual claims and interrogates each one. It is better suited to catching specific wrong facts than structural or stylistic problems.

4.4 Devil’s Advocate

Prompt explicitly for the strongest case against the current direction before finalizing. The goal is a real counterargument, not a token one.
"I've decided to use OCaml for TextFinder2 over Haskell and Clojure.  Before I commit, argue
the strongest case against that choice -- not a weak strawman, the actual best argument someone
who disagreed would make."
Chain-of-Verification checks factual claims; Self-Critique reviews the artifact you produced; Devil’s Advocate argues against the decision itself, deliberately adopting an opposing stance. It is useful specifically for catching motivated reasoning or a choice you talked yourself into too quickly.

5. Iterative Loops

Iterative-loop patterns turn a single best-effort generation into a loop anchored to something checkable ‐ a test result, a real tool observation, or a pre-agreed pass/fail criterion. Correctness is verified during the process rather than assumed at the end.

5.1 Reflexion

Generate an attempt, evaluate it against an observable result, feed the failure back in explicitly, and retry ‐ continuing until the evaluation passes, rather than stopping after one shot.
"Write a function in TextFinder1's core crate that strips ANSI color codes from a line before
writing match output to a log file.  Run it against this test line with embedded color codes,
show me the actual output, and if it still contains escape sequences, fix it and try again --
repeat until the test line comes out clean."
The loop is anchored to an observable result, not the model’s confidence in its own code. It keeps correcting until reality confirms it, not until the explanation sounds plausible.

5.2 ReAct (Reason + Act)

Interleave reasoning with actual tool calls or actions, observing the real result of each step before reasoning about the next one. The next action is determined by the actual output of the prior step, not a plan made blind at the start.
"Find out why TextFinder1's build is failing on Windows.  Don't guess at the whole fix up front --
run the build, look at the actual error, reason about what it means, then take the next action
based on that, and keep going until it builds clean."
Reflexion loops on a single artifact (write code, test it, fix it); ReAct interleaves reasoning with arbitrary actions as it goes. The next action might be entirely different in kind from the last ‐ read a file, run a command, edit a different file ‐ not a revision of the same artifact.

5.3 Test-Driven Prompting

Write the tests or acceptance criteria first, then prompt for an implementation that must satisfy them. “Done” is defined before any code exists, rather than checked afterward.
"Before writing the multi-extension matching logic for TextFinder1, write 4-5 test cases covering:
single extension, comma-separated list, extension with/without leading dot, and an empty extension
string.  Show me the tests first.  Only after I approve them, write the implementation that passes
all of them."
Reflexion and ReAct both react to outcomes after an attempt. Test-Driven Prompting front-loads the definition of success so the very first implementation attempt is aimed at a fixed target ‐ and writing the tests often surfaces unresolved design questions before any implementation inherits them.

6. Shaping the Output

Shaping-the-output patterns constrain the form or content of what the model produces. Few-Shot and Template Filling fix shape ‐ by example and by explicit skeleton respectively. Role Framing and Constraint-Based Prompting fix substance ‐ by evaluative lens and by hard boundaries.

6.1 Few-Shot / Worked Examples

Show 1–3 concrete input→output examples before the real request, letting the model pattern-match to a definite shape rather than interpret an abstract description.
"I want function doc comments in this exact style for TextFinder1's core crate.
Here are two examples:"
/// Parses a comma-separated extension list into normalized form.
/// Returns Err if the input is empty or contains only whitespace.
pub fn parse_extensions(input: &str) -> Result<Vec<String>, MatchError>

/// Checks whether a line contains the configured pattern.
/// Empty lines never match, regardless of pattern.
pub fn matches(&self, line: &str) -> bool
"Now write doc comments in this same style for find_matches and from_args."
A description like "write good doc comments" leaves "good" open to interpretation. Two concrete examples remove that ambiguity entirely. The model mirrors structure, tone, and coverage, not just format.

6.2 Role / Persona Framing

Assign an expert persona to bias the model’s vocabulary, rigor, and priorities. Useful when you want a specific lens applied, not just a generic answer.
"Review this TextFinder1 directory-traversal function as a security auditor would -- focus
specifically on path handling, symlink behavior, and anything that could let traversal escape
the intended root directory."
Few-Shot fixes the output format with concrete examples; Role Framing fixes the evaluative lens the model reasons through, without specifying what the output should literally look like. The same code yields a different review depending on whether the assigned role is "security auditor," "maintainability reviewer," or "performance engineer."

6.3 Template Filling

Give a fixed output structure with named sections and have the model populate it, rather than free-form generate. Anything uncertain is expressed as an explicit open question, not a silent guess.
"Fill in this exact template for the new --regex feature spec -- don't add sections, don't remove
any, leave anything you're unsure about as <!-- open question --> rather than guessing:"
## Goal
## Behavior
## Edge Cases
## Out of Scope
## Acceptance Criteria
Few-Shot shows what a filled example looks like; Template Filling gives the empty skeleton itself and constrains the model to that exact structure. The explicit open-question placeholder forces uncertainty to the surface rather than letting the model silently pick an answer you never approved.

6.4 Constraint-Based Prompting

State hard constraints ‐ version limits, forbidden APIs, required error patterns ‐ up front so the model self-filters during generation, rather than producing something you then have to catch and correct.
"Write the new matcher logic for TextFinder1's core crate.  Hard constraints: must compile on
Rust 1.70 (no newer edition features), no unsafe blocks anywhere, no new dependencies beyond
what's already in Cargo.toml, and follow the existing Result<T, MatchError> error pattern
rather than panicking."
With boundaries stated before generation, the model avoids reaching for a newer stdlib API, introducing an unnecessary unsafe block, or pulling in a convenience crate. Template Filling constrains the document structure; Constraint-Based Prompting constrains the content and implementation choices within whatever structure is being produced.

7. Review / Retrospective

Review patterns bookend the same work from opposite ends. Pre-Mortem tries to buy down risk before it is incurred; Retrospective makes sure whatever risk did materialize gets converted into a lesson rather than just fixed and forgotten.

7.1 Pre-Mortem

Before starting work, ask "assume this fails ‐ why?" to surface risks while there is still time to change course.
"I'm about to approve the plan for adding --regex support to TextFinder1.  Before I approve it,
do a pre-mortem: assume we implemented this exactly as planned and it caused problems three months
from now.  What's the most likely reason, and what in the current plan should change to prevent it?"
Pre-mortems surface process and scope risks that a code review or test suite would never catch, because nothing is broken yet ‐ the plan is just incomplete in a way that won’t bite until later. The failure mode is often not a coding mistake at all.

7.2 Retrospective / Postmortem

After the work is done, review what actually happened against what was planned ‐ not to assign blame, but to extract a concrete change for next time.
"We just finished implementing --regex for TextFinder1.  Do a retrospective: where did the actual
implementation deviate from the original plan, what caused each deviation, and what should change
in how we plan features like this going forward?"
Pre-Mortem reasons about a hypothetical failure before anything exists; Retrospective reasons about what actually happened, using real deviations as evidence. It is strictly more reliable than Pre-Mortem, but only available after any mistakes have already been paid for.