API Design

Interview Context

DS/ML 工程師遲早要把 model 包成 API 給下游用,也要天天呼叫別人的 API 抓資料。面試官想確認你能設計一個乾淨、可擴展、不會被 retry 打爆的 endpoint,也想知道你被第三方 API rate limit 時知道怎麼優雅地處理 — 這是「能不能獨立把東西上線」的重要信號。

What You Should Understand

  • 能解釋 REST 的核心概念:resources、HTTP verbs、statelessness、URL 設計慣例
  • 知道 REST、GraphQL、gRPC 各自的運作方式和適用場景
  • 能描述 API gateway 做的每一件事,以及它和 load balancer、reverse proxy 的分工
  • 知道常見的 API versioning 策略和各自的 tradeoffs
  • 能列出 API 效能優化的主要手段(pagination、caching、async、compression)並系統性地 debug 慢 API
  • 理解 idempotency key、exponential backoff、circuit breaker 如何讓 API 呼叫變可靠

REST Fundamentals

REST (Representational State Transfer) is an architectural style, not a protocol. Core ideas:

PrincipleMeaning白話說明
ResourcesEverything is a resource identified by a URL每個「東西」(user、order、model)都有自己的網址
HTTP verbsActions are expressed by standard methods動作用 GET/POST/PUT/DELETE 表達,不寫進 URL
StatelessnessEach request contains everything the server needsServer 不記 session — 每個 request 自帶身分與 context
Uniform interfaceSame conventions everywhere學會一個 endpoint 的用法,其他 endpoint 也猜得到

Statelessness 是 scalability 的關鍵:因為任何 server 都能處理任何 request,load balancer 可以自由分配流量、server 可以隨時加減。

HTTP Verbs

VerbSemanticsIdempotent?Safe?Typical Use
GETRead a resourceYesYesFetch predictions, list users
POSTCreate / trigger an actionNoNoCreate a job, submit a batch predict request
PUTReplace a resource entirelyYesNoOverwrite a config object
PATCHPartially update a resourceNot guaranteedNoUpdate one field of a model registry entry
DELETERemove a resourceYesNoDelete an experiment

Idempotent = 同一個 request 重送多次,結果和送一次一樣。Safe = 不改變 server state。這兩個屬性決定了「哪些 request 可以放心 retry」— GET/PUT/DELETE 可以直接 retry,POST 需要 idempotency key(後面會講)。

Anatomy of a URL

https://api.example.com/v1/users/123/orders?status=shipped&sort=-created_at&page=2
\___/   \_____________/ \_/ \___________________/ \______________________________/
scheme      host       version   resource path            query parameters
PartExampleRole
SchemehttpsProtocol — production API 一律 HTTPS
Hostapi.example.com通常用獨立 subdomain 和主站分開
Versionv1API version(見 Versioning 一節)
Resource path/users/123/ordersHierarchy: collection → item → sub-collection
Query params?status=shippedFiltering, sorting, pagination — 不改變資源本身

REST Design Best Practices

PracticeDoDon't
Nouns, not verbsPOST /predictionsPOST /createPrediction
Plural resourcesGET /modelsGET /model
Nest for relationshipsGET /users/123/ordersGET /getOrdersByUser?id=123
Shallow nesting/orders/456(頂多兩層)/users/123/orders/456/items/789/reviews
Filtering via paramsGET /orders?status=shippedGET /orders/shipped
Sorting via paramsGET /orders?sort=-created_at為每種排序開新 endpoint
Pagination via paramsGET /orders?limit=20&cursor=abc一次回傳全部資料
Standard status codes201 Created, 404 Not Found, 429 Too Many Requests全部回 200 然後把錯誤塞在 body

動詞放進 URL 是最常見的 code smell

看到 /getUser、/updateOrder、/doPredict 這種 URL,面試官會直接扣分。HTTP verb 已經表達了動作 — URL 只描述「資源是什麼」。唯一常見的例外是不好對應到 CRUD 的操作(例如 /models/123:promote 這種 action-style endpoint),但要先窮盡 resource 的表達方式再考慮。

REST vs GraphQL vs gRPC

三種主流 API paradigm 的運作方式:

REST: Client 對 resource URL 發 HTTP request,server 回傳(通常是 JSON 的)resource representation。一個 endpoint 對應一種 resource — 需要多種資料就打多個 endpoint。

GraphQL: Client 送一段 query(描述「我要哪些欄位」)到單一 endpoint,server 依 schema 解析後只回傳被要求的欄位。解決 REST 的 over-fetching(拿到不需要的欄位)和 under-fetching(一個畫面要打好幾個 endpoint)問題。

