PracHub
QuestionsLearningGuidesInterview Prep
|Home/System Design/Snowflake

Design a REST API Abstraction Layer

Last updated: Jun 17, 2026

Quick Overview

This question evaluates system design and API design competencies, focusing on creating a REST API abstraction layer and internal SDK that address cross-cutting concerns like reliability, observability, security, versioning, and contract/interface modeling.

  • hard
  • Snowflake
  • System Design
  • Software Engineer

Design a REST API Abstraction Layer

Company: Snowflake

Role: Software Engineer

Category: System Design

Difficulty: hard

Interview Round: Technical Screen

## Design a REST API Abstraction Layer (Internal Service-Client SDK) A company runs many application servers that call internal REST services. Today, **every caller writes the same boilerplate by hand** for each call: construct the HTTP request, serialize the payload, add headers, attach authentication, parse the response, retry on failure, and map errors into something usable. As the number of services and endpoints grows, this duplicated code becomes a maintenance and reliability liability — a fix to retry behavior or auth has to be copied into dozens of call sites, and inconsistencies cause subtle production failures. Design an **abstraction layer / internal SDK** so that an application developer can write a typed call such as: ``` response = exampleService.processSomething(request) ``` instead of manually building a `POST` request. The layer should hide REST implementation details while still delivering **reliability** (timeouts, retries, circuit breaking), **observability** (metrics, logs, traces), **security** (service-to-service auth without callers handling secrets), **service evolution** (versioning and backward compatibility), and support for **many downstream services**. The interviewer gave **no specific performance or scale numbers** — state your own assumptions. Cover the architecture, the caller-facing API, the contract / interface-definition model, the end-to-end request flow, error handling, versioning, and operational concerns. ```hint Where should each concern live? A fix to retry or auth logic currently has to be copied into every call site. Think about which work is *cross-cutting* (the same for every service — transport, auth, retries, tracing, error mapping) versus *per-service* (the method names, paths, and schemas that differ). Where you put each kind determines how a single policy fix propagates. Decide what an individual caller writes, what a service owner declares, and what a shared piece of plumbing does on everyone's behalf. ``` ```hint How does the caller get a typed method? There's more than one way to turn `exampleService.processSomething(request)` into a real HTTP call. Consider the axis between resolving the call surface ahead of time (so a caller's IDE/compiler can see the types) versus resolving it at runtime from a contract (so nothing has to be rebuilt when an endpoint changes). Each end of that axis buys you something and costs you something — name the tradeoff and be ready to justify a default, including when you'd offer the other option. ``` ```hint Don't hide too much Hiding HTTP is good for ergonomics, but ask which controls a caller *legitimately* still needs even though they no longer see verbs or status codes — think about a call that's taking too long, a write that gets retried, or a caller who must branch on *why* a call failed. If every failure collapses into one generic exception, what can the caller no longer do? ``` ### Constraints & Assumptions State assumptions explicitly. Reasonable defaults for this problem: - **Users:** internal application developers across many teams; the SDK ships in the org's primary languages (e.g. Java, Python, Go, TypeScript). - **Topology:** services run inside one trusted network / mesh; calls are service-to-service (machine identity), not end-user-facing. - **Scale (assumed, since unspecified):** tens to low-hundreds of internal services, hundreds to low-thousands of endpoints, evolving weekly; per-call overhead from the abstraction should be negligible (sub-millisecond serialization + policy cost, no extra network hop unless a sidecar is deliberately chosen). - **Transport:** REST/JSON over HTTP today; the design should leave room for gRPC or async messaging later without re-architecting. - **Reliability target (assumed):** the layer must never make availability *worse* than hand-written calls — i.e. consistent timeouts and bounded, safe retries by default. ### Clarifying Questions to Ask - Who owns the contracts — a central platform team, or each downstream service owner self-serving their own API definition? - Which languages must the generated client support on day one, and is there a primary language we can lead with? - Is there an existing service mesh, API gateway, or service-discovery system the runtime should integrate with rather than reinvent? - How is service-to-service identity issued today (mTLS, signed JWT / SPIFFE, a token broker)? Can the runtime fetch and rotate credentials on the caller's behalf? - Are downstream endpoints predominantly idempotent reads, or are mutating writes common (which changes the default retry posture)? - Do we need a runtime/dynamic-discovery escape hatch, or is a compile-time-pinned generated client acceptable for everyone? ### What a Strong Answer Covers - A clear **layering** into contract registry → generation pipeline → shared runtime, with an explicit argument for *what lives where* (cross-cutting concerns in the runtime, not the generated stubs). - A concrete **contract / IDL model** (OpenAPI or an internal IDL) listing the fields a service owner must declare, plus CI validation of changes. - The **caller-facing API shape**: typed method, typed request/response, and the small set of call options (deadline, idempotency key, headers/context) that remain exposed — and a justified choice of generated-client vs. dynamic-proxy. - An **end-to-end request flow** from method call → schema validation → HTTP request → auth/trace injection → policy selection → dispatch → response/​error mapping → telemetry emission. - A **typed error taxonomy** plus the **retry / timeout / circuit-breaker** policy, including the idempotency rule for safe retries of mutating calls. - **Security** handled by the runtime (token fetch / rotation / injection, least-privilege scoping) so callers never touch secrets. - **Observability** emitted automatically per call (latency histogram, error/retry/timeout counts, circuit state, trace propagation) with payload-redaction defaults. - **Versioning & backward-compatibility** rules and how generated clients are **distributed** (internal package registries, version pinning). - Honest **tradeoffs** and failure modes (build/release overhead, escape hatches for special cases, the risk of over-hiding HTTP). ### Follow-up Questions - How do you support a **streaming** or **long-running async** call through an interface designed around a simple request/response method? What does the typed surface look like? - A downstream team ships a **breaking change** without bumping the version and breaks callers in production. What in your design should have prevented this, and how do you detect and roll back? - How would you extend the layer to also front **non-REST** backends (gRPC, a message queue) without changing the caller-facing programming model? - If you instead pushed all this cross-cutting logic into a **service-mesh sidecar** (e.g. Envoy) rather than an in-process library, what do you gain and lose, and which concerns can *only* live in the in-process SDK?

