Prompt and scope
The bootstrap endpoint orchestrates three downstream calls. Profile and permissions are required for a correct shell; the feed may be stale or absent. The endpoint must return a typed response that lets the client render safe sections independently. The 300 ms p95 budget includes orchestration overhead, and the design should state which data may be cached and for how long.
What the interviewer is testing
- Deriving parallelism, timeout budgets, and failure semantics from user-visible requirements.
- Distinguishing missing data, an empty result, and a dependency error.
- Composing retries, circuit breakers, bulkheads, and cache policy without multiplying load.
- Designing an evolvable response contract and useful operational signals.
Clarifying questions to ask
Ask whether permissions can be stale, whether feed data has a freshness limit, whether the client can render progressively, and whether calls share a tenant or authorization context. If permissions are never allowed to be stale, they stay on the critical path; if the feed has a five-minute freshness target, a stale-while-revalidate cache can protect latency.
The 30-second answer
I would make the gateway authenticate once, fan out profile, permissions, and feed calls in parallel, and reserve a deadline for assembly. Required sections fail closed; optional sections return an explicit unavailable state with a reason code and freshness timestamp. Retries are limited to transient, idempotent calls and consume a shared budget. Per-dependency timeouts, bulkheads, circuit breakers, stale cache, and a typed problem-details envelope prevent one outage from blanking the shell. Metrics track section-level success, deadline exhaustion, stale serves, and dependency saturation.
Step-by-step deep dive
1. Set the time and concurrency budget
At 2,000 requests per second, three sequential calls waste the 300 ms budget. Fan out concurrently, give each dependency a deadline below the overall deadline, and keep assembly plus serialization inside the remainder. Use bounded connection pools and a per-request cancellation signal so a timed-out dependency stops consuming work.
2. Define partial-response semantics
Return a stable envelope with a status for each section: ready, stale, or unavailable. Include a machine-readable reason and data timestamp, but do not leak internal hostnames. Profile or permissions errors should fail closed or return a login/action state; a feed timeout can leave the shell usable. An error object following RFC 9457 can describe request-wide failures without pretending that every section failed.
3. Protect dependencies from retries and outages
Retry only transient failures, only for idempotent reads, and only once within a shared deadline. Add jitter and stop retries when the circuit is open. Bulkheads cap concurrent calls per dependency; a stale cache or a bounded default handles optional feed data. A circuit breaker is useful when repeated downstream failures would otherwise consume all gateway capacity, but it does not replace timeouts or a recovery probe.
4. Evolve and observe the contract
Version fields additively, let clients ignore unknown sections, and include a request correlation identifier. Record dependency latency, timeout cause, circuit state, cache age, section status, and payload size. Trace the fan-out tree, sample slow requests, and alert on required-section failure rate, stale age, and retry volume. Contract tests must cover mixed outcomes such as permissions ready, profile stale, and feed unavailable.
A strong sample answer
I would clarify freshness and whether progressive rendering is allowed. The gateway authenticates once, fans out three reads, and assigns per-call deadlines inside a 300 ms overall budget. It returns section-level ready, stale, or unavailable states. Required identity and permissions fail closed; feed data may come from a bounded stale cache. One jittered retry is allowed only for transient idempotent reads, protected by a dependency bulkhead and circuit breaker. Metrics and traces expose section failures and deadline pressure, while additive versioning keeps older clients working.
Common mistakes
- Call dependencies sequentially → latency adds up past the budget → fan out with bounded pools and deadlines.
- Return HTTP 200 with ambiguous nulls → the client cannot tell empty data from failure → use explicit section status and reason codes.
- Retry every error in every layer → one outage becomes a retry storm → classify errors and enforce one shared retry budget.
- Cache permissions without a policy → access can outlive its authorization → define a freshness bound or fail closed.
- Use one global circuit → a feed outage blocks identity data → isolate breakers and bulkheads by dependency.
- Log only total latency → optional and required failures are indistinguishable → emit section-level metrics and traces.
Follow-up questions and responses
The feed is slow but the user needs the shell immediately. What changes?
Shorten the feed deadline, serve a bounded stale result when its age is acceptable, and return unavailable otherwise. Keep the shell response independent so the client can load the feed later.
Permissions data is stale in one region. Can you serve it?
Only if the authorization policy explicitly permits that staleness and the response communicates its age. For sensitive actions, recheck against an authoritative source and fail closed on uncertainty.
How do you prevent a retry from extending past 300 ms?
Pass one absolute deadline through the fan-out context. Before each retry, reserve time for the attempt and assembly; if none remains, return the section’s timeout state instead of starting work that cannot finish.
A dependency recovers after the circuit opens. How is traffic restored?
After a cool-down, send a small number of half-open probes. Close the circuit only after successful probes meet the same timeout and error criteria; otherwise keep it open with an observable next-probe time.