gRPC: 基於 HTTP/2 的 RPC framework。用 Protocol Buffers 定義 service 和 message(強型別 schema),資料以 binary 編碼傳輸 — payload 比 JSON 小很多、序列化快很多,還支援 streaming。缺點是 browser 原生支援差,主要用於 server 之間的內部通訊。

DimensionRESTGraphQLgRPC
ProtocolHTTP/1.1 or HTTP/2HTTP (single POST endpoint)HTTP/2
PayloadJSON (text)JSON (text)Protobuf (binary, compact)
Typing無強制 schema(可用 OpenAPI 補)Strong schema (SDL)Strong schema (.proto)
Caching極好 — HTTP caching 直接可用難 — 都是 POST,需要 client-side cache難 — binary + POST-like
Streaming無原生支援(可用 SSE/WebSocket)SubscriptionsNative bi-directional streaming
Best forPublic API、CRUD、簡單整合前端聚合多資料源、mobile 省流量內部 microservices、低延遲高吞吐

Where a DS/ML Team Meets Each

ParadigmTypical Encounter
REST把 model 包成 /predict endpoint、呼叫第三方資料 API(Stripe, OpenWeather)、feature store 的 REST 介面
GraphQL公司前端團隊用 GraphQL gateway 聚合資料 — 你的 ML service 是它背後的一個 data source
gRPCTensorFlow Serving 和 Triton 的原生介面、內部 feature fetching(latency 敏感)、大量 embedding 傳輸

面試怎麼選?

標準答案框架:對外(public、瀏覽器、第三方)用 REST — 生態系與 caching 最成熟;內部 service-to-service 且 latency/throughput 敏感用 gRPC — binary payload + HTTP/2 multiplexing;前端需要彈性聚合多種資料才考慮 GraphQL — 但要接受 caching 和 rate limiting 變複雜的代價。

API Gateway

API gateway 是所有 client request 進入後端的單一入口。它把橫切關注點(auth、rate limiting、logging)從每個 service 抽出來集中處理。一個 request 進入 gateway 後的典型流程:

  1. Parameter validation — 解析 HTTP request,檢查必要欄位、格式是否合法
  2. Allow-list / deny-list check — 封鎖已知惡意 IP 或只放行白名單來源
  3. Authentication & authorization — 呼叫 identity provider 驗證身分(authn)、確認權限(authz)
  4. Rate limiting — 依 user/IP/endpoint 檢查是否超過額度,超過就回 429
  5. Dynamic routing — 依 path 把 request 導到正確的 backend service
  6. Protocol transformation — 例如對外收 REST/JSON,對內轉成 gRPC 呼叫 microservice
  7. Circuit breaking — 偵測到某個 downstream service 持續失敗時快速失敗,不讓錯誤擴散
  8. Error handling & fault isolation — 統一格式化錯誤回應,隔離單一 service 的故障
  9. Logging, monitoring, caching — 記錄 access log、匯出 metrics、cache 熱門回應

Load Balancer vs API Gateway vs Reverse Proxy

三者常被混用,但職責不同:

ComponentLayerPrimary JobKnows About
Load BalancerL4 or L7把流量平均分配到多台相同的 serverServer health、分配演算法(round robin, least connections)
Reverse ProxyL7代表 server 接收流量:SSL termination、compression、靜態內容 cache、隱藏後端拓撲後端在哪,但不懂 API 語意
API GatewayL7 (application-aware)API 管理:auth、rate limiting、routing、protocol 轉換、versioningAPI contract、user identity、business rules

概念上:reverse proxy 是「泛用的中介」,load balancer 是「專注於分流的 reverse proxy」,API gateway 是「懂 API 語意的 reverse proxy 加上一整套管理功能」。

How they work together — 大型系統常見的串接順序:

Client → DNS → Load Balancer (multi-region / multi-instance)
       → API Gateway (auth, rate limit, routing)
       → internal Load Balancer → Service instances

外層 LB 讓 gateway 本身可以水平擴展;gateway 做完 API 層的檢查後,再由內層 LB 把 request 分配到 service 的多個 instance。

API Versioning Strategies

API 一旦有人在用,任何 breaking change(改欄位名、刪 endpoint、改語意)都會弄壞 client。Versioning 讓你能演進 API 而不破壞既有整合。