Quick Answer: This question evaluates system design and API design competencies, focusing on creating a REST API abstraction layer and internal SDK that address cross-cutting concerns like reliability, observability, security, versioning, and contract/interface modeling.

Related Interview Questions

  • Design a Geolocation Search Service - Snowflake (medium)
  • Design an Audit Logs Service - Snowflake (medium)
  • Design an Automated Jira-Ticket-to-PR System - Snowflake (hard)
  • Design a Cron Job Scheduler - Snowflake (medium)
|Home/System Design/Snowflake

Design a REST API Abstraction Layer

Snowflake logo
Snowflake
Apr 5, 2026, 12:00 AM
hardSoftware EngineerTechnical ScreenSystem Design
24
0

Design a REST API Abstraction Layer (Internal Service-Client SDK)

A company runs many application servers that call internal REST services. Today, every caller writes the same boilerplate by hand for each call: construct the HTTP request, serialize the payload, add headers, attach authentication, parse the response, retry on failure, and map errors into something usable. As the number of services and endpoints grows, this duplicated code becomes a maintenance and reliability liability — a fix to retry behavior or auth has to be copied into dozens of call sites, and inconsistencies cause subtle production failures.

Design an abstraction layer / internal SDK so that an application developer can write a typed call such as:

response = exampleService.processSomething(request)

instead of manually building a POST request. The layer should hide REST implementation details while still delivering reliability (timeouts, retries, circuit breaking), observability (metrics, logs, traces), security (service-to-service auth without callers handling secrets), service evolution (versioning and backward compatibility), and support for many downstream services.

The interviewer gave no specific performance or scale numbers — state your own assumptions. Cover the architecture, the caller-facing API, the contract / interface-definition model, the end-to-end request flow, error handling, versioning, and operational concerns.

Constraints & Assumptions

