Building AI-powered applications with LLM APIs looks deceptively simple. The hello-world is five lines: send a prompt, get a completion, show it to the user. The distance between that and a production feature โ one that behaves predictably, fails gracefully, stays within budget, and survives a model upgrade โ is where most teams learn their expensive lessons. This article collects the patterns that have stabilized across the industry: how to structure LLM calls, control costs, handle the inherent weirdness of probabilistic components, and avoid the pitfalls that recur in almost every postmortem.
Architecture: treat the model as an unreliable dependency
The foundational mindset shift is architectural. An LLM API is a remote dependency that is slow (seconds, not milliseconds), variable (same input can yield different output), rate-limited, and occasionally wrong with high confidence. Everything you already know about calling unreliable external services applies, plus a few new rules:

- Put a gateway in front of it. Route all LLM calls through one internal service or module that handles retries with backoff, timeouts, rate-limit queuing, logging, and provider failover. This is also where model routing lives โ sending cheap tasks to cheap models โ and where you swap providers without touching application code.
- Design for asynchrony. Anything beyond a short completion belongs in a job queue with status updates, not a blocking HTTP request. For interactive features, stream tokens to the user โ perceived latency is the latency that matters.
- Version your prompts. Prompts are code. Store them in the repository, review changes, and tag outputs with the prompt version that produced them so regressions can be traced.
- Log everything (minus secrets). Request, response, model, token counts, latency, and cost per call. You cannot debug, evaluate, or optimize what you did not record.
Structured output: the pattern that makes LLMs composable
Free-text output is fine for chat; it is a liability everywhere else. The single most useful production pattern is forcing the model to return structured data โ JSON conforming to a schema โ so downstream code can consume it like any other API response. Modern APIs support this natively via structured-output modes and tool/function definitions. Validate the result against the schema anyway, and handle failure explicitly:
result = llm.complete(prompt, response_schema=InvoiceExtraction)
parsed = validate(result, InvoiceExtraction)
if parsed is None:
retry_once_with_error_feedback()
# still failing -> route to human review queue, never guess The companion rule: keep each call\'s job small. A pipeline of focused calls โ classify, then extract, then summarize โ outperforms one mega-prompt in accuracy, testability, and cost, and lets you use cheaper models for the easy stages.
Costs: where the money goes and how to control it
LLM costs are token costs, and they surprise teams in predictable ways. Input tokens usually dominate โ context, retrieved documents, conversation history โ not the visible output. The standard control levers, roughly in order of impact:

- Model routing. Most traffic in most applications is easy: classification, short rewrites, routine extraction. Route it to small, cheap models and reserve frontier models for the requests that need them. Blended cost reductions from routing alone are frequently dramatic.
- Prompt caching. Providers offer steep discounts for repeated prompt prefixes. Structure prompts so the static parts โ instructions, schemas, few-shot examples โ come first and stay byte-identical across calls, with the variable content at the end.
- Context discipline. Retrieve less, summarize conversation history instead of replaying it, and cap document sizes. Ten thousand tokens of "just in case" context on every call is a tax with no refund.
- Batch APIs. For non-interactive workloads, batch endpoints trade latency for significant discounts โ a free lunch for nightly pipelines.
- Budgets and alerts from day one. Per-feature cost tracking and hard spending alerts belong in the first sprint, not the postmortem. A retry loop calling a frontier model is a very efficient money-burning machine.
Handling the failure modes
Every LLM application inherits a standard set of failure modes; mature applications have an explicit answer for each:
- Hallucination: ground responses in retrieved or provided data, require citations where feasible, and give the model explicit permission to say it does not know โ then test that it does.
- Prompt injection: if the application processes untrusted content (user uploads, emails, web pages), assume that content contains instructions. Separate system instructions from data, scope any tools to least privilege, and gate consequential actions on human approval.
- Nondeterminism: lower temperature for extraction and classification tasks; design UX so variability is acceptable where it remains; never build logic that assumes identical output for identical input.
- Model updates: providers deprecate and upgrade models on their schedule, and behavior shifts even between minor versions. Pin model versions explicitly, and keep an evaluation suite you can run against a new version before migrating โ this converts a risky forced migration into a routine regression test.
- Rate limits and outages: queue and shed load gracefully, and decide per feature what degraded service looks like โ a cached response, a simpler model, or an honest "try again later."
Evaluation: the tests your AI feature actually needs
Unit tests cannot assert exact strings from a probabilistic component, so production teams build evaluation suites instead: a few hundred representative inputs with expected outcomes or scoring rubrics, run automatically against every prompt change, model swap, and provider update. Grade what matters โ correctness, groundedness, format compliance, refusal behavior โ using code where possible and an LLM judge where necessary, with periodic human spot checks to keep the judge honest. Pair offline evaluation with online signals: thumbs ratings, edit distance between AI drafts and what users actually send, escalation rates. The teams that ship AI features confidently are, without exception, the teams with evaluation infrastructure.

The pitfalls that recur in every postmortem
A short list worth pinning above the sprint board: shipping a chat interface when a button would do (open-ended input multiplies failure modes); skipping the human-review queue for outputs that carry real consequences; letting one prompt grow into a thousand-line monolith nobody dares edit; benchmarking on five hand-picked examples; and assuming today\'s model behavior is permanent. None of these are model limitations. All of them are engineering choices.
The encouraging conclusion is that building well with LLM APIs is a learnable discipline, not alchemy. The patterns are known: gateway and routing, structured outputs, small focused calls, caching, grounding, evaluation, and honest failure handling. Teams that apply them ship AI features that feel dependable. Teams that skip them ship demos โ and the difference becomes obvious about two weeks after launch.
Related Service
๐ค Business Process Automation
Business process automation with n8n, Zapier, Make, and AI โ connect your tools, eliminate repetitive work, and let workflows run themselves around the clock.
Explore Business Process Automation →
Reviews & Comments
Reviews are moderated and appear after approval.
No reviews yet โ be the first to share your thoughts.
Leave a Review