StrategyExampleProsCons
URL pathGET /v2/users最直觀、好 debug、curl 一看就懂、容易按版本 routeURL 不再「純粹指涉資源」;版本爆炸時 route 混亂
Query parameterGET /users?version=2不動 path、預設可 fallback 到最新版容易被忽略、cache key 要把 param 算進去、不夠正式
HeaderAccept: application/vnd.api+json;version=2URL 最乾淨、符合 HTTP 語意(content negotiation)不能直接在瀏覽器測、對新手不友善、debug 麻煩

實務上 URL path versioning 最流行(Stripe、Twitter、OpenAI 都是 /v1/),因為顯式、好觀察、好 route。原則:只有 breaking change 才需要 bump major version;加欄位、加 endpoint 這種 backward-compatible 改動不用。

Semantic Versioning

Library 和 API contract 常用 SemVer 表達版本號:MAJOR.MINOR.PATCH(例如 2.4.1)。

SegmentBump WhenExample
MAJORIncompatible / breaking API changes移除欄位、改回傳格式 → 2.4.1 becomes 3.0.0
MINORBackward-compatible new functionality加一個 optional 參數 → 2.4.1 becomes 2.5.0
PATCHBackward-compatible bug fixes修一個計算錯誤 → 2.4.1 becomes 2.4.2

Pre-release 標記如 1.0.0-alpha、1.0.0-rc.1 表示尚未穩定。0.x.y 版本代表「API 還沒承諾穩定,隨時可能變」。

API Performance

The Top Optimization Levers

LeverHow It WorksWhen to Use
Pagination一次只回一頁(limit + offset 或 cursor)List endpoint 的資料量會成長時 — 幾乎永遠
CachingClient cache(Cache-Control, ETag)、CDN、server-side cache(Redis)讀多寫少、可以容忍些微 staleness 的資料
Connection pooling重用 TCP/DB connection,省去反覆 handshake任何高頻對 DB 或 downstream 的呼叫
Async / queue for slow work慢工作丟進 queue,立刻回 202 + job id,client 之後 poll 或收 webhook任務超過 1-2 秒(batch prediction、report 生成)
Payload compressiongzip / brotli 壓縮 response body大 JSON payload、頻寬受限的 mobile client
N+1 avoidance一次 batch query 取代迴圈裡的 N 次 queryList endpoint 要帶出關聯資料時(每筆 order 再查一次 user)

Pagination: Offset vs Cursor

ApproachExampleProsCons
Offset-based?limit=20&offset=40簡單、可跳頁Deep offset 慢(DB 要掃過前面所有 rows);資料插入時會漏或重複
Cursor-based?limit=20&cursor=eyJpZCI6MTIzfQ穩定、深頁也快(用 index seek)不能跳頁、cursor 要編碼排序鍵

推薦系統或 feed 類 API 一律用 cursor-based — 因為資料一直在插入,offset 分頁會讓 user 看到重複內容。

How to Debug a Slow API

面試被問「你的 API 很慢,怎麼查?」— 沿著 request 的生命週期逐段量測,不要亂猜:

DNSTCP connectTLS handshakeServer processingSerializationNetwork transfer\text{DNS} \to \text{TCP connect} \to \text{TLS handshake} \to \text{Server processing} \to \text{Serialization} \to \text{Network transfer}
StageWhat Goes WrongHow to Check / Fix
DNS resolution慢的 DNS provider、無 cachecurl -w timing、換 DNS、加 TTL cache
TCP connection每個 request 都重新 handshakeConnection pooling、keep-alive
TLS handshake反覆 full handshakeTLS session resumption、HTTP/2 重用連線
Server processing最常見:慢 query(missing index)、N+1、blocking synchronous calls、CPU-heavy 邏輯Profiling 找 hot path、加 index、batch query、慢工作丟 async
External API calls第三方服務慢,拖垮整體平行呼叫、aggressive timeout、cache 第三方回應
Serialization巨大 JSON 的 encode/decode縮小 payload、用 faster serializer、gRPC/protobuf
Network transferPayload 太大、client 太遠Compression、CDN、pagination
InfrastructureServer 滿載、connection pool 上限Auto-scaling、調 pool size

Measure First

面試中最強的信號是先講「量測」再講「解法」:先看 p50/p95/p99 latency 分辨是普遍慢還是尾端慢,再用 tracing(如 OpenTelemetry)拆出每一段的耗時,找到實際 bottleneck 才動手。直接跳「加 cache」的候選人會被追問到死。

Idempotency & Reliability

