Section 4: Three Audiences, Three Contracts - API Design (with Pushback)
Executive Summary: A feature flag platform has three very different callers: product teams changing flags a few thousand times a day, server code asking "is this on?" 50 million times a second, and browsers and phones that must never see the rules. This section designs one contract per caller: management REST with optimistic concurrency and a dedicated kill endpoint, an in-process SDK that never throws and never blocks, a version-addressed stream protocol, and a client evaluation endpoint that returns values instead of rules. Four rounds of pushback test the choices that matter most.
The interviewer uncaps the marker: "Walk me through the APIs." The trap here is drawing one API. Section 3 showed that the read path and the write path differ by roughly nine orders of magnitude, so you say out loud that there are three audiences, and each one gets its own contract.
| Surface | Caller | Volume | Latency budget |
|---|---|---|---|
| Management REST | Product teams, rollout controller, on-call | 5,000 changes/day | p99 ≤ 500 ms |
| Server SDK + stream | 100,000 service instances | 50M evals/s, all local | p99 ≤ 50 µs per eval |
| Client evaluation | Web and mobile apps | ~35,000 fetches/s at peak | p99 ≤ 50 ms at the edge |
The Management API: Where Humans Change Things
Creating smart-reply-v2 is a small, ordinary resource:
POST /v1/flags
{ "key": "smart-reply-v2", "type": "BOOLEAN",
"variations": [false, true], "defaultVariation": false,
"owner": "fabrikam-mail-compose", "tags": ["mail", "ai"] }
type is one of BOOLEAN | STRING | NUMBER | JSON, so the same machinery carries multivariate values later. Targeting lives per environment, and every edit is a conditional write:
PUT /v1/flags/smart-reply-v2/environments/prod/rules
If-Match: "v41"
{ "rules": [ { "segment": "seg-no-preview", "serve": false },
{ "segment": "seg-ring0-fabrikam", "serve": true } ],
"rollout": { "bucketBy": "userId", "percent": 25 } }
-> 200 OK ETag: "v42" or 412 Precondition Failed
Interviewer: "Why the If-Match? It's a config tool, not a bank."
You: "Because two people editing the same flag is normal on deploy days, and those days peak around 50 changes a minute. Without a precondition, whoever saves last silently erases the other person's rule. With it, the second writer gets a 412, re-reads version 42, and merges on purpose. Every save creates an immutable version, so the ETag is just that flag version number. The environment also gets a new snapshot version, 8813, which is what the fleet tracks. Section 5 builds the tables behind both."
Rollouts are a resource of their own, so nobody has to babysit percentages:
POST /v1/flags/smart-reply-v2/rollouts
{ "steps": [ { "segment": "ring-0-fabrikam", "bakeMinutes": 1440 },
{ "percent": 1 }, { "percent": 5 }, { "percent": 25 },
{ "percent": 50, "approval": true }, { "percent": 100, "approval": true } ],
"minBakeMinutes": 60,
"gates": { "errorRatePp": 0.5, "p99Ratio": 1.5, "sustainMinutes": 5 } }
The plan states intent: Ring 0 for 24 hours, then 1% → 5% → 25% → 50% → 100%, at least an hour of bake per step, human approval past 25%, and treatment-vs-control gates. The rollout controller in Section 9 executes it. Two endpoints complete the surface: POST /v1/flags/{key}:kill with :unkill, and GET /v1/flags/{key}/usage, which serves the per-minute rollups behind stale-flag cleanup.
Quick check before moving on:
Sign in to continue reading
The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.
Sign in to read the full lesson