Building with — rather than against — the LLM provider
Every LLM provider is shipping a new model every few months. The teams that benefit from this cadence treat each release as a tailwind. The teams that get hurt by it built their features against a specific model's quirks and discover, on release day, that the quirks are gone and so is their feature.
The discipline that separates the two is mostly about where you put your contracts.
Put the contract above the model, not below it
The teams that fare best treat the model as an implementation detail of a stable feature contract. The feature has a typed input, a typed output, a quality bar measured by automated evals, and a budget. Behind that contract you can swap models freely.
The teams that fare worst write code that talks to a specific provider's SDK, passes a specific model name as a string, and depends on the specific verbosity, refusal pattern, or formatting quirk of that model. When the model improves — or when you want to try a competitor — the change ripples through every consumer.
The four-layer pattern
A pattern that scales:
- A typed feature interface owned by the product code. "Summarise this support ticket into a one-paragraph triage note." Input shape, output shape, quality contract.
- A prompt template version-controlled and tested against your eval suite. Changing the prompt is a normal pull request.
- A model adapter that knows how to call a specific provider. New providers are new adapters; consumers do not change.
- An eval harness that runs the feature across labeled examples and reports quality, cost, and latency for each model.
With this in place, evaluating a new model is a one-day exercise: point the eval harness at the new adapter, look at the dashboards, decide.
Treat refusals as a configurable feature
Every modern provider has a refusal model that is itself a moving target. Some prompts that worked last quarter no longer work this quarter, and vice versa. The eval suite catches this for you. The harder discipline is treating refusals as a normal output mode rather than an exceptional one — your application code has to have a defined behavior when the model declines.
The result
Teams that adopt this pattern consistently report that each new provider release becomes a small upgrade they can ship in a sprint. Teams that do not find that each release is a fire drill. The difference is design, not engineering effort.
Get the contracts right. Everything else gets easier.