State assumptions explicitly. Reasonable defaults for this problem:

  • Users: internal application developers across many teams; the SDK ships in the org's primary languages (e.g. Java, Python, Go, TypeScript).
  • Topology: services run inside one trusted network / mesh; calls are service-to-service (machine identity), not end-user-facing.
  • Scale (assumed, since unspecified): tens to low-hundreds of internal services, hundreds to low-thousands of endpoints, evolving weekly; per-call overhead from the abstraction should be negligible (sub-millisecond serialization + policy cost, no extra network hop unless a sidecar is deliberately chosen).
  • Transport: REST/JSON over HTTP today; the design should leave room for gRPC or async messaging later without re-architecting.
  • Reliability target (assumed): the layer must never make availability worse than hand-written calls — i.e. consistent timeouts and bounded, safe retries by default.

Clarifying Questions to Ask Guidance

  • Who owns the contracts — a central platform team, or each downstream service owner self-serving their own API definition?
  • Which languages must the generated client support on day one, and is there a primary language we can lead with?
  • Is there an existing service mesh, API gateway, or service-discovery system the runtime should integrate with rather than reinvent?
  • How is service-to-service identity issued today (mTLS, signed JWT / SPIFFE, a token broker)? Can the runtime fetch and rotate credentials on the caller's behalf?
  • Are downstream endpoints predominantly idempotent reads, or are mutating writes common (which changes the default retry posture)?
  • Do we need a runtime/dynamic-discovery escape hatch, or is a compile-time-pinned generated client acceptable for everyone?

What a Strong Answer Covers Guidance

  • A clear layering into contract registry → generation pipeline → shared runtime, with an explicit argument for what lives where (cross-cutting concerns in the runtime, not the generated stubs).
  • A concrete contract / IDL model (OpenAPI or an internal IDL) listing the fields a service owner must declare, plus CI validation of changes.
  • The caller-facing API shape : typed method, typed request/response, and the small set of call options (deadline, idempotency key, headers/context) that remain exposed — and a justified choice of generated-client vs. dynamic-proxy.
  • An end-to-end request flow from method call → schema validation → HTTP request → auth/trace injection → policy selection → dispatch → response/​error mapping → telemetry emission.
  • A typed error taxonomy plus the retry / timeout / circuit-breaker policy, including the idempotency rule for safe retries of mutating calls.
  • Security handled by the runtime (token fetch / rotation / injection, least-privilege scoping) so callers never touch secrets.
  • Observability emitted automatically per call (latency histogram, error/retry/timeout counts, circuit state, trace propagation) with payload-redaction defaults.
  • Versioning & backward-compatibility rules and how generated clients are distributed (internal package registries, version pinning).
  • Honest tradeoffs and failure modes (build/release overhead, escape hatches for special cases, the risk of over-hiding HTTP).

Follow-up Questions Guidance

  • How do you support a streaming or long-running async call through an interface designed around a simple request/response method? What does the typed surface look like?
  • A downstream team ships a breaking change without bumping the version and breaks callers in production. What in your design should have prevented this, and how do you detect and roll back?
  • How would you extend the layer to also front non-REST backends (gRPC, a message queue) without changing the caller-facing programming model?
  • If you instead pushed all this cross-cutting logic into a service-mesh sidecar (e.g. Envoy) rather than an in-process library, what do you gain and lose, and which concerns can only live in the in-process SDK?

Submit Your Answer to Earn 20XP

Sign in to leave a comment

Loading comments...

Browse More Questions

More System Design•More Snowflake•More Software Engineer•Snowflake Software Engineer•Snowflake System Design•Software Engineer System Design

Your design canvas — auto-saved

PracHub

Master your tech interviews with 8,500+ real questions from top companies.

Product

  • Questions
  • Learning Tracks
  • Interview Guides
  • Resources
  • Premium
  • For Universities

Browse

  • By Company
  • By Role
  • By Category
  • Topic Hubs
  • SQL Questions
  • AI Coding Questions
  • Compare Platforms
  • Discord Community

Support

  • support@prachub.com
  • (916) 541-4762

Legal

  • Privacy Policy
  • Terms of Service
  • About Us

© 2026 PracHub. All rights reserved.