Lesson 30 of 30
Everything else in this track (caching, model choice, observability, BYO keys) is the groundwork — this lesson is what it takes to actually put an AI feature in front of real users and keep it standing when a provider has a bad day.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
Question 1 of 3
An AI feature's provider call fails with a timeout. Which response best matches graceful degradation as described in this lesson?
Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
An AI call is a network request to a system you don't control, that can be slow, wrong, rate-limited, or briefly down, on a schedule you have no visibility into. Shipping an AI feature to production means designing for that reality up front, not discovering it the first time a provider has an outage. Three patterns do the real work:
None of these are exotic. They're the same defensive-engineering instincts used everywhere else in a production system — timeouts, retries, circuit breakers, kill switches — applied specifically to the AI call site, because that call site fails in its own particular ways (rate limits, quota exhaustion, occasional bad output) that a generic API client doesn't usually have to think about.
// lib/ai/chat/handler.ts
export async function handleChat(messages, deps) {
const primary = resolveProviderConfig(deps); // platform key -> user key -> NeedsUserKeyError
try {
return await make(primary).chat({ messages });
} catch (err) {
if (err instanceof QuotaExhaustedError) {
const user = deps.userConfig();
if (user && user.apiKey !== primary.apiKey) {
return await make(user).chat({ messages }); // fall back and retry
}
}
throw err; // caller surfaces a real, actionable error state
}
}
This one function is the whole track in miniature: resolveProviderConfig (the BYO-key lesson) picks a config; the actual call goes through a single shared entry point rather than each call site touching a provider directly (the model-choice lesson's abstraction-layer point); a live quota failure triggers a real fallback retry instead of surfacing a broken UI (this lesson's degradation pattern); and every call site that wants observability (the observability lesson) has exactly one place to add it. None of these pieces is sufficient alone — the fallback is only useful because there's a real config to fall back to, and the whole thing is only debuggable in production because it's logged.
"Walk me through how you'd make an AI feature production-ready, not just a working demo" is a real, common closing question for AI-feature work, and a strong answer ties together cost control (caching, model choice), debuggability (observability), and resilience (degradation, flags, provider fallback) as one coherent system — not a list of unrelated nice-to-haves.