Skip to content

Module 3: Effective Prompting

Claude Code is only as good as your prompts, but prompting an agent is different from prompting a chatbot. You're not asking questions; you're delegating tasks to a system that can read files, run commands, and make changes autonomously. The quality of that delegation determines the quality of the output.

This module covers what makes a prompt effective, when different prompt patterns apply, and how to build verification into every request.


Be Specific, State Constraints, Give Context

Most prompt failures come down to three problems: the request is too vague, the boundaries aren't clear, or Claude is missing information it can't infer from code alone.

Vague vs. Specific

Vague promptSpecific prompt
Fix the bugThe form doesn't validate email addresses. Add validation to contact.js only.
Make it look betterIncrease heading font size to 24px in globals.css
Add a featureAdd a dark mode toggle button in the header component
Clean up the codeExtract the repeated database connection logic in routes/users.js and routes/orders.js into a shared db.js module

Vague prompts force Claude to guess your intent. It fills the ambiguity with assumptions, sometimes good, often not. Specific prompts remove the guessing.

State Constraints Explicitly

Without boundaries, Claude tends to over-deliver. You ask for a bug fix; it refactors the file, updates the error handling, and adds logging you didn't request. Constraints prevent this.

Add input validation to the contact form. ONLY modify contact.js — don't touch other files.
Follow the existing pattern in CustomerService when implementing OrderService.
Don't introduce new abstractions or base classes.
Fix the null reference in calculateTotal. Don't refactor the method — just handle the null case.

The word "ONLY" is your most useful constraint. Use it freely.

Use @ to Reference Files Directly

When your prompt involves specific files, use @ to reference them. This tells Claude exactly which files to focus on, saving context and avoiding unnecessary searches.

Look at @src/middleware/auth.ts and add rate limiting to the login handler.
The pattern in @src/services/UserService.ts should be followed when
implementing OrderService.
@package.json has the test script — run it after making changes.

Without @, Claude searches for files by name, which costs more context and can find the wrong file if names are ambiguous. With @, it reads the exact file you mean.

Give Context the AI Can't Infer

Claude can read your code, but it can't read your mind, your deployment architecture, or your team's unwritten rules. If something matters and it's not in the code or CLAUDE.md, say it.

This endpoint is consumed by a legacy SAP system — it must return XML, not JSON.
We use Decimal for all money calculations, never float or double.
This service runs behind an API gateway that already handles authentication.
Don't add auth middleware here.
The database is PostgreSQL 12 — don't use features from newer versions.

When In Doubt, Over-Specify

It's faster to give Claude information it didn't need than to redo work because it was missing context. A prompt that's "too detailed" still produces correct results. A prompt that's too vague produces a result you have to reject and rephrase.


Verification-First Prompting

This is the single most impactful prompting habit you can build: include your expected outcome or test criteria in the prompt itself.

Instead of hoping Claude gets it right and checking afterward, tell Claude what "right" looks like upfront. This turns Claude from "generate code and hope" into "generate code, verify, fix, verify again."

Without verification:

Add email validation to the contact form.

With verification:

Add email validation to the contact form. Write a test that verifies
empty strings, missing @, and unicode characters are rejected.
Run the tests to confirm they pass.

The second prompt triggers Claude's agentic verify loop: it writes the code, writes the test, runs the test, reads the failures, fixes them, and re-runs until green.

More examples:

Add rate limiting to /api/login. After implementing, write a test that
sends 6 requests within one minute and verifies the 6th returns 429.
Run it.
Refactor OrderService to use the repository pattern. The existing test
suite in tests/orders/ should still pass without modifications. Run
the tests after refactoring to confirm.
Fix the currency formatting bug. The function should format 1234.5 as
"$1,234.50" and 0 as "$0.00". Add these as test cases and run them.

Why This Matters

Without verification criteria, Claude gives you code that looks correct. With verification criteria, Claude gives you code that is correct, because it ran the checks itself before returning. The difference compounds across a session.


One Task per Prompt

Resist the urge to batch unrelated changes into a single prompt. Multi-task prompts create several problems:

  • Claude's attention splits, and quality drops on each individual task
  • If one part fails, it's harder to isolate what went wrong
  • Diffs become large and difficult to review
  • Rejecting one change means rejecting all of them

Instead of this:

Add input validation to the form, update the footer copyright year,
and refactor the database queries to use parameterized statements.

Do this:

Add input validation to the contact form email field. Reject empty
strings and strings without @. Only modify contact.js.

Review. Accept or iterate. Then move to the next task.

The exception: closely related changes that are part of the same logical unit. "Add the EmailService class and update OrderService to use it" is one task with two parts, not two unrelated tasks.


Prompt Patterns for Different Task Types

Different types of work call for different prompt structures. Here are the patterns that consistently produce good results.

Explore

Use when you need to understand code before changing it. These prompts keep Claude in read-only mode mentally. It focuses on tracing logic rather than generating code.

How does the authentication system work? Trace the flow from login
request to session creation.
What happens when a payment fails? Follow the error path through the
codebase.
Show me all the places where user roles are checked. Are they
consistent?

Implement

Use when you know what you want built. Always specify the location, the pattern to follow, and ideally a verification step.

Add rate limiting to the /api/login endpoint. Use the existing Redis
client in src/lib/redis.ts. Limit to 5 attempts per minute per IP.
Return 429 with a Retry-After header when the limit is exceeded.
Create a new endpoint POST /api/webhooks that accepts a JSON payload,
validates the signature header against WEBHOOK_SECRET from env, and
queues the event using the existing BullMQ setup in src/queues/.
Write a test.

Test

Use when you want test coverage for existing code. Specify what to cover and where the test infrastructure lives.

Write tests for the calculateDiscount function covering: zero quantity,
negative prices, bulk discount thresholds. Use the existing test setup
in tests/.
The UserService has no tests. Write unit tests for createUser,
updateUser, and deleteUser. Mock the database layer. Follow the
patterns in tests/OrderService.test.ts.

Refactor

Use when restructuring existing code. The key constraint: specify what should NOT change (interfaces, tests, external behavior).

Extract the email-sending logic from OrderService into a dedicated
EmailService. Keep the same interface. Don't change any tests — they
should still pass.
The API route handlers in routes/users.js are doing validation,
business logic, and database access inline. Extract the business logic
into a UserService class. The route handlers should only parse
requests and return responses.

Debug

Use when something is broken. Give Claude the symptom, ask for the root cause, and include a verification step.

The /api/orders endpoint returns 500 when quantity is 0. Find the root
cause and fix it. Run the test suite to verify the fix doesn't break
anything else.
Users report that search results are duplicated when the query contains
special characters. Reproduce the issue, find the cause, and fix it.
Add a test case for this scenario.

Common Prompting Mistakes

Asking "Can you...?": Claude always says yes. Skip the question and give the instruction directly. Not "Can you add validation?" but "Add validation to..."

Providing the solution instead of the problem: "Add a try-catch around line 42" is less effective than "The function crashes when the input array is empty. Fix it." Claude might find a better solution than your assumed one.

Forgetting to constrain scope: Every implementation prompt should have an explicit boundary. Without one, Claude interprets your request as broadly as possible.

No verification step: If your prompt doesn't include "run the tests" or "verify that X works," you're leaving correctness to chance.

Key Takeaway

The best prompts have three parts: what to do, where to do it (and what not to touch), and how to verify it worked. Get those three right and the quality of Claude Code's output jumps dramatically.

Want to discuss a project like this?

SFLOW helps companies implement practical AI solutions — from custom platforms to industrial integrations with your existing systems.

SFLOW BV - Polderstraat 37, 2491 Balen-Olmen