Network 是不可靠的:request 可能送達但 response 掉了。此時 client 不知道操作有沒有成功 — retry 可能造成重複扣款、重複建 job。可靠的 API 設計要同時處理「怎麼安全地重試」和「失敗時怎麼不雪崩」。

Idempotency Keys

機制:client 為每個操作產生一個唯一的 key(通常是 UUID),放在 header 如 Idempotency-Key 中。Server 第一次看到這個 key 就執行操作並把結果存起來;之後帶同一個 key 的 request 不重新執行,直接回傳存好的結果。

  • POST(non-idempotent)因此變得可以安全 retry — Stripe 的付款 API 是經典範例
  • Key 通常設定 TTL(例如 24 小時)後過期
  • Server 端用 unique constraint 或 Redis SETNX 保證同 key 只執行一次

Retries with Exponential Backoff + Jitter

Retry 的原則:只 retry 可能是暫時性的失敗(timeout、429、500/502/503),不 retry 明確的 client error(400、401、404 — 重送一百次也不會變對)。

Naive 的固定間隔 retry 會讓所有 client 同時重試 — 對已經在掙扎的 server 形成 retry storm。解法是 exponential backoff with jitter:

delay=min(cap, base2attempt)random(0,1)\text{delay} = \min(\text{cap},\ \text{base} \cdot 2^{\text{attempt}}) \cdot \text{random}(0, 1)
  • Exponential: 等待時間 1s → 2s → 4s → 8s,給 server 恢復的時間
  • Cap: 上限(例如 60s),避免等到天荒地老
  • Jitter: 乘上隨機數把 client 的重試時間打散,避免 thundering herd(大家同秒重試)

Timeouts

沒有 timeout 的呼叫是掛掉的開始:一個慢的 downstream 會佔住你的 thread/connection,最後把整個 service 拖垮。原則:每一個對外呼叫都要有 timeout,而且上游的 timeout 要大於下游的 timeout 總和(否則上游先放棄,下游做白工)。

Circuit Breaker Pattern

Retry 解決「偶發失敗」,circuit breaker 解決「持續失敗」— 當 downstream 已經倒了,繼續打它只是浪費資源、拖慢自己。Circuit breaker 是一個有三個狀態的 state machine:

StateBehaviorTransition
Closed(正常)Request 照常通過,統計失敗率失敗率超過 threshold → 跳到 Open
Open(熔斷)直接 fail fast,不打 downstream經過 cooldown 時間 → 進入 Half-Open
Half-Open(試探)放少量 request 過去試水溫成功 → 回 Closed;失敗 → 回 Open

Reliability 三件套的關係

Timeout 決定「單次呼叫等多久放棄」,retry with backoff 決定「放棄後怎麼再試」,circuit breaker 決定「什麼時候連試都不要試」。三者疊加才是完整的 resilience 策略 — 面試中能把三者的分工講清楚就是 senior 信號。

Rate Limiting in Practice

Rate limiting 保護 API 不被單一 client(惡意或失控的 retry loop)打爆,也用來實作商業方案的配額(free tier 每分鐘 60 次)。

The Client-Facing Contract

被限流時 server 回 429 Too Many Requests,並用 headers 告訴 client 怎麼辦:

HeaderMeaning
Retry-After幾秒後可以再試
X-RateLimit-Limit這個 window 的總額度
X-RateLimit-Remaining還剩幾次
X-RateLimit-Reset額度何時重置(timestamp)

良好的 client 收到 429 應該讀 Retry-After 並配合 backoff — 而不是立刻重試(那只會吃掉更多額度)。

Rate Limit Keys: Per-User vs Per-IP

KeyProsConsUse When
Per-user / per-API-key精準、可依方案分級需要 authentication 才能識別已登入的 API(大多數 SaaS)
Per-IP不需要身分、擋匿名濫用NAT 後多人共用一個 IP 會誤傷;攻擊者換 IP 就繞過公開 endpoint(登入頁、註冊)
Per-endpoint保護特別貴的操作管理複雜昂貴 endpoint(batch predict、report export)

實務上常疊加使用:per-IP 擋匿名濫用 + per-key 實作方案配額 + 貴的 endpoint 額外收緊。

Algorithms Recap

AlgorithmHow It WorksProsCons
Fixed window每分鐘一個 counter,超過就擋最簡單、省記憶體邊界問題:兩個 window 交界處可瞬間打進 2x 流量
Sliding window log記錄每個 request 的 timestamp,數過去 60 秒內的數量完全精準每個 request 都要存 — 記憶體貴
Sliding window counter用前後兩個 window 的 counter 加權近似精準度和成本的好平衡是近似值,非精確
Token bucketBucket 以固定速率補充 token,每個 request 消耗一個允許短暫 burst、實作簡單參數(bucket size, refill rate)要調
Leaky bucketRequest 進 queue,以固定速率流出處理輸出速率平滑穩定Burst 會被排隊延遲,不適合 latency 敏感場景

