Skip to content
DevPedia

Getting startedGuide 2 of 12

How to write good prompts for Claude Code

How to phrase clear requests, supply the context that's needed, and define criteria for judging the result.

Updated 9 min read

The five-piece structure

Claude Code makes better decisions the better defined the problem is. Almost every professional prompt is assembled from up to five pieces:

PieceAnswersAlways needed?
ContextWhat does Claude need to know about the situation?No — a trivial fix may not need it
TaskWhat exactly does it have to do?Yes, always
ConstraintsWhat must it not touch, and what limits does it have?Recommended for anything non-trivial
FormatHow do you want the result?When the default doesn’t work for you
VerificationHow do you confirm it came out right?Yes, almost always — it’s the piece most often forgotten
01Context
02Task
03Constraints
04Format
05Verification
06Ready for the next step
If Verification doesn't pass, it goes back to Context

When a result isn’t what you expected, in the vast majority of cases one of these five pieces is missing — what’s missing isn’t “more AI”.

Seven patterns for everyday work

The examples use the same fictional project: ReservaResto, a restaurant table booking platform, with Node.js + Express + TypeScript + PostgreSQL.

1. Implement a feature

Context: ReservaResto is a restaurant table booking platform
(Express + TS + PostgreSQL). Restaurant signup and reservation
creation already work.

Task: Implement a waitlist. When a restaurant is full for a time
slot, a person can put their name down. When a reservation is
cancelled, automatically offer the spot to the next person on the
list for that slot. Add an endpoint for the restaurant owner to see
how many people are waiting, by time slot.

Constraints:
- Confirming a reservation must not get slower (process the
  waitlist after responding to the user).
- Follow the existing module structure.
- Don't change the interface of the current reservation endpoints.

Verification: Write tests for joining the list and for the
automatic promotion when a spot is cancelled. Run the whole suite
when you're done.

The context keeps it from inventing an architecture of its own; the constraints protect what already works; the verification makes Claude run the tests (or some other criterion) and treat a failure as part of the task, instead of handing you code you then have to start debugging.

2. Debug a problem

The most common mistake when reporting a bug is saying “it doesn’t work” and expecting it to guess. A good bug report for an agent looks like the one you’d give a teammate: exact error, where it happens, what you were doing.

Context: I'm testing concurrent reservations in ReservaResto.

Task: If two people book the last available table at the same
restaurant for the same time slot within the same minute, both
reservations end up confirmed — one table too many is committed and
neither one raises an error.

Format: Before fixing anything:
1. Identify the root cause, not the symptom
2. Explain why it happens
3. Propose the fix and justify it
4. What else could be affected?

Verification: A test that simulates two concurrent reservations for
the same time slot and confirms only one is accepted.

Asking it to think before touching code produces more precise fixes, and asking about side effects keeps one fix from creating two new problems.

3. Refactor

This is the pattern where it’s easiest to overrun: with no clear constraints, “refactor this module” can end in a rewrite that breaks half the application. The key is defining what must not change.

Context: In ReservaResto, validation is scattered: the reservation
service validates time slots, the restaurant service validates
table capacity, the user service validates party size. I want to
centralize it.

Task: Extract all validation into a dedicated module the other
services import.

Constraints:
- The endpoints must return exactly the same errors.
- The existing tests pass without modifying them.
- The new module follows the current naming and export convention.

Format: Before touching code, show me what you'd change, which
files it affects, and what risks you see.

Verification: Run the whole suite without touching it. If something
fails, the refactor broke something.

“The tests pass without changing them” is the most powerful safety net you can give it: it turns a potentially destructive change into something objectively verifiable.

4. Explore a project

This pattern doesn’t produce code, it produces understanding — and it’s the step most people skip before a big change.

Task: Analyze ReservaResto and answer:
1. What pattern separates routes from business logic?
2. How is the database initialized and accessed?
3. What naming conventions do the files and functions use?
4. If I add a "reviews" module (so diners can rate a restaurant),
   which files do I need in order to follow the same pattern?

Format: A short summary per question + an ASCII diagram of the
relevant folder structure.

Constraints: Don't modify anything. Read-only.

When it explores before implementing, it anchors its decisions in the real code instead of in generic assumptions — and the code it generates afterwards will be consistent with what already exists.

5. Write tests

Asking for tests “for X” with no further detail produces tests that only confirm the function doesn’t blow up. Specifying the three categories with concrete examples changes everything:

Context: booking.service.ts has createReservation and
cancelReservation. It integrates with an external payment gateway
for the deposit.

Task: Generate tests for each public function:
- Happy path: a successful reservation returns a confirmation code;
  a successful cancellation releases the table.
- Edge cases: booking the last available table, cancelling right at
  the edge of the free-cancellation window, last-minute booking
  (same day).
- Errors: booking with no tables available, cancelling an already
  cancelled reservation, booking without required fields.

Constraints: Jest with describe/it. Tests independent of each other.

Verification: Run the tests and then `jest --coverage`. Show me the
coverage for booking.service.ts.

6. Document

The simplest pattern in structure, but the one that benefits most from your telling it the format. Defining who is going to read the documentation changes the result radically — “for developer onboarding” produces something very different from “for the QA team”.

Context: New developers need a reference for the ReservaResto API
to get started.

Task: Generate docs/API.md with: a project description,
installation/running/tests, each endpoint (method, path, auth,
parameters, response, possible errors), and a curl example.

Format: Markdown with sections per module (Restaurants,
Reservations, Waitlist).

Verification: Compare against the routes registered in the code. If
an endpoint is missing, add it.

7. Ask for a rationale

This isn’t an implementation pattern, it’s a review one. You use it after Claude has done something, to understand the why and spot better alternatives — something it doesn’t usually explain unless you ask.

About the waitlist implementation:
1. What alternatives did you consider for not blocking the
   reservation confirmation?
2. If the write fails while promoting the next person on the list,
   is that lost silently?
3. If ten reservations at the same restaurant are cancelled at the
   same time, which part of the system fails first?
4. If you had to scale this to thousands of restaurants at once,
   what would you change about the architecture?

Chaining patterns

A complex task isn’t one giant prompt, it’s a sequence where each instruction uses a different pattern and produces something verifiable before you launch the next step:

Explore     → "Analyze how module X works"
Implement   → "Following that pattern, add Y"
Tests       → "Generate tests for what you implemented"
Rationale   → "What alternatives did you consider?"
Document    → "Update the documentation with the changes"

If at any link in the chain you can’t check the result, that step is too big or it’s missing the verification component — go back and split it.

With practice, structuring your requests this way becomes second nature, and you no longer need to refer to the table.

When the chain of prompts stays long even though it’s well split, or the team needs to agree on the scope of a change before touching code, it’s worth adding a layer above loose prompts: see Spec-Driven Development.

Related documentation: official prompt engineering guide and Best practices for Claude Code.

Share this guide

Search by concept, pattern or practice.