Application Programming Interface Design and Strategy Questions
Covers the design, developer experience, and strategic operating decisions for Application Programming Interfaces and developer platforms. Candidates should demonstrate core design principles such as simplicity, consistency, discoverability, clear naming and conventions, intuitive resource modeling, robust error handling, stability, backward compatibility, and explicit versioning strategies. They should understand trade offs among interface paradigms including Representational State Transfer style APIs, Graph Query Language approaches, and remote procedure call frameworks such as gRPC, and how those choices affect discoverability, latency, schema evolution, client ergonomics, testing, and mocking. The topic also includes the developer facing surface area beyond the interface itself: documentation, quickstart guides, sample code, software development kits, command line tools, interactive explorers, sandbox environments, and other onboarding artifacts that reduce friction. Candidates should be able to identify common friction points such as unclear documentation, complex setup and authentication flows, unhelpful error messages, inconsistent or surprising behaviors, slow feedback loops, and endpoints that are hard to mock or test, and propose concrete engineering and process solutions. Measurement and optimization expectations include onboarding and adoption metrics such as time to first successful call, time to first meaningful result, onboarding success rates, developer satisfaction and sentiment, adoption and churn, support and integration costs, error rates and latency, and how to instrument and monitor the developer journey. Engineering practices to discuss include stable contract design, semantic versioning and compatibility guarantees, schema and contract testing, clear deprecation policies, monitoring and observability for developer journeys, automated client generation and migration tooling, authentication and rate limiting strategies, webhook and event mechanisms, and monetization or partnership models for platform growth. Senior candidates should connect technical and experience decisions to product and business outcomes, explaining how design choices drive adoption, reduce support load, enable ecosystem growth, and preserve long term platform velocity, and should provide concrete examples of improvements implemented or proposed and how their impact was measured.
MediumSystem Design
59 practiced
Design a rate-limiting strategy for a public API with differentiated plans (free: 60 req/min, paid: 600 req/min) and an expected scale of 100M monthly active users. Discuss per-user vs per-API-key limits, short-term burst handling, fair-sharing across tenants, enforcement across distributed gateway instances, and how to surface limits to clients.
Sample Answer
Requirements & constraints- Two plans: free=60 req/min, paid=600 req/min.- Scale: 100M MAU (peak concurrent callers could be millions).- Goals: per-tenant fairness, short bursts allowed, low-latency enforcement across many gateway instances.High-level approach- Use a hybrid, distributed rate limiter: per-API-key (tenant) primary limits, with optional per-user (user-id) if tenant exposes many end-users. Enforce plan quotas on API key; use per-user limits to prevent a single user from exhausting a tenant’s allotment.Algorithm and burst handling- Token Bucket (leaky allowance + refill) per key for burst support; refill rate = plan_rate/60 tokens per second, bucket capacity = refill_rate * burst_window. For example allow a 5s burst: capacity = (60/60)*5 = 5 tokens for free, (600/60)*5 = 50 tokens for paid. This provides controlled bursts while preserving average rate.Fair-sharing across tenants- Limits are per-key; to ensure fairness across a tenant’s internal users, allocate each tenant a share and optionally apply a secondary per-user token bucket with a fraction of tenant capacity or use Weighted Fair Queuing at gateway for concurrent requests coming from same tenant.Enforcement across distributed gateways- Hybrid enforcement: 1. Fast-path local token bucket in each gateway instance using a small local cache (e.g., in-memory or local Redis) for milliseconds latency. 2. Global correctness via a central, highly-available store (Redis Cluster/Clustered Cassandra) holding authoritative counters and refill metadata. 3. Use Redis LUA scripts for atomic decrement/check when strong consistency required. For performance, gateway tries local token consumption; when local tokens exhausted or sync window reached, fall back to central check.- Sharding keys by hash (API key) across Redis shards to scale to 100M users. Add circuit-breaker if central store becomes slow (fail-open/closed policy depending on safety).Sliding-window alternative- If evenly distributed smoothing required, implement sliding-window log or approximate sliding window using fixed window with multiple sub-windows (sliding window counter) to avoid bursts at boundary.Data modeling & scale- Store per-key: plan_id, refill_rate, bucket_capacity, last_refill_ts, tokens. Use TTL to garbage-collect inactive keys. Estimate storage: 100M keys × ~100B = ~10GB; shard across cluster.Client surface & UX- On each response include standard headers: - RateLimit-Limit: plan limit (per minute) - RateLimit-Remaining: tokens left - RateLimit-Reset: seconds until refill - Retry-After on 429- Return 429 with JSON body explaining plan, how to upgrade, and time until retry. Provide dashboard / API for clients to query current usage and webhook alerts near limits.Monitoring & metrics- Emit metrics: rejects/sec, avg latency added by limiter, token consumption per plan, hot keys. Alert on increased 429s, Redis latency, shard hot-spots.Trade-offs- Local caches reduce latency but risk slight over-issuing; acceptable when bounded by conservative local capacity. Strict global enforcement costs latency and throughput but provides exact limits. Choice depends on business tolerance for occasional overuse.Operational considerations- Rate limit rules versioned and deployable via config service; support burst tuning, per-endpoint overrides, and quota windows (daily/monthly) for billing integration. Test with load tests and chaos engineering focused on Redis failover.
HardSystem Design
56 practiced
Design offline-first client SDK behavior for mobile applications consuming your APIs. Cover local caching strategies, optimistic updates, conflict resolution approaches (LWW, CRDTs, operational transforms), schema migration for local stores, incremental sync endpoints, and server-side support needed to reconcile and replay client changes.
Sample Answer
Requirements & constraints:- Mobile SDK must work offline, give responsive UX, guarantee eventual consistency, minimize data loss, support large datasets, handle schema evolution, and operate with intermittent connectivity and mobile resource limits.High-level architecture:- Client: local store (SQLite/Realm/LevelDB), write-ahead operation log (op-log), sync engine, conflict resolver, background sync scheduler, public SDK API (get/read/subscribe).- Server: incremental sync endpoints (pull/changes & push/accept), reconciliation service, operation store, transform/CRDT merge logic, versioning, idempotency and audit logs.Local caching strategies:- Primary store: a normalized local DB (SQLite/Realm) with lightweight indices. Use TTLs and LRU for large caches.- Two-layer cache: materialized views for UI (fast reads) + canonical local data for persistence.- Keep a compact write-ahead op-log recording intents: {opId, entityType, entityId, clientVersion, baseVersion, mutation, timestamp}.Optimistic updates:- Apply updates immediately to UI and local store, append op to op-log, mark rows as “pending”.- Expose hooks to rollback or reconcile after server ack.- Assign client-generated stable IDs (UUIDs) and monotonic client-version per entity to aid reconciliation.Conflict resolution approaches:- LWW (Last-Write-Wins): simple—store (lastWriteTimestamp, writerId). Best for low-conflict fields (e.g., lastSeen). Pros: simple, low overhead. Cons: data loss, clock dependency—use logical clocks if possible.- Version vectors / Vector clocks: detect concurrent updates. Use for small set of writers and when detectability matters.- CRDTs: use for replicated counters, sets, maps where merges should be commutative/associative/idempotent (e.g., presence, counters, collaborative lists with RGA/WOOT). Pros: no central coordinator; Cons: complexity, payload size.- Operational Transforms (OT): for rich collaborative editing where intent must be preserved. Requires centralized sequencing or transformation server; complex to implement on mobile.Recommendation: default to hybrid: - Per-field resolution policy: metadata-driven schema declares resolution (LWW/logical-timestamp, merge, CRDT, custom server-side hook). - Use CRDTs for high-collaboration objects; use vector clocks to detect conflicts for domain objects and surface merges to user or server-side business rules.Schema migration for local stores:- Versioned schema with migration scripts in SDK. On SDK init, compare local schema version → run deterministic migrations (DDL + data transforms) within transaction; keep backward-compatible migrations where possible.- Keep op-log compatible: include operation schema version; provide op-log migration pipeline to rewrite old ops when applying or during background compaction.- Provide a safe fallback: if migration fails, export data for diagnostics and optionally rebuild from server via full sync.Incremental sync endpoints & protocol:- Pull endpoint: GET /sync/changes?since=checkpoint -> returns changes (entities + metadata: version, opId, tombstone) and latest checkpoint token.- Push endpoint: POST /sync/push -> accept batch of client ops; respond with per-op result (accepted/merged/conflict/need-retry) and server-assigned versions.- Checkpointing: use server-issued monotonically increasing sync tokens (logical clock / ledger index) rather than wall-clock.- Support pagination, compression, and delta-encoding (send only changed fields).- Provide snapshots/full download for initial bootstrap or recovery.Server-side support to reconcile & replay:- Operation ingestion pipeline: validate ops, deduplicate by opId, enforce idempotency, compute server version, apply merge strategy per-entity/field.- Store authoritative versions and append-only change log (event-sourcing style) to enable replay for new clients or auditing.- Conflict detection: compare client baseVersion vs server version; if server changed, apply merge policy: - Automatic merge (CRDT/merge functions) -> produce new version. - Deterministic server-side transform (e.g., merging list operations). - If ambiguous, mark as conflict and expose resolution API to client or escalate to business logic service for server-side resolution (notify user, offer merge UI).- Provide a replay endpoint and snapshotting (GET /sync/snapshot?token=) to rebuild client state and to reconcile long-offline clients.- Expose webhooks/events for downstream systems when conflicts require manual resolution.Reliability, security & operational concerns:- Ensure idempotency: client includes opId + clientId; server stores processed opIds with TTL.- Authentication & authorization per-op; enforce field-level write rules server-side.- Network & battery: exponential backoff, batching, background sync windows, opportunistic on Wi‑Fi, make sync resumable.- Telemetry & observability: expose metrics on conflicts, sync durations, op failure rates; provide SDK hooks to log sync problems.- Data privacy: encrypt sensitive local data (device keystore), secure transport (TLS), server-side encryption.Developer ergonomics:- Declarative model schema with per-field resolution policies; migration APIs; clear SDK callbacks for conflict/resolution UI; ability to register custom merge handlers.- Testing: provide a sync simulator and unit tests for merge policies; CI checks for migration scripts.Example flow:1. User edits entity E offline -> SDK writes to local DB, app sees change, append op O1 (clientV=5).2. Later, device syncs: POST /sync/push O1. Server sees serverVersion=6 (different) -> uses vector clock detect concurrency.3. If policy LWW and client timestamp > server -> server accepts; else server applies merge (CRDT or custom), responds with new serverVersion=7.4. SDK reconciles local state with server response: update local row, mark op as applied or resolve conflict UI if server flagged.Trade-offs:- LWW simplicity vs CRDT correctness/size; choose based on domain: prefer CRDTs only for data actually needing commutative merges.- Client complexity vs server authority: pushing more logic to server simplifies SDK but increases server load and latency; hybrid approach balances responsiveness and correctness.This design gives an SDK that is fast and responsive offline, supports deterministic reconciliation, and provides extensibility for domain-specific conflict resolution while maintaining operational safety (idempotency, migrations, telemetry).
EasyTechnical
60 practiced
Explain the core principles of RESTful API design and how they map to HTTP semantics. Give concrete examples of good vs poor resource URIs (e.g., /users/123/orders vs /getUserOrders?id=123), explain when to use query parameters versus path segments, and clarify where verbs belong (HTTP methods vs path). Also describe idempotency expectations for GET, PUT, POST, and DELETE.
Sample Answer
Core principles of RESTful API design mapped to HTTP semantics:- Resource orientation: model nouns (resources) not actions. Use URLs to identify resources (e.g., /users/123, /users/123/orders).- Uniform interface: standard HTTP methods with well-defined semantics (GET, POST, PUT, PATCH, DELETE).- Statelessness: each request contains all info; server doesn't keep client session state.- Representations: resources returned in JSON, XML, etc., with content-type.- Hypermedia (HATEOAS) where practical.Good vs poor URIs:- Good: /users/123/orders — clear resource hierarchy, cacheable, human-readable.- Poor: /getUserOrders?id=123 or /ordersForUser?userId=123 — verbs in path and RPC-like patterns break REST intent.Query parameters vs path segments:- Path segments for resource identity/hierarchy: /users/{id}/orders/{orderId}.- Query params for filtering, sorting, pagination, optional modifiers: /orders?status=shipped&page=2&limit=20.Where verbs belong:- Use HTTP methods for verbs: - GET /users/123 — retrieve - POST /users — create - PUT /users/123 — replace - PATCH /users/123 — partial update - DELETE /users/123 — delete- Avoid action verbs in path (e.g., /users/123/delete).Idempotency expectations:- GET: safe, idempotent, and cacheable — no side effects.- PUT: idempotent — repeated identical PUTs yield same state (replace resource).- DELETE: idempotent — deleting same resource multiple times results in resource absent (second call may 404).- POST: not idempotent — used for create/processing; repeated POSTs can create duplicates unless server enforces idempotency keys.Extras / best practices:- Use proper status codes (200, 201, 204, 400, 404, 409, 500).- Support content negotiation and pagination for large collections.- Document contracts (OpenAPI) and consider idempotency keys for safe retries.
HardSystem Design
53 practiced
Design a robust webhook retry, deduplication, and replay system for delivering events to unreliable partner endpoints. Cover detection of duplicates, idempotency practices for subscribers, retry/backoff policies, dead-letter queues, replay capabilities for recovery, and how to expose delivery insights and replay APIs to partners.
Sample Answer
Requirements:- Reliable at-least-once delivery to partner endpoints that can be flaky- Duplicate detection and idempotency support- Configurable retry/backoff, DLQ, and replay for recovery- Observability and partner-facing APIs for delivery status and replayHigh-level architecture:Event Producer → Ingest + Validate → Durable Event Store (append-only, immutable) → Delivery Worker Pool → Partner EndpointSupporting: Retry Scheduler, Dedup Index, Dead-Letter Store, Replay API, Dashboard/TelemetryKey components:1. Durable Event Store: write-ahead log (e.g., Kafka or cloud storage) containing event_id (UUID), source_id, sequence, payload, timestamp, signature.2. Delivery Worker: reads events, computes delivery attempts, sends HTTP POST with Idempotency-Key: event_id and signature header. Records attempt metadata to Delivery Log (DB).3. Retry Scheduler: schedules retries using exponential backoff with jitter; persists next_attempt in DB; uses rate- and concurrency-limits per partner.4. Dedup Index & Receiver-side guidance: keep a per-partner time-windowed dedup store (Redis with TTL) mapping event_id → delivered_at. Delivery Worker checks before sending to avoid concurrent duplicate sends.5. Dead-Letter Queue: after N attempts / max time or 5xx persists to DLQ (store event + last error) and notifies partner.6. Replay API: partners can request replay by event_id, time-range, or sequence; system will re-enqueue events with replay flag and preserve original event_id.7. Observability & APIs: expose partner-facing endpoints and dashboard showing per-event status, attempt history, latency, failure reasons, current queue lengths, and DLQ contents. Webhooks/callbacks for DLQ notification.Duplicate detection & idempotency:- Primary detection: canonical event_id included in request and Idempotency-Key header. Partner must store recent event_ids and return 2xx for duplicates.- Secondary: signature (HMAC) to validate authenticity; sequence numbers for ordering.- If partner is non-idempotent, provide "at-most-once" opt-in where system waits for explicit ack before retrying (slower).Retry/backoff & throttling:- Default: exponential backoff (base 2s), cap 1hr, jitter (±20%).- Policy configurable per partner (max attempts, max total retry window).- Respect HTTP status semantics: 2xx success, 3xx follow Location, 4xx permanent failure (no retry except 429 or 409 with guidance), 5xx/transient → retry.- Honor Retry-After header from partner to schedule next attempt.Dead-letter & recovery:- Move to DLQ after policy exhausted. DLQ stores full context, diagnostics, and suggestion tags (auth, TLS, 4xx, schema error).- Provide partner-facing DLQ access + ability to edit payload/endpoint and request replay.Replay behavior:- Replay preserves original event_id and timestamps, marks attempts as replay. Optionally supports batch replay with rate limits and windowed exponential backoff to avoid spikes.- Provide "replay dry-run" to validate acceptance without marking delivered.Security & consistency:- HMAC signatures, TLS, mutual TLS for sensitive partners.- Rate limiting and circuit-breaker per partner to avoid cascading failures.- Idempotency store size: TTL tuned to business SLAs; store in Redis with eviction and fallback to DB.Telemetry & partner APIs:- APIs: GET /events?status=failed, POST /replay, GET /events/{id}/attempts, PUT /partner/config- Dashboard: per-event timeline, latency histograms, success rate, current backoff, last error message.- Alerts: email/SMS/Slack for persistent failures or DLQ growth.Trade-offs:- At-least-once simplifies reliability but requires partner idempotency; at-most-once avoids duplicates but is slower and less resilient.- Long dedup TTL increases storage but reduces duplicates; short TTL lowers memory but may re-deliver duplicates.This design prioritizes durability, clear idempotency contract, configurable retry policies, safe replay, and strong observability so partners can diagnose and recover quickly.
MediumTechnical
58 practiced
Design an API caching strategy for read-heavy endpoints such as a product catalog. Discuss client-side caching (Cache-Control), CDN edge caching, origin caching, conditional requests with ETag/If-None-Match, and cache invalidation strategies when products are updated (event-driven invalidation, TTLs, and stale-while-revalidate).
Sample Answer
Requirements / goals:- Fast reads, low origin load, reasonably fresh product data (e.g., inventory/price updates must propagate within seconds–minutes depending on business).- Support many clients and geographic distribution.Client-side caching (Cache-Control)- Set Cache-Control: public, max-age=<short-to-medium>, must-revalidate/immutable where appropriate.- Example: static product metadata (name, description, images): max-age=86400, immutable. Price/stock: max-age=30–300 depending on business SLA.- Use Vary headers only when necessary (e.g., Accept-Language).CDN edge caching- Let CDN cache responses with Cache-Control and/or explicit CDN TTL. Use long TTLs for stable assets and short TTLs for volatile fields.- Configure CDN to respect origin headers or override for key endpoints.- Use cache key normalization (strip irrelevant query params, include headers like Authorization only when needed).Origin caching & conditional requests- Support ETag and Last-Modified on origin. On 304 responses CDN or clients can avoid downloading bodies.- Use strong ETags when response body deterministic. For partial fields that change frequently, consider separate endpoints to increase cacheability (e.g., /product/{id}/metadata vs /product/{id}/price).Cache invalidation strategies- Event-driven invalidation: on product update (price/stock change, publish/unpublish) publish an event to CDN/orchestrator to purge or soft-purge specific cache keys. Targeted invalidation is preferred to avoid global flushes.- TTLs: combine short TTLs for volatile data with longer TTLs for stable data to reduce invalidation load.- Stale-while-revalidate / stale-if-error: return stale cached data while asynchronously revalidating to origin to keep latency low and avoid spikes. Configure short s-w-r for volatile endpoints (e.g., stale-while-revalidate=5s–60s).- Graceful degradation: if origin unreachable, serve stale for limited time (stale-if-error).Trade-offs and recommendations- Prefer splitting endpoints by volatility so most reads are long-cached.- Use event-driven targeted purges for critical updates and rely on TTL+stale-while-revalidate to smooth traffic.- Monitor cache hit ratios, purge rates, and propagation latency; add observability (metrics, traces) to ensure SLAs.- Secure cache keys for per-user responses (Authorization -> do not cache publicly; use signed URLs or CDN edge auth).This approach balances freshness, latency, and origin load while keeping invalidation complexity manageable.
Unlock Full Question Bank
Get access to hundreds of Application Programming Interface Design and Strategy interview questions and detailed answers.