Token bucket 是業界最常見的選擇(AWS、Stripe 都用)— 因為它天然容忍合理的 burst,符合真實流量的形狀。

Real-World Use Cases

Case 1: ML Model Serving API(batch predict 與 version 管理)

你的團隊要把 churn model 開放給公司內其他部門使用。單筆預測用 POST /v1/predictions 同步回傳;但行銷部門每週要對 200 萬用戶跑一次全量預測 — 同步 endpoint 會 timeout。

設計:batch 工作走 async pattern — POST /v1/batch-predictions 收到請求後把 job 丟進 queue,立刻回 202 Accepted 加上 job id;client 之後 GET /v1/batch-predictions/234 查狀態,完成後拿到結果檔案的下載連結。Model 升級用 URL versioning(/v1 vs /v2)處理 breaking change(例如 feature schema 改變),而同一個 API version 內部的 model 迭代則透過 response 裡的 model_version 欄位揭露,方便下游 debug「為什麼分數變了」。

面試 follow-up:

  • 如果 batch job 跑到一半掛掉,client 重送 request 會發生什麼?(idempotency key 避免重複跑一次 200 萬筆的預測)
  • /v1 和 /v2 的 model 需要同時在線多久?怎麼讓下游遷移?(deprecation policy、sunset header、用 access log 追蹤誰還在打 v1)
  • 同步 predict endpoint 的 latency budget 怎麼定?(下游服務的 timeout 減去 network overhead,反推 model 推論時間上限)

Case 2: 推薦系統 API 的 Pagination 與 Caching

你負責的推薦 API 提供 GET /v1/users/123/recommendations 給 App 的首頁 feed。流量尖峰時每秒上萬次請求,而且 user 會不斷往下滑載入更多。

設計:分頁必須用 cursor-based pagination — 推薦列表是動態生成的,offset 分頁會在候選池更新時讓 user 看到重複的項目;cursor 編碼「上次看到哪裡」讓結果穩定。Caching 分層處理:推薦結果對同一個 user 短時間內不需要重算,server-side 用 Redis cache 個 5 分鐘(key 是 user id + context);response 帶 Cache-Control: private, max-age=300 讓 client 也能重用 — private 是因為推薦是個人化的,絕不能進 shared CDN cache。熱門的 fallback 推薦(給冷啟動用戶的 popular items)則是全站相同,可以放 CDN。

面試 follow-up:

  • Cache 5 分鐘會不會讓「剛買過的商品還被推薦」?(event-driven cache invalidation:purchase event 觸發刪 cache)
  • 個人化內容為什麼不能進 CDN?(shared cache 會把 A 的推薦回給 B — privacy 事故)
  • 怎麼衡量 cache 的效益?(cache hit rate、p99 latency 前後對比、Redis 與重算的成本比較)

Case 3: 第三方資料 API 抓取的 Retry 與 Rate Limit 應對

你要每天從第三方 API 抓取 50 萬筆商品資料餵給 feature pipeline。對方的限制是每分鐘 600 次請求,而且偶爾會回 500 或 timeout。

設計:Client 端自己實作 token bucket throttling(每秒最多 10 次)主動遵守額度,而不是打到 429 才被動處理 — 被動觸發限流可能導致 API key 被封。收到 429 時讀 Retry-After header 並暫停;收到 500/timeout 時用 exponential backoff + jitter 重試最多 3 次;為整個抓取任務加上 circuit breaker — 如果對方持續回錯,暫停 10 分鐘再試,避免無謂消耗額度。所有 request 記錄 request id 和 response status,方便對帳「今天少了哪些商品」。抓取進度要 checkpoint(記錄抓到第幾個 cursor),任務中斷後從斷點續跑而不是從頭來過。

面試 follow-up:

  • 為什麼 backoff 需要 jitter?(多個 worker 同時失敗會同時重試 — thundering herd 讓對方更痛,也讓你的重試更容易再失敗)
  • 哪些 status code 不該 retry?(400/401/403/404 — deterministic failure,重試無意義且浪費額度)
  • 如果 pipeline 的時間窗不夠抓完 50 萬筆怎麼辦?(提高並行度但總速率仍受 rate limit 約束 → 和對方談 bulk export/webhook、增量抓取只拿 changed records)

