Section 2: Promise Only What the Mirror Can Keep - Requirements (Functional & Non-Functional)
Executive Summary: This section converts Section 1's clarifications into a contract: functional requirements demoed against the 203.0.113.42 story, and non-functional numbers a test can check — ≤ 2 µs added per request with zero network on the path, ≤ 60 s propagation to 30 PoPs, and a per-class freshness table that is the promise (D8). We name what stays out of scope, survive two pushbacks — "why is 'real time' not in this table?" and "fail-open?! legal says block" — and frame the core tension: compliance wants certainty, availability wants independence, and the design buys both by making the expensive class local.
The clarifications are done, and the interviewer caps the marker: "So what exactly are we committing to?" Two audiences hide in that question. Legal and trust & safety want certainty: a deny rule is enforced, provably, on a known clock. The platform team wants independence: 5 million requests per second must never wait on a 150-request-per-second vendor. And every requirement must be a promise a test could fail. "Block bad IPs quickly" can't fail. "A manual override reaches all 30 PoPs within 60 seconds" can.
The Functional Contract
You tie each requirement to the running example so the interviewer can picture the demo:
| Requirement | What it means | The 203.0.113.42 demo |
|---|---|---|
| In-line decision on every request | Allow/deny in-process at the edge, before routing, no network hop (D1) | The 09:14:00 request at the Frankfurt PoP gets its verdict in ~2 µs |
| Three rule sources, one precedence | Overrides > country/legal > vendor list; merged at compile time, never per request (D2, D11) | An allow-override would beat SentinelDB's listing of 203.0.113.42 |
| CIDR + singles, IPv4 + IPv6 | Rules from one address to a country's worth of ranges, both families | The CN rule compiles against the ~400K-range GeoIP dataset; 203.0.113.42 is one /32 |
| Async validation pipeline | Never-seen IPs served, reported, validated by priority within the 120 req/s budget (D3, D4, D7) | First seen 09:14, checked 09:16 (suddenly sending 400 req/s), verdict v-5,812 |
| Policy & override API | Legal enacts country rules; trust & safety upserts overrides — audited, reason required, expiring | The 14:00 CN policy commit; no vendor involved |
| Appeals / unblock path | The 403 carries a support code; human review overrides the vendor until re-review (D11) | A false-positive customer gets an allow-override in minutes |
| Audit | Decisions countable by (rule source, verdict, PoP, minute); versions append-only | "Why was this IP denied at 14:32?" → rule source + snapshot version |
| Dashboards | Coverage, validation lag, deny rates by source, exposure minutes | Priya watches first-look queue depth against the coverage SLO |
The interviewer circles the second row.
Interviewer: "Three sources with precedence — why not just one block list?"
You: "Because the sources have different physics. The vendor list is bounded by a 150-req/s check-only API — I can't promise it anything faster than my re-validation cadence. Country rules are a local GeoIP database plus a policy commit — no vendor in the loop, so I can promise minutes. Manual overrides are a human fixing something right now — they must beat both, in seconds. One merged list means one freshness promise for all three: a lie about the vendor class, or an insult to the override class."
Key Insight: Splitting rules by source is what makes the next table honest — and the expensive class, the legal one, is deliberately the local one (D2).
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