Application Programming Interface Design and Integration Questions
Designing Application Programming Interfaces and selecting communication protocols to meet functional and non functional requirements. Candidates should be able to evaluate and choose between Representational State Transfer style resource oriented interfaces, Graph Query Language approaches, remote procedure call frameworks such as Google Remote Procedure Call, and message based or event driven integration patterns. Discussion should cover protocol and format trade offs including latency, throughput, consistency and ordering, binary versus text serialization formats such as protocol buffers or JavaScript Object Notation, developer ergonomics, client diversity, and resource consumption. Core design topics include contract design and schema evolution, versioning and backward compatibility strategies, pagination, filtering, sorting and error handling conventions, authentication and authorization models, rate limiting and quota strategies, caching choices, and gateway or proxy patterns. Integration concerns include direct synchronous calls, asynchronous message based decoupling, event streaming, and web hooks, plus client and server software development kits and data transformation between layers. Candidates should also explain resilience and reliability patterns such as timeouts, retries, circuit breaker and bulkhead techniques, and describe testing, monitoring and observability approaches including logging, metrics and distributed tracing. Finally, discussion should connect API and protocol choices to coupling, scalability, operational complexity, security posture, and developer productivity.
MediumSystem Design
44 practiced
Describe the responsibilities you would place in an API gateway and which you would keep in downstream services. Consider authentication, rate limiting, routing, request/response transformation, circuit breakers, caching, observability, and input validation. Explain trade-offs of centralizing cross-cutting concerns in the gateway vs pushing them to services.
Sample Answer
Requirements & constraints:- Single entrypoint for clients, heterogeneous downstream microservices, need security, scalability, low latency, and team autonomy.- Assume trusted internal network between gateway and services.High-level split (what I put in API Gateway vs downstream services)API Gateway (centralized cross-cutting concerns)- Authentication & authorization (token validation, OAuth/OIDC, JWT verification): validate tokens at edge to reject unauthorized requests early and reduce load on services. For fine-grained RBAC, pass claims to services.- Routing & load balancing: path/host-based routing, versioning, A/B or canary routing.- Rate limiting & throttling (per-client / per-api): protect system from abusive clients; often implemented in gateway for consistent global quotas.- Request/response transformation: protocol translation (e.g., HTTP↔gRPC), aggregation (fan-out), header enrichment for tracing; keeps clients simple.- Caching (edge cache for idempotent GETs): reduce latency and downstream load for highly cacheable responses.- Observability hooks: centralized metrics, logging, distributed tracing headers injection.- Basic input validation & size limits: guardrails to block obviously malformed or huge payloads.Downstream Services (decentralized, domain-aware responsibilities)- Business input validation & domain invariants: deep validation that requires domain knowledge (e.g., user-specific rules).- Fine-grained authorization & ownership checks: enforce data-level permissions and business rules.- Circuit breakers & retry policies: service-local circuit breakers to manage dependency failures and apply domain-specific backoff semantics. Gateway can have coarse circuit handling but services need local resilience.- Service-level rate limiting / quotas: per-tenant or per-user quotas tied to business logic.- Caching of domain data and materialized views: service knows data semantics and eviction strategies.- Detailed observability and audit logs: business events and sensitive traces kept at service level.Trade-offs (centralize vs push to services)- Consistency vs autonomy: Centralizing (gateway) gives uniform behavior, easier ops and single policy enforcement, but becomes a choke point and reduces service autonomy. Pushing to services increases flexibility and domain correctness but risks inconsistent policies and duplication.- Latency & throughput: Gateway-level auth/rate-limiting/caching reduces downstream latency/traffic. However complex transformations or aggregation at gateway can add latency and CPU—consider async or dedicated edge services.- Security: Gateway-first authentication reduces attack surface. Sensitive authorization checks must remain in services to avoid privilege escalation risks.- Reliability & blast radius: Gateway failure impacts all traffic—use HA, autoscaling, and avoid heavyweight logic in gateway. Distribute resilience (circuit breakers) to services to localize failures.- Observability: Centralized metrics simplify system-wide views; service-level telemetry is required for root-cause and domain insights.Practical recommendations- Keep gateway thin for auth/token validation, routing, global rate limits, caching, and protocol translation.- Delegate domain validation, business auth, local rate-limits, and circuit breakers to services.- Use standardized tokens/claims, tracing headers, and shared libraries to reduce duplication.- Monitor gateway performance closely and scale it independently; version gateway policies via config so teams can evolve without code changes.
EasyTechnical
38 practiced
Explain token-bucket and leaky-bucket rate limiting algorithms. For a public API that permits 100 requests/minute per API key and allows short bursts, recommend which algorithm fits best and why. Also state what response headers you would return to clients to communicate quota usage and how you'd handle clients exceeding limits.
Sample Answer
Token Bucket vs Leaky Bucket (short):- Token Bucket: Tokens accumulate at a fixed rate into a bucket up to a max capacity. Each request consumes a token; if tokens available, request passes immediately. Allows bursts up to bucket capacity because tokens can be saved before a burst.- Leaky Bucket: Treats requests as water flowing into a fixed-capacity queue; they are served out at a constant drain rate. If the queue is full, incoming requests are dropped or delayed. Enforces a steady output rate and smooths bursts (doesn't allow large bursts).Recommendation for public API (100 requests/minute per API key, short bursts allowed):- Use Token Bucket. It enforces an average rate (100/minute) while permitting short bursts up to the bucket capacity (choose capacity based on acceptable burst, e.g., 20 tokens = allow 20 extra requests immediately). It’s simple to implement (atomic token decrement + refill by time delta) and matches the requirement to allow bursts while keeping long-term rate.Response headers to communicate quota:- X-RateLimit-Limit: 100- X-RateLimit-Remaining: number of tokens left- X-RateLimit-Reset: epoch seconds when quota fully refilled (or time until next token)- Retry-After: seconds to wait when limit exceeded (HTTP 429 responses)Handling clients exceeding limits:- Return HTTP 429 Too Many Requests with Retry-After and the above rate headers. Prefer a short backoff policy and soft throttling if helpful (e.g., allow a small queue with delayed responses for some clients). Log and optionally apply progressive penalties for repeat offenders (longer blocks, API key suspension). Ensure headers are computed atomically and consider using a distributed store (Redis with Lua scripts) for correctness across instances.
HardTechnical
48 practiced
Design an event streaming pipeline using Kafka (or equivalent) that guarantees ordering where required and provides at-least-once or exactly-once processing semantics for critical financial events. Discuss partitioning strategy, use of producer idempotence, Kafka transactions, consumer offsets, and the practical trade-offs and pitfalls of implementing exactly-once end-to-end.
Sample Answer
Requirements & constraints:- Guarantee ordering where required (per-entity ordering, e.g., account id).- Provide at-least-once or exactly-once processing for critical financial events.- Low-latency, high-throughput, operationally maintainable.High-level architecture:- Producers -> Kafka topics (partitioned by key) -> stream processors (Kafka Streams/Flink) -> sinks (databases, downstream topics).- Use separate topics for critical financial events to apply strict guarantees.Partitioning strategy:- Choose key = entity requiring ordering (account_id, transaction_id family). Use topic-partitions such that all events for one key map to the same partition; Kafka guarantees partition-local ordering.- Right-size partitions for throughput while keeping per-key hot-spotting in mind (use hashing + sticky partitioners or partitioning schemes that include tenant shard to avoid single hot partition).Producer idempotence:- Enable producer.idempotence = true so retries don’t create duplicates at broker level. This ensures at-most-once-to-broker duplication avoidance for a single producer session (sequence numbers + producerId).Kafka transactions & exactly-once:- To get end-to-end exactly-once from producer -> Kafka -> consumer-sink, use Kafka Transactions: - Producers use transactions to atomically write to multiple partitions/topics and mark offsets in the consumer group via sendOffsetsToTransaction. Stream processors (Kafka Streams) use the EOS modes. - Consumers must be transactional-aware: read-process-write in one transaction, commit consumer offsets into the same transaction so either both writes and offset commit succeed or neither do.- This yields Kafka-level exactly-once semantics (EOS) but only within the Kafka ecosystem and transactional sinks (Kafka sinks). External systems (databases) must support idempotent writes or transactions coordinated with Kafka (e.g., two-phase commit, CDC with idempotence keys).Consumer offsets:- Use transactional offset commits (sendOffsetsToTransaction) to couple offset commit with output writes. For non-transactional consumers, rely on at-least-once with idempotent downstream handling.Practical trade-offs & pitfalls:- Exactly-once is bounded: Kafka EOS guarantees exactly-once semantics between Kafka topics and within Kafka clients; achieving true end-to-end exactly-once into arbitrary external systems is hard and often impractical.- Performance hit: transactions increase latency and reduce throughput; EOS requires more broker state and may use additional disk IO.- Operational complexity: more careful producer lifecycle management (no reusing producer.transactional.id across incompatible runs), handling transaction timeouts, long transactions causing log retention/GC issues.- Ordering vs parallelism: strict per-key ordering reduces parallelism; hot keys become throughput bottlenecks—consider sharding keys or batching to amortize cost.- Idempotence limits: producer idempotence protects duplicates from retries, but consumer-side retries or multi-producer writes for same key still require idempotent processing downstream (use unique event IDs and dedupe store).- Failure modes: network partitions, broker restarts, and unclean leader elections can complicate guarantees; ensure appropriate broker configs (min.insync.replicas, acks=all).Recommendations:- Use partition-by-key for ordering, enable idempotent producers, use transactions (Kafka Streams or producer transactions + sendOffsetsToTransaction) when writing back to Kafka.- For external sinks, implement idempotent writes (unique event IDs), or use a transactional connector (e.g., exactly-once sink connectors, or consume from Kafka topic produced transactionally).- Monitor latency, transaction metrics, and partition hotness; begin with at-least-once + strong idempotence patterns, move to EOS when business requires strict financial correctness and you accept operational cost.
MediumTechnical
40 practiced
You're choosing internal service-to-service communication for latency-sensitive microservices inside a data center. Compare gRPC (with protobuf over HTTP/2) and REST (JSON over HTTP/1.1) across latency, throughput, streaming support, developer ergonomics, client diversity, observability, and deployment complexity. Which would you choose and why?
Sample Answer
Short answer: For latency-sensitive, internal service-to-service calls inside a data center I would choose gRPC (protobuf over HTTP/2). It generally gives lower latency, better throughput, native streaming, and strong typing—trade-offs are manageable for internal ecosystems.Comparison:- Latency: gRPC uses HTTP/2 multiplexing, binary protobufs and smaller payloads → lower serialization + network overhead. REST/JSON on HTTP/1.1 has higher parsing cost and larger payloads, and head-of-line blocking on single connections.- Throughput: gRPC’s compact binary format and multiplexed streams maximize throughput; lower CPU for (de)serialization means higher requests/sec.- Streaming support: gRPC has first-class unary, server-, client-, and bidi-streaming. REST needs workarounds (chunked, WebSockets) that are less standardized.- Developer ergonomics: gRPC requires proto definitions and generates stubs—good for strongly-typed teams but adds a build step. REST is simpler and flexible (no codegen), easier for quick prototyping.- Client diversity: REST/JSON wins if many polyglot clients or non-engineered consumers (browsers). gRPC supports many languages but browser support needs gRPC-Web or proxies.- Observability: REST is simple to introspect (textual), integrates with existing HTTP tooling. gRPC needs HTTP/2-aware tracing/logging and mapping of status codes, but mature ecosystems (OpenTelemetry) support it.- Deployment complexity: gRPC requires HTTP/2-aware load balancers/ingress and careful HTTP/2 tuning; REST works everywhere with simpler infra.Recommendation and reasoning:Pick gRPC for internal, latency-sensitive microservices where you control clients and infra—benefits in latency, throughput, and streaming outweigh added proto+infra complexity. Use generated stubs, standardize protobufs, add gRPC health and reflection, ensure LB/ingress supports HTTP/2, and add OpenTelemetry tracing and metrics. If you need broad external client/browser support or want minimal ops friction, prefer REST.
HardSystem Design
41 practiced
Architect an API platform for a multi-tenant SaaS product supporting tenant isolation, per-tenant feature flags, per-tenant rate limits, custom schema extensions, and data residency constraints. Discuss tenancy models (shared schema with tenant_id vs isolated DBs), tenant routing, policy enforcement, auditing, and how to evolve shared contracts safely without breaking tenants.
Sample Answer
Requirements & constraints:- Tenant isolation, per-tenant feature flags & rate limits, custom schema extensions, data residency (region placement), strong auditing, safe contract evolution, low latency and operational cost.Tenancy models (trade-offs):- Shared schema + tenant_id: - Pros: lower cost, easier cross-tenant analytics, simpler provisioning. - Cons: weaker blast radius, harder strict data residency, schema customization trickier. - Mitigations: row-level security (RLS), tenant_id as PK prefix, strict DB roles, encrypted columns per-tenant.- Isolated DB per-tenant: - Pros: strong isolation, easy to satisfy residency/regulatory needs, independent migrations. - Cons: higher ops cost, harder global queries, slower provisioning at scale.- Recommendation: hybrid: default shared-schema for small/medium tenants; isolated DB for enterprise/regulated tenants (flagged at onboarding).High-level architecture:- Edge/API Gateway (global) -> Tenant Router -> Auth & Tenant Context Service -> API Services (stateless) -> Tenant Data Plane (shared DB cluster(s) or tenant-specific DBs)- Components: Gateway (rate-limiting, tenancy routing), Tenant Context Service (resolves tenant metadata: features, residency, DB connection), Policy Engine, Feature Flag Store, Audit/Event Store, Monitoring.Tenant routing & data residency:- Tenant metadata registry maps tenant -> region, tenancy-model, feature flags, rate limits.- At edge: use tenant identifier from hostname, API key, or JWT claim; gateway calls Tenant Context Service to get routing decisions.- For residency: gateway routes requests to regional ingress aligned with tenant's data region; services connect to region-local DBs. Use geo-replicated control plane (metadata) separate from data plane.Policy enforcement:- Central Policy Engine (OPA or custom) enforces: - Authentication & authorization (JWT + tenant claims) - Row-level access (RLS policies / service-level checks) - Per-tenant feature flags (cached in local service; consistent with central flag store like LaunchDarkly or open-source store with streaming updates) - Per-tenant rate limits implemented at gateway (token-bucket in Redis or in-memory with consistent hashing) and at service boundaries for burst protection.- Services receive Tenant Context (immutable for request) and consult policy engine for decisions.Custom schema/extensions:- Options: - JSONB / schemaless columns in shared DB: allow tenant-specific fields with validation in services; keep core contract small. - Extension tables per-tenant in shared DB (namespaced tables) for heavier customizations. - For isolated DBs: allow full schema extensions per-tenant.- Enforce schema compatibility using service-level validation and a schema registry per tenant for custom fields.Auditing & observability:- Emit immutable audit events for data-modifying operations to append-only event store (Kafka + compacted storage) and write-ahead audit DB (region-local with replication policy).- Include tenant_id, user_id, request_id, operation, before/after diffs (redact PII).- Centralized SIEM/compliance pipelines, tamper-evident logs (WORM or signed events) for regulatory needs.- Track policy decisions and feature-flag evaluations for postmortem.Evolving shared contracts safely:- Version your public APIs and protobufs/JSON schemas; prefer additive changes.- Backward compatibility practices: - Additive fields only; avoid removing fields — mark deprecated. - Use feature flags and per-tenant rollout to gate new behavior. - Schema migration patterns: expand-with-copy for columns; use dual-write or transformation layer; perform online migrations on isolated DBs first. - Contract compatibility tests in CI with a tenant-matrix (different feature-flag combinations and extensions).- Canary + progressive rollout: release to internal tenants, then a small % of customers, monitor metrics and audits, then ramp.- Provide a compatibility shim in the API layer to translate old client requests where feasible.Operational concerns & best practices:- Automate tenant provisioning and lifecycle (infrastructure as code).- Backup & restore by tenant; encryption keys per-tenant where required.- SLA-driven isolation: offer tiers (shared vs isolated) with clear SLAs.- Regularly run chaos tests and compliance audits; keep runbooks for cross-region failover.This design balances cost and isolation via a hybrid model, enforces runtime policies centrally, ensures data-residency by routing to region-local data planes, supports tenant-level customizations safely, and uses versioning + feature flags + canaries to evolve contracts without breaking tenants.
Unlock Full Question Bank
Get access to hundreds of Application Programming Interface Design and Integration interview questions and detailed answers.