Everyone's building AI features now. Most of them are getting mediocre results from capable models and blaming the model when the problem is actually the prompt.
Prompt engineering isn't about magic phrases. It's about understanding how language models process context and structuring your input so the model has everything it needs to do the job well.
Here's what actually moves the needle.
Be Specific About Output Format
The single highest-leverage thing you can do is tell the model exactly what format you want the output in. Vague prompts produce vague outputs.
javascript// Vague
const prompt = Analyze this customer review and tell me if it's positive or negative.;
Review: ${review}
// Specific
const prompt = `Analyze the following customer review and respond with ONLY a JSON object
in this exact format:
{
"sentiment": "positive" | "negative" | "neutral",
"confidence": 0.0 to 1.0,
"key_topics": ["topic1", "topic2"],
"flags": {
"mentions_competitor": boolean,
"requests_refund": boolean,
"has_profanity": boolean
}
}
Review: ${review}
Respond with only the JSON. No explanation, no markdown code block.`;
Use a System Prompt to Set Behavior, Not the User Turn
The system prompt defines who the model is and how it should behave. The user turn is where the actual task goes. Mixing them creates inconsistent behavior.
javascriptconst response = await client.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 1024,
system: You are a code review assistant. You review code for correctness, ,
performance, and security issues. You are direct and specific. You cite line
numbers when pointing out issues. You do not compliment code or add positive
framing — just identify problems and suggest fixes. Format your output as a
numbered list.
messages: [{
role: 'user',
content: Review this function:\n\n${code}
}]
});
Chain Prompts for Complex Tasks
If you're trying to get the model to do multiple things in one prompt (extract, then analyze, then format), split it into separate calls. Each step produces cleaner output.
javascript// Step 1: Extract the facts
const extractionResponse = await client.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 512,
messages: [{
role: 'user',
content: Extract only the factual claims from this text. Output as a JSON array of strings.
Text: ${articleText}
}]
});
const facts = JSON.parse(extractionResponse.content[0].text);
// Step 2: Verify each claim
const verificationResponse = await client.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 1024,
messages: [{
role: 'user',
content: For each claim, rate it as "likely_true", "uncertain", or "likely_false"
based on general knowledge. Return JSON.
Claims: ${JSON.stringify(facts)}
}]
});
Two focused prompts beat one complex prompt almost every time.
Give the Model a Way Out for Uncertainty
If you don't, it will make things up.
javascriptconst prompt = `Answer the following question based ONLY on the provided context.
If the answer is not clearly stated in the context, respond with exactly:
{"answer": null, "reason": "not_in_context"}
Context: ${context}
Question: ${question}`;
This prevents hallucination by giving the model an explicit path when it doesn't know something.
Few-Shot Examples Are Powerful
If you want consistent output format or style, show the model examples of what you want rather than just describing it.
javascriptconst prompt = `Convert raw support ticket text to structured data. Here are examples:
Input: "My order #12345 hasn't arrived and it's been 2 weeks. I need this fixed ASAP."
Output: {"order_id": "12345", "issue": "delivery_delay", "urgency": "high", "action_needed": "investigate_shipment"}
Input: "How do I change my password?"
Output: {"order_id": null, "issue": "account_help", "urgency": "low", "action_needed": "send_help_article"}
Now process this:
Input: "${ticketText}"
Output:`;
The model will match the pattern you've shown rather than inventing its own format.
Temperature: When to Change It
Most people don't touch temperature. Here's when to:
Creative writing, brainstorming: temperature 0.8–1.0 (more varied, less predictable)
Data extraction, classification, structured output: temperature 0.0–0.2 (more consistent, deterministic)
General Q&A: default (0.7 or whatever the API default is) is usually fine
For any production feature that needs consistent behavior, keep temperature low.
javascriptconst response = await client.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 256,
temperature: 0.1, // Low for structured extraction
messages: [{ role: 'user', content: prompt }]
});
Top comments (0)