Hands-on: API Design Patterns in Python

FastAPI Endpoint with Pagination and Idempotency Key

from fastapi import FastAPI, Header, HTTPException, Query

app = FastAPI()
idempotency_store = {}  # in production: Redis with TTL

@app.get("/v1/models/{model_id}/predictions")
def list_predictions(
    model_id: str,
    limit: int = Query(20, le=100),   # cap page size
    cursor: str | None = None,        # cursor-based pagination
):
    rows, next_cursor = fetch_page(model_id, limit, cursor)
    return {
        "data": rows,
        "next_cursor": next_cursor,   # null when no more pages
    }

@app.post("/v1/batch-predictions", status_code=202)
def create_batch_job(
    payload: dict,
    idempotency_key: str = Header(...),  # client-generated UUID
):
    # Same key seen before -> return the stored result, do NOT re-run
    if idempotency_key in idempotency_store:
        return idempotency_store[idempotency_key]

    job_id = enqueue_batch_job(payload)  # slow work goes to a queue
    response = {"job_id": job_id, "status": "queued"}
    idempotency_store[idempotency_key] = response
    return response  # 202 Accepted + job id; client polls for status

Retry Decorator with Exponential Backoff and Jitter

import random
import time
from functools import wraps

RETRYABLE = {429, 500, 502, 503, 504}  # transient failures only

def retry_with_backoff(max_attempts=4, base=1.0, cap=30.0):
    def decorator(fn):
        @wraps(fn)
        def wrapper(*args, **kwargs):
            for attempt in range(max_attempts):
                try:
                    return fn(*args, **kwargs)
                except TransientAPIError as e:
                    if e.status not in RETRYABLE or attempt == max_attempts - 1:
                        raise  # non-retryable or out of attempts
                    # full jitter: random delay in [0, min(cap, base * 2^attempt)]
                    delay = random.uniform(0, min(cap, base * 2 ** attempt))
                    time.sleep(delay)
        return wrapper
    return decorator

@retry_with_backoff(max_attempts=4)
def fetch_products(cursor):
    # honor Retry-After on 429 before raising, inside the client
    return third_party_client.get("/products", cursor=cursor)

Simple Circuit Breaker

import time

class CircuitBreaker:
    """Closed -> Open on repeated failures; Half-Open after cooldown."""

    def __init__(self, failure_threshold=5, cooldown=60.0):
        self.failure_threshold = failure_threshold
        self.cooldown = cooldown
        self.failures = 0
        self.state = "closed"
        self.opened_at = 0.0

    def call(self, fn, *args, **kwargs):
        if self.state == "open":
            if time.time() - self.opened_at < self.cooldown:
                raise CircuitOpenError()      # fail fast, skip downstream
            self.state = "half_open"          # cooldown over: probe

        try:
            result = fn(*args, **kwargs)
        except Exception:
            self.failures += 1
            if self.state == "half_open" or self.failures >= self.failure_threshold:
                self.state = "open"           # trip the breaker
                self.opened_at = time.time()
            raise

        self.failures = 0                     # success resets the breaker
        self.state = "closed"
        return result

breaker = CircuitBreaker(failure_threshold=5, cooldown=60)
# result = breaker.call(fetch_products, cursor)

Interview Signals

What interviewers listen for:

  • 你的 URL 設計符合慣例(nouns、plural、filtering 用 query params),不會發明 /getUser 這種 endpoint
  • 你能依場景選 REST/GraphQL/gRPC 並講清楚 tradeoffs,而不是背「gRPC 比較快」
  • 被問到 slow API 時你先講量測(p95/p99、tracing 拆段),再講解法,且知道 N+1 和 missing index 是最常見兇手
  • 你會主動提到 idempotency key、timeout、backoff with jitter、circuit breaker — 代表你被 production 教育過
  • 你把 rate limiting 當雙向問題:自己的 API 要限流保護,呼叫別人的 API 要主動 throttle 與優雅退避

Practice

Flashcards

Flashcards (1/10)

REST 的 statelessness 是什麼?為什麼它對 scalability 重要?

每個 request 自帶 server 處理所需的全部資訊(身分、context),server 不保存 session state。好處:任何 server 都能處理任何 request,load balancer 可以自由分流、server 可以隨意水平擴展或替換。

Click card to flip

Quiz

Question 1/10

下列哪個 endpoint 設計最符合 REST 慣例?

Mark as Complete

3/5 — Okay