Problem RestatementProblem
Design the home page of a cloud provider's console, the first page a user sees after login (asked at Microsoft). It shows widgets: recently used resources, a resource count by type, service health in the user's regions, a billing summary, alerts, and recommendations. The data comes from many backend services. The interviewer cared about authentication, fast loading, and behaving well when some services are slow or down.
RequirementsRequirements
- Secure login (SSO, MFA), and only show data the user may see.
- The page is useful in under ~1–2 seconds, even if some widgets load later.
- Handle slow or failing dependencies gracefully (show the rest, and a friendly error per widget).
- Personalization: pinned resources, recent items, chosen subscription or project.
- Must work during partial cloud outages (people open the console exactly then).
ArchitectureArchitecture
%%{init: {"look":"handDrawn","handDrawnSeed":7,"theme":"base","fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","themeVariables":{"fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","fontSize":"16px","primaryColor":"#fff4e6","primaryBorderColor":"#1e1e1e","primaryTextColor":"#1e1e1e","secondaryColor":"#e7f5ff","tertiaryColor":"#ebfbee","lineColor":"#1e1e1e","textColor":"#1e1e1e","mainBkg":"#fff4e6","nodeBorder":"#1e1e1e","clusterBkg":"#f8f9fa","edgeLabelBackground":"#ffffff","classText":"#1e1e1e"}}}%%
flowchart LR
B["Browser - SPA shell from CDN"] --> GW["API Gateway - auth, token validation"]
GW --> BFF["Home BFF - aggregates widgets"]
BFF -->|"parallel, with timeouts"| RS["Resource inventory"]
BFF --> HL["Service health"]
BFF --> BILL["Billing summary"]
BFF --> AL["Alerts"]
BFF --> REC["Recommendations"]
BFF --> PREF[("User prefs - recent, pinned")]
BFF --> CACHE[("Per-user widget cache")]
IDP["Identity provider - SSO/MFA"] --> B- Static app shell (HTML, JS, CSS) from a CDN renders instantly with skeleton placeholders.
- Auth: login via the identity provider (OAuth/OIDC). The browser holds a short-lived access token (or a secure HTTP-only session cookie). The gateway validates it on each call.
- BFF (backend-for-frontend): one endpoint
GET /home(or one per widget) tailored to this page. It fans out to backend services in parallel and returns a combined response.
Deep Dive — A page built from eight servicesDeep dive
The home page shows recent resources, counts by type, service health, billing, alerts and recommendations. Each comes from a different backend, and any of them can be slow.
Call the services one after another
The backend-for-frontend calls each service in turn and renders when everything is in.
%%{init: {"look":"handDrawn","handDrawnSeed":7,"theme":"base","fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","themeVariables":{"fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","fontSize":"16px","primaryColor":"#fff4e6","primaryBorderColor":"#1e1e1e","primaryTextColor":"#1e1e1e","secondaryColor":"#e7f5ff","tertiaryColor":"#ebfbee","lineColor":"#1e1e1e","textColor":"#1e1e1e","mainBkg":"#fff4e6","nodeBorder":"#1e1e1e","clusterBkg":"#f8f9fa","edgeLabelBackground":"#ffffff","classText":"#1e1e1e"}}}%%
flowchart LR
BFF["BFF"] --> S1["Resources - 120 ms"]
S1 --> S2["Health - 200 ms"]
S2 --> S3["Billing - 800 ms"]
S3 --> S4["Recommendations - degraded, 9 s"]
S4 --> RENDER["Render at 10+ seconds"]
S4 --> FAIL["Recommendations down - the whole page 500s"]Latency is the sum of every dependency, and availability is the product of them: eight services at 99.9% each give a page that fails roughly one visit in 125. The least important widget can take down the login landing page.
Call them in parallel
Fan out concurrently and wait for all the responses.
Latency drops from the sum to the maximum, which is a big improvement. Availability is unchanged — waiting for all of them still means any one failure fails the page — and the maximum is set by whichever dependency is having the worst day.
Per-dependency deadlines, partial responses, progressive render
Stop treating the page as one transaction:
%%{init: {"look":"handDrawn","handDrawnSeed":7,"theme":"base","fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","themeVariables":{"fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","fontSize":"16px","primaryColor":"#fff4e6","primaryBorderColor":"#1e1e1e","primaryTextColor":"#1e1e1e","secondaryColor":"#e7f5ff","tertiaryColor":"#ebfbee","lineColor":"#1e1e1e","textColor":"#1e1e1e","mainBkg":"#fff4e6","nodeBorder":"#1e1e1e","clusterBkg":"#f8f9fa","edgeLabelBackground":"#ffffff","classText":"#1e1e1e"}}}%%
flowchart LR
BFF["BFF - fan out with per-dependency deadlines"] --> H["Health - 300 ms"]
BFF --> B["Billing - 800 ms"]
BFF --> R["Recommendations - 400 ms, circuit breaker"]
H --> W1["widget: status ok, data"]
B --> W2["widget: status ok, data"]
R --> W3["widget: status timeout"]
W1 --> PAGE["Page renders what is ready"]
W2 --> PAGE
W3 --> RETRY["'Couldn't load recommendations - retry'"]
PAGE --> STREAM["Critical widgets stream first"]- Each widget returns a status, not just data.
{ status: ok | error | timeout, data }makes a failed widget a rendered state rather than an exception, so one broken dependency costs one card. - A deadline per dependency, sized to what that widget is worth. Health gets 300 ms because it is on the critical path; billing can have 800 ms because it is lower on the page.
- Circuit breakers so a dependency that is failing is skipped quickly instead of consuming its full deadline on every request.
- Progressive delivery — stream widget results as they complete, or let the client call per-widget endpoints — so the page is useful before the slowest widget resolves.
One rule that is easy to get wrong: the BFF must not widen access. It passes the user's identity to each service, and each applies its own permission check. A BFF calling downstream with its own privileged credentials is how a console leaks billing data to someone who may only read resources.
Caching
- Per-user widget cache (a short TTL of 30–120 s): resource counts and billing summaries don't change second to second. Serve from cache and refresh in the background (stale-while-revalidate).
- Shared cache for non-personal data (public service health per region).
- Client cache: keep the last home page data in local storage, so repeat visits paint instantly, then update.
- Recent items and pins: stored per user in a fast KV store, and updated asynchronously when the user opens resources elsewhere in the console.
Reliability During Outages
- The console's own dependencies are spread across regions. The home page must not depend on the region that's down.
- Status/health widget reads from a separate, highly available status system.
- Degrade gracefully: if inventory is down, show pinned and recent items from cache, with a banner.
Frontend PerformanceScale
- Code-split per widget, lazy-load heavy charts, and set a budget (e.g., under 200 KB JS for first paint).
- Measure real-user metrics (time to first meaningful widget), and alert on regressions.
Wrap-UpWrap-up
Serve an instant app shell from a CDN, authenticate with SSO/OIDC tokens validated at the gateway, and use a backend-for-frontend that fetches every widget's data in parallel with per-dependency timeouts, circuit breakers and partial responses. Cache per-user widgets briefly with background refresh, paint repeat visits from the client cache, enforce authorization in each backing service, and keep the page usable during outages by degrading widget by widget.