Web Development

Stop Treating Your LLM Provider Like It's Forever

A

Adil Sher

Author

Aug 18, 2026
5 min read
8 views
Stop Treating Your LLM Provider Like It's Forever

I spent three weeks last month ripping out Anthropic-specific code from a review automation system we built for a logistics company. Three weeks. The work itself took maybe two days, the rest was tracking down subtle differences in how we'd structured prompts, handled errors, and shaped JSON responses. We'd convinced ourselves we were "flexible" because we'd abstracted the API calls. We weren't. We were just committed to a provider in ways that didn't show up until we needed to switch.

That experience is why this article hit different for me. Most "AI integration" posts feel like they're written by people who've never actually had to migrate a production system off one model provider to another. This one? It reads like someone's already made that mistake and learned the hard way.

The Real Decision Isn't Technical, It's Operational

Here's the thing that resonated most with me: the author frames this as an operations decision wearing a technical disguise. You're not really choosing between OpenAI and Anthropic based on model quality or API elegance. You're choosing which provider-specific behaviors you're willing to own for the next 18 months.

OpenAI compatibility wins for most teams because it's boring. There's middleware, examples, and a clear path to swap providers later. Your adapter layer stays thin. Anthropic's native API is genuinely good, but if you use it, you need to commit to understanding why their message contract matters for your use case, not just treating it as "another LLM API."

The trap I fell into was pretending I could abstract both equally. I couldn't. Every time Anthropic changed something about structured output or system prompt placement, I'd have to choose: hide the difference in my adapter (which makes portability theater) or accept that my application knows about provider-specific behavior.

Durability Matters More Than Integration Speed

What got my attention was the section on idempotency. In logistics code review, a timeout isn't just annoying, it means the review findings might get written twice, creating race conditions in the queue system. Most chatbot tutorials skip this entirely. They show you a happy path demo and assume your production infrastructure will just... work.

The author's approach is bulletproof: derive a stable review ID from repository path, commit SHA, policy version, and diff digest. Store that ID before calling the model. Make your final database write conditional on that same ID. You never end up with duplicate reviews even if the model API succeeds twice.

I'm doing exactly this now. The retry logic goes into the client (3 attempts, explicit budget), but the idempotency boundary wraps the entire review record. A 429 is retryable. A malformed finding isn't. A timeout is ambiguous, so the worker calls again, but the database write is protected by the review ID, not the API call.

// Stable review ID derived before any external calls
const reviewId = crypto
 .createHash('sha256')
 .update(`${repo}:${commitSha}:${policyVersion}:${diffDigest}`)
 .digest('hex');

// Store the ID first, it anchors the entire operation
await db.reviews.insertIfNotExists({
 id: reviewId,
 status: 'pending',
 createdAt: new Date(),
});

// Call the model with retry budget
let findings = null;
for (let attempt = 0; attempt < 3; attempt++) {
 try {
 findings = await modelProvider.review(diff, policy);
 break;
 } catch (err) {
 if (attempt === 2) throw err;
 await sleep(1000 * (attempt + 1));
 }
}

// Write findings conditional on the same ID
await db.reviews.updateIfNotModified(reviewId, {
 findings,
 status: 'complete',
});

My Take: Test Your Switching Story Before You Need It

I agree with almost everything here, but I'd push one point further: don't just design for portability. Actually test it. Redact some real diffs, run them against your current provider, then swap to a competing provider and run them again. Compare findings. See where they diverge.

That experiment surfaced three behaviors in our system that we thought were generic but weren't: how we formatted code snippets in the prompt, how we weighted severity classifications, and how we handled edge cases in the policy engine. None of those are provider differences, they're application differences we'd baked in while thinking we were provider-agnostic.

I'd also push back slightly on the "OpenAI compatibility for everyone" stance. If you're already embedded in AWS or Google Cloud, using Bedrock or Vertex AI makes sense operationally, even if it adds a layer. Your governance, logging, and cost tracking are already there. Don't fight your platform for the sake of portability theater.

What Would You Do Differently?

I'm curious how you'd handle this if you were building something similar today. Would you start with the portable contract, or would you optimize for the provider you're actually committed to? And if you've already hit the "we need to switch providers" problem in production, I want to hear how painful it was.

The first time you realize your supposedly portable system depends on one provider's quirks is the same time you start thinking like an infrastructure person, not just an API consumer.


Source: This post was inspired by "In-App Chatbot Code Reviews, A Beginner's Portable API Contract" by Dev.to. Read the original article

Written by Adil Sher

Full stack developer building high-traffic platforms, AI services, and custom web applications. Explore my portfolio, learn about my background, or get in touch.

Related Articles

Why I Stopped Treating Dead-Letter Queues as a Trash Bin
Web Development Oct 25

Why I Stopped Treating Dead-Letter Queues as a Trash Bin

Three months ago, I shipped a reservation expiry system that silently swallowed failures. A payment hold stayed open for days past its window because a webhook timed out, nobody logged it, and the retry logic just gave up. The customer's balance looked wrong. My manager asked wha...

When a Vulnerability Sits Hidden for Two Years, We All Have a Problem
Web Development Oct 23

When a Vulnerability Sits Hidden for Two Years, We All Have a Problem

I was debugging a memory leak in one of our microservices last month when a colleague mentioned the PHP security incident casually over coffee. "Yeah, they patched something critical," he said. I nodded and moved on. But then I actually read the details, and something clicked: th...