Prompt Engineering for Developers: What Actually Works in Production
AIClaude APIOpenAILaravel

Prompt Engineering for Developers: What Actually Works in Production

Practical techniques I use when integrating Claude and OpenAI APIs into real applications — output format control, system prompts, few-shot examples, and defensive parsing.

Mokammel Tanvir

Mokammel Tanvir

Software Engineer

Skip the Theory

Most prompt engineering content is either academic papers nobody applies in practice or marketing material from AI companies. This is neither.

I've been integrating Claude and OpenAI APIs into production systems at Smart Provider LLC for over a year. These are the techniques that changed output quality in ways that actually mattered for shipping reliable features.

Be Explicit About Output Format

Vague prompts produce vague outputs. If your code needs to parse the response, the LLM needs to know the exact format to return — not approximately, exactly.

// Bad — output shape is unpredictable
$prompt = "Extract the customer name and address from this: {$rawData}";

// Good — explicit structure the code can parse reliably
$prompt = <<<PROMPT
Extract the following fields from the text below.
Return ONLY a valid JSON object with this exact structure — no explanation, no markdown, no extra fields:

{
    "customer_name": "string or null",
    "street_address": "string or null",
    "city": "string or null",
    "state": "string or null",
    "zip_code": "string or null"
}

Text:
{$rawData}
PROMPT;

The second prompt returns parseable JSON consistently. The first returns whatever the model feels like on that particular run.

System Prompts Are Not Optional

A system prompt sets context for the entire conversation — role, constraints, and behavior when input is ambiguous. Don't skip it.

$response = Http::withHeaders([...])->post('https://api.anthropic.com/v1/messages', [
    'model'      => 'claude-sonnet-4-6',
    'max_tokens' => 512,
    'system'     => 'You are a data extraction assistant for a service management platform. Extract structured information from unstructured service documents. Always return valid JSON. If a field is not present in the text, return null — never guess or infer values.',
    'messages'   => [['role' => 'user', 'content' => $prompt]],
]);

Without a system prompt, you're relying on the model's default behavior. With one, you're defining exactly what role it plays.

Few-Shot Examples Beat Instructions

Showing the model correct input/output pairs is more reliable than explaining what to do. For structured extraction tasks, three good examples outperform two paragraphs of instructions.

$prompt = <<<PROMPT
Example 1:
Input: "Customer: John Smith, 123 Main Street, Atlanta GA 30301"
Output: {"customer_name": "John Smith", "street_address": "123 Main Street", "city": "Atlanta", "state": "GA", "zip_code": "30301"}

Example 2:
Input: "Service request from ABC Corp - no address on file"
Output: {"customer_name": "ABC Corp", "street_address": null, "city": null, "state": null, "zip_code": null}

Now extract from:
Input: "{$rawData}"
Output:
PROMPT;

Temperature: Low for Extraction, Higher for Generation

Temperature controls output randomness. For extraction where you need consistent, predictable structure — use 0 or close to it. For content generation where variation is good — use 0.7 or higher.

I've seen developers leave temperature at the default for extraction tasks and then spend days debugging why identical input occasionally produces a slightly different JSON structure.

Parse Defensively

LLMs sometimes wrap JSON in markdown code blocks. They return valid-looking JSON with wrong types. They include extra fields you didn't ask for.

function parseAIResult(string $raw): ?array
{
    // Strip markdown fences if present
    $clean = preg_replace('/```json?\s*(.*?)\s*```/s', '$1', $raw);

    $data = json_decode(trim($clean), true);

    if (json_last_error() !== JSON_ERROR_NONE) {
        Log::warning('AI returned invalid JSON', ['raw' => $raw]);
        return null;
    }

    $required = ['customer_name', 'street_address', 'city', 'state', 'zip_code'];
    foreach ($required as $field) {
        if (!array_key_exists($field, $data)) {
            Log::warning('AI response missing field', ['field' => $field]);
            return null;
        }
    }

    return $data;
}

When parsing fails, log the raw output and flag the record for manual review. Never silently discard failures.

The Test I Run Before Shipping

Write the prompt, run it against 20 real examples from production data, count how many return correct parseable output. If it's below 95%, the prompt needs more work.

The LLM isn't the unreliable part. The prompt usually is.

All Posts
Mokammel Tanvir

Mokammel Tanvir

Full-Stack Engineer · Laravel · Vue · WordPress · AI

Building web applications with Laravel, Vue/Nuxt, and WordPress — SaaS platforms, REST APIs, and AI-integrated workflows. Open to remote and hybrid opportunities.