· 5 min read
AI Is Not a Magic Bullet
Your tech stack already solves most of your problems. AI should enhance specific gaps, not replace things that already work. A real-world story about what happens when it does.
Published: · 5 min read
Your tech stack already solves most of your problems. AI should enhance specific gaps, not replace things that already work. A real-world story about what happens when it does.
I worked on a project recently where we needed to generate letters for clients. Formal correspondence, based on application data. The kind of thing that has a fixed structure with a few dynamic parts.
PHP is very good at this. It has been very good at this for decades.
The system used Blade templates. Each letter type had a template with placeholders for client data. Name, address, dates, reference numbers. Standard string replacement.
<p>Dear {{ $application->client_name }},</p>
<p> Following your application submitted on {{ $application->submitted_at->format('d/m/Y') }}, we are writing to confirm the outcome.</p>
<p> Your application was {{ $application->status }} for the following reason:</p>
<p>{% ai_enhance summarise the reason based on {{ $application->reason }} %}</p>
<p>If you have any questions, please contact us.</p>Most of the letter was deterministic. Fixed structure, known values, predictable output. The one place where AI added value was summarising a free-text reason field into something readable for the client. A small, well-scoped job.
This worked. The letters came out consistent. The AI did one thing and did it well. Everything else was handled by the language that was built for it.
An advisor to the company had a different vision. The entire letter generation should be handled by AI. No templates. No Blade. A prompt-based system where you described what you wanted, fed in the application data, and let the model produce the full letter.
The prompt looked something like this:
You are a formal letter writer for [Company Name].Generate a letter for the following application:
Client: {client_name}Reference: {reference_number}Status: {status}Reason: {reason}Date submitted: {submitted_at}
The letter should be professional, formal, and includeall relevant details. Use the company letterhead format.Include the reference number in the header.On paper, this sounds reasonable. In practice, it fell apart immediately.
The model hallucinated reference numbers. It invented dates that weren’t in the data. It changed the tone between runs. Two letters for the same application would read completely differently. Sometimes it added paragraphs that referenced policies that didn’t exist.
The system went from predictable to probabilistic. For formal client correspondence, that’s not acceptable.
Every letter needed manual review. The time saved by removing templates was spent checking AI output for errors. The team lost confidence in the system and started rewriting letters by hand.
The complexity increased too. Error handling for malformed AI responses. Retry logic for when the model returned something unusable. Validation layers to catch hallucinated data. All of this to replace something that already worked.
// Before: deterministic, testable, predictable$letter = view('letters.approved', [ 'application' => $application])->render();
// After: probabilistic, untestable, unpredictable$letter = $ai->generate( prompt: $this->buildLetterPrompt($application), temperature: 0.3, max_tokens: 2000);
// Plus validation, retry logic, manual review queue...if (!$this->validateLetter($letter, $application)) { $this->flagForReview($letter, $application);}The AI-driven approach lasted a few months. It got replaced by something close to the original. Templates with fixed structure, known values in known positions, and AI used only where it added genuine value: summarising free-text fields.
The final version was simpler than both previous attempts. The templates were cleaner. The AI enhancement was more targeted. The letters were consistent and correct.
PHP has been doing string replacement and templating since 1995. It is exceptionally good at it. Blade, Twig, even raw PHP, these are tools purpose-built for exactly this kind of work. Thirty years of refinement, edge cases handled, encoding issues solved.
When your tech stack already solves the problem, adding AI doesn’t improve it. It replaces a known solution with an unknown one.
AI is good at specific things. Summarising text. Classifying intent. Generating natural language from structured data. These are real, useful capabilities. But they work best when scoped tightly to the task they’re suited for.
The mistake is treating AI as a general-purpose replacement for things your existing tools already handle. String replacement is a solved problem. Date formatting is a solved problem. Letter structure is a solved problem. You don’t need a language model for solved problems.
The pattern that works is straightforward. Let your tech stack do what it’s built for. Use AI for the specific gaps where probabilistic output is acceptable and where deterministic code can’t do the job.
A Blade template with an AI-enhanced summary field is better than a fully AI-generated letter. It’s more predictable, more testable, more maintainable, and it actually works.
Your framework, your language, your database, these tools have years of development behind them. They handle edge cases you haven’t thought of. They have test suites. They produce the same output every time.
AI is a tool. A good one. Use it where it fits, and leave the rest to the tools that were built for the job.
· 5 min read
Your tech stack already solves most of your problems. AI should enhance specific gaps, not replace things that already work. A real-world story about what happens when it does.
· 4 min read
Developers are either dismissing AI or panicking about it. Both reactions are wrong. The tools are here, they're getting better fast, and the job now is to pick them up.