Engineering··10 min
Prompt engineering for Claude Code: 12 patterns that actually work
Practical prompt patterns for getting better, faster, more reliable code out of Claude Code — based on a year of shipping production work with the agent.
Most "prompt engineering" content is hype. Real prompt engineering for coding agents is a small set of patterns that, when followed consistently, dramatically improve output. Here are the 12 that have survived a year of production use with Claude Code.
1. State the constraint before the goal
-----------------------------------------
Bad: "Write a function that fetches user data."
Good: "We use Drizzle ORM, the existing helper is db.users.findMany. Add a function that fetches a single user by id — should match the existing pattern in lib/db/queries.ts."
The model produces dramatically better code when you frontload the constraints (what library, what pattern, what file) before stating the goal. This single change probably accounts for 30% of the quality difference between novice and expert prompt-writers.
2. Reference real files, not abstractions
-------------------------------------------
Bad: "Add a similar pattern to our other utilities."
Good: "Add it the same way lib/utils/format-date.ts does — same file structure, same export style, same JSDoc comment shape."
Concrete file references give the model an exact target to mimic. "Similar pattern" gives it nothing.
3. Ask for a plan before code on multi-file changes
----------------------------------------------------
For anything touching 3+ files, prefix your request with: "First, list the files you'll change and what each change is. Don't write any code yet."
This costs you one extra turn and saves you 10 turns of debugging the agent's wrong assumptions. You can also catch mistakes before they're written.
4. Use TODO comments as scope markers
---------------------------------------
When you want changes constrained to a region:
// TODO(claude): rewrite this block to use streaming
// ... existing code ...
// TODO(claude): end of scope
Claude treats those markers as boundaries. Without them, it tends to "improve" surrounding code that you didn't ask to change.
5. Pin the test framework explicitly
-------------------------------------
Claude defaults to Vitest in TypeScript projects, even when you're using Jest. State the framework in your first prompt of the session: "We use Jest and @testing-library/react. Use describe/it style." This avoids the painful 3rd-turn discovery that all your tests use the wrong matcher.
6. Reject unfounded "while I'm here" changes
----------------------------------------------
When Claude returns a 200-line patch and 40 of those lines are "while I'm here" cleanups: reject the patch and reissue with "only change the function I asked about. Don't touch anything else, even if you think it could be improved."
Drift is the silent killer of long sessions. Police it from prompt 1.
7. Quote error messages literally
-----------------------------------
Bad: "I'm getting a TypeScript error about union types."
Good: paste the exact error, e.g.:
Type 'User | null' is not assignable to type 'User'.
Type 'null' is not assignable to type 'User'.
Claude is excellent at parsing error messages — but it's much worse at guessing which error you saw. The fix on a paraphrase is often unrelated to the actual issue.
8. Use "minimum diff" framing for fixes
-----------------------------------------
For bug fixes specifically:
Apply the smallest possible change that fixes this bug. No
refactoring. No "while I'm here" cleanups. The diff should
be as small as possible.
This frames the model toward surgical edits and away from rewriting the file.
9. Bring in skill files for repeat work
-----------------------------------------
If you find yourself repeatedly saying "use Drizzle ORM, the helper is at lib/db, write tests with Jest, follow the conventions in lib/utils" — promote that into a SKILL.md and install it once with claude skills add. The skill stays active for the project; you stop re-typing the constraints.
For common patterns, check claudeskil.com/explore — many are already published.
10. Use placeholders for sensitive data
-----------------------------------------
Bad: "My API key is sk-12345; use it to call X."
Good: "I'll provide an API key as the env var FOO_API_KEY. Generate code that reads from process.env.FOO_API_KEY."
Claude actively refuses to embed secrets in code, but the cleaner pattern is just not to provide them in the first place. Reference the env var by name and pass real values at runtime only.
11. Use diff format for surgical edits
----------------------------------------
When you want a 3-line change in a 500-line file:
Output the change as a unified diff against the current file.
No prose, no full-file rewrite — just the diff.
Claude produces clean diffs reliably. This is much faster to review and apply than a full-file regeneration.
12. End complex sessions with a "summarise what changed"
----------------------------------------------------------
Before you commit, ask: "Summarise the changes you've made in this session, file by file. Anything I should know before reviewing?"
This catches subtle drift. Claude often confesses to "while I was at it, I also..." changes that weren't in the original ask. Better to find that before you commit than after.
The meta-pattern
-----------------
All twelve patterns share a structure: be more specific about constraints, more concrete about references, and more explicit about scope. Models reward specificity disproportionately. The work of prompt engineering is the work of being clearer than feels necessary.
For more patterns specific to your role (frontend, data, SQL, SEO, etc.), the SKILL.md ecosystem at claudeskil.com has community-tested skills you can install in 60 seconds.