Distilled Prep
Distilled Prep — Stripe — printed — distilledprep.com

Stripe

New here? Start with the canon below — easiest first — then browse Fresh by interest.

Stripe's engineering culture prizes craft, correctness, and practicality: they move money, so the tolerable error rate on core systems is effectively zero, and their famously practical interviews (debugging real code, integrating against real APIs) mirror that. Their blog is low-volume and high-signal — almost every post is a lesson in API design, migration discipline, or financial correctness.

For data scientists, expect payments-flavored analysis: incrementality with honest counterfactuals, funnel diagnosis across issuers and networks, and fraud/risk trade-offs where false positives cost real merchants real revenue. For software engineers, the canon topics are idempotency, ledgers, webhooks, and API contracts — systems where "mostly correct" is a failure state.

Names worth recognizing: Sorbet (their gradual type checker for Ruby) and their public engineering ethos around API versioning and backwards compatibility.

Their vocabulary

Idempotency keys

The API pattern Stripe made famous: clients attach a unique key to mutating requests so retries never double-charge. Not internal jargon but a Stripe-popularized concept every candidate should be able to design from scratch.

Sorbet

Stripe's open-source gradual type checker for Ruby, built to keep a massive Ruby codebase safe and refactorable. Emblematic of their engineering culture: pragmatic tooling investments in correctness.

Fresh from their blog

Derived from recent posts, newest first.

SWE ·
Distilled from a public engineering post

You run a payments API that thousands of businesses have integrated against. Your team needs to ship a breaking change: a field that was a boolean is being replaced by a richer string enum. Some integrations are years old and you cannot force everyone to migrate at once.

Design a versioning system that lets you ship this change — and future breaking changes — without ever silently corrupting an existing integration. Focus on the server-side mechanics: how versions are identified per request, how the transformation is applied, and what the maintenance cost looks like as the number of versions grows.

Systems design · api-design · developer-tooling · Aug 2017
Practice against the follow-up probes
  • How does the server know which version of the response to produce for a given request, and what are the failure modes of each approach?
  • Walk me through what happens in your system when a client sends a request with no version header at all.
  • Your transformation pipeline now has forty version change modules accumulated over five years. A new engineer adds one in the wrong order. What breaks, and how do you prevent it?
  • Some breaking changes can't be expressed as a pure response transformation — for example, the semantics of how a webhook event is triggered have changed. How does your versioning abstraction handle that?
  • At what point, and by what process, would you retire an old version? What are the risks on both sides of that decision?
Show answer guide
This guide is the map, not the speech. In a real interview, a strong candidate covers perhaps half of this — aloud, imperfectly, recovering when probed. Use it to check your reasoning afterward, not as a script to memorize.

What the interviewer is probing

This question probes whether the candidate can separate version identification (per-request routing) from version transformation (response rewriting) and reason about each independently. Strong candidates will surface the fixed-cost property — that adding a new version should not require touching old code — and grapple honestly with the cases where clean encapsulation breaks down (side-effectful changes, webhook semantics, schema migrations). Decision quality is tested by the version-retirement probe: it forces the candidate to weigh correctness and compatibility guarantees against long-term maintenance debt.

What a strong answer covers

  • Version identification — three sources in priority order: explicit header on the request (developer testing or per-request override), OAuth app version if the request is acting on behalf of a user, else the account's pinned version set at first API call. The pinned-at-first-call pattern is important: it means a newly signed-up user always gets the latest behavior without any configuration, while existing integrations are shielded from surprises.
  • Canonical representation: internally, the API always generates responses in the current schema — the latest version is the source of truth. Versioned responses are produced by transforming down from current, never by maintaining parallel codepaths for each version. This keeps feature code clean.
  • Transformation pipeline: model each breaking change as a self-contained module (e.g., a class or record) that declares: the version it belongs to, which resource types it affects, and a transformation function f(current_response) → older_response. Modules are stored in a time-ordered list. To serve version V, walk backwards from current through every module dated after V and apply each transformation in sequence. Each module is written assuming it receives data shaped as it was when it was authored, not the raw current shape — this is the key invariant that makes modules composable without coupling.
  • The boolean→enum case specifically: the current schema has status: "verified" | "unverified" | "pending". The version change module for the old version maps statusverified: (status === "verified"). A client on the old pinned version sees the boolean; a client on any newer version sees the enum. No branching in the main codebase.
  • Side-effectful changes — the hard case: some changes can't be expressed as a pure response transformation. Examples: a webhook is now fired under different conditions, or a field change alters idempotency behavior. These must be flagged explicitly (e.g., an annotation on the module) and handled with conditional logic elsewhere in the codebase. This is unavoidable but should be the exception; the system should make the cost of a side-effectful change visible so engineers are incentivized to design around it.
  • Maintenance cost growth: with many modules, the risks are (a) a module inserted in the wrong order corrupts responses for version ranges between two dates, and (b) modules that can't be encapsulated accumulate as scattered conditionals. Mitigations: enforce module ordering with an automated test that applies the full pipeline against golden fixtures for each version; lint for un-annotated side-effect conditionals outside modules.
  • Version retirement: set a deprecation date, email users whose pinned version is below a sunset threshold, provide a migration guide, and sunset with enough lead time (e.g., 12–18 months). Risks of retiring too aggressively: break silent integrations; risks of never retiring: the transformation pipeline grows without bound and the oldest modules become load-bearing archaeology no one understands. A pragmatic criterion: retire when the module count for a version range exceeds a threshold and active traffic to that version drops below a floor.
  • Common wrong turns: (1) maintaining parallel response-generation codepaths per version — doesn't scale; (2) putting version logic inline in controller code — spreads the maintenance cost invisibly; (3) treating version as just a URL prefix with no transformation system — forces users into big-bang upgrades.

The underlying concept

Backward-compatible API evolution is fundamentally a transformation problem: the internal model evolves forward, and versioned responses are produced by composing a sequence of invertible (or at least directional) transforms from the current shape to the requested historical shape. The key design invariant is that each transform module is written against a stable interface — the shape of data at the time the breaking change was made — so modules remain independently understandable even as the current schema continues to evolve. This is analogous to database migration scripts run in reverse: you never rewrite old migrations, you write new ones. The account-pinned-at-first-call pattern solves a usability problem orthogonal to the transformation mechanics: it removes the need for new integrators to configure a version while still guaranteeing that existing integrators never receive an unexpected schema change. The long-run tension in any such system is between compatibility guarantees (never break existing users) and accumulated complexity (every old version is debt); retirement policies are the pressure-release valve, and their design is as important as the versioning mechanics themselves.

Source

Derived from APIs as infrastructure: future-proofing Stripe with versioning

SWE ·
Distilled from a public engineering post

You're building rate limiting middleware for a payment API. The rate limiter uses a shared Redis cluster to track per-user request counts.

Your tech lead asks two questions before approving the design:

  1. What happens to normal traffic if Redis becomes unavailable?
  2. How do you safely roll out new rate limit thresholds without disrupting existing users?

Walk me through your answers and the trade-offs involved.

Systems design · api-design · reliability · Mar 2017
Practice against the follow-up probes
  • You chose to fail open when Redis is down. A Redis outage is also likely to coincide with high load — the exact moment you most need rate limiting. How do you defend that choice?
  • What does "dark launching" a rate limiter mean concretely, and what metrics do you collect during that phase?
  • Your token bucket refill rate and bucket size are two separate knobs. Walk me through the difference between tuning one versus the other, and what user behavior each targets.
  • A single high-volume user is hammering a CPU-intensive endpoint with concurrent requests. Your per-second rate limiter isn't helping because their individual requests are slow and few. What do you do?
  • You want to alert on-call when rate limiting is triggering unexpectedly. What metrics do you instrument, and how do you distinguish "working as intended" from "misconfigured threshold"?
Show answer guide
This guide is the map, not the speech. In a real interview, a strong candidate covers perhaps half of this — aloud, imperfectly, recovering when probed. Use it to check your reasoning afterward, not as a script to memorize.
Also: observability

What the interviewer is probing

This question probes failure-mode reasoning: does the candidate see that the rate limiter's dependency (Redis) can itself be a reliability risk, and can they articulate why fail-open is the correct default for a correctness-critical payment API? It also probes deployment maturity — shadow mode / dark launch thinking, feature flags as kill switches, and observability as a first-class requirement, not an afterthought. Strong candidates expose the tension between fail-open (availability) and fail-closed (abuse prevention) and make a defensible recommendation with a named condition for each.

What a strong answer covers

  • Redis failure — fail open vs. fail closed:
  • Fail closed (reject all requests when Redis is down): protects against abuse but also takes down the API for legitimate users — unacceptable for a payment API where availability is the north star.
  • Fail open (pass all requests when Redis is down): preserves API availability; accepted risk is a window of unlimited traffic. Mitigate with circuit breakers and alerts so on-call knows Redis is down immediately.
  • Wrong turn: treating this as a binary choice without naming the mitigation. A strong answer pairs fail-open with fast alerting, a short TTL on the window, and possibly a local in-process fallback counter as a rough backstop.
  • Exception handling must be exhaustive — catch at the middleware layer so a bug in rate limiting code never propagates to the request handler.

  • Safe rollout of new thresholds:

  • Dark launch / shadow mode: run the limiter in observe-only mode; log which requests would have been rejected without actually rejecting them. Evaluate whether those requests represent abuse or legitimate usage.
  • Threshold tuning: compare shadow-mode rejection distribution against user segments; work with high-volume users whose patterns would be affected before flipping the limiter live.
  • Feature flags as kill switches: every limiter should be disableable in production without a deploy; this is non-negotiable for a payments API where a misconfigured limiter is an incident.
  • Gradual rollout: enable for a small traffic percentage, watch metrics, widen. Avoid a flag that's either fully on or fully off.

  • Token bucket mechanics (relevant to both):

  • Bucket size controls burst tolerance; refill rate controls sustained throughput. Conflating them is a common wrong turn.
  • Per-user buckets in Redis: key = rate_limit:{user_id}, value = token count, with TTL to auto-expire idle users.
  • Atomic decrement + check must be a single Redis operation (Lua script or SET/GETSET pattern) to avoid TOCTOU race between read and write.

  • Observability requirements:

  • Counters: requests allowed, requests rejected, rejection reason (rate limit vs. load shed), Redis errors.
  • Dashboards: rejection rate per user tier, per endpoint, per limiter type.
  • Alerts: spike in rejections (limiter misfiring), Redis error rate, worker utilization crossing shed thresholds.
  • Distinguish signal from noise: a limiter that always fires at 0.01% is working; one that suddenly fires at 5% needs investigation.

  • Concurrent vs. rate limiter distinction (probe setup):

  • Per-second limiter is ineffective for slow, resource-intensive endpoints where a user sends few but expensive requests concurrently.
  • Concurrent request limiter tracks in-flight requests (increment on entry, decrement on exit); caps parallelism rather than throughput — the right tool for CPU-bound endpoints.

The underlying concept

Rate limiters are a dependency — they sit in the hot path of every request, so their own failure mode directly determines API availability. The canonical resolution is fail-open with observability: preserve the user-facing contract when the limiter's backing store is unavailable, but make that condition immediately visible to operators. Token bucket is the standard algorithm because it naturally separates two concerns: bucket size governs burst tolerance (how much a user can overshoot momentarily) and refill rate governs sustained throughput — tuning them independently lets you match real traffic shapes. Dark launching (shadow mode) is the correct rollout discipline for any middleware that rejects traffic: measure impact before imposing it, because a miscalibrated threshold is indistinguishable from an outage from the user's perspective. The deeper principle is that reliability tooling needs its own reliability envelope — kill switches, feature flags, and metrics are not optional polish but load-bearing parts of the design.

Source

Derived from Scaling your API with rate limiters

SWE ·
Distilled from a public engineering post

You're building a payment API where each charge to a customer must happen exactly once. Clients are third-party servers that can crash mid-request, and your own network is occasionally unreliable — connections drop, responses time out, and retries are inevitable.

Design the server-side mechanism that makes this API safe to retry, and walk me through how the client should behave when a request fails.

Systems design · api-design · reliability · Feb 2017
Practice against the follow-up probes
  • A client retried with the same key, but the first attempt is still running on your server — it hasn't succeeded or failed yet. What happens?
  • The first attempt hit your server, updated the database, but crashed before it could call the downstream payment processor. The client retries. How do you avoid a double charge?
  • How long do you store idempotency key records, and what happens when you evict them?
  • Your idempotency key store itself becomes unavailable. Do you fail open or fail closed, and what are the consequences of each choice?
  • A client implements exponential backoff but omits jitter. Under what scenario does that still cause problems, and how bad is it compared to no backoff at all?
Show answer guide
This guide is the map, not the speech. In a real interview, a strong candidate covers perhaps half of this — aloud, imperfectly, recovering when probed. Use it to check your reasoning afterward, not as a script to memorize.
Also: distributed-systems

What the interviewer is probing

This question probes whether the candidate separates the problem into two distinct concerns — the server-side deduplication invariant and the client-side retry discipline — and whether they can reason about the failure cases that arise inside the idempotency machinery itself (in-flight duplicates, partial server-side progress, store unavailability). Strong candidates expose the at-least-once vs. exactly-once distinction and the role of atomic database operations in bridging it. Weaker candidates describe the happy path but miss the concurrent-retry and partial-execution edge cases.

What a strong answer covers

Server-side mechanism - Clients generate a unique idempotency key (e.g., UUID v4) per logical operation and send it in a request header. - On receipt, the server does an atomic "find-or-create" on an idempotency key record in a durable store (keyed by client ID + key value). This is the critical section: two concurrent requests with the same key must not both proceed. - If the record is new: mark it in_flight, execute the operation, then atomically update the record to complete with the serialized response. - If the record is complete: return the cached response immediately. - If the record is in_flight: a concurrent retry is racing with the original — safest default is to return a 409 Conflict or a retry-after response rather than proceeding, to avoid the double-execution problem. - The "find-or-create" must be atomic (database-level unique constraint or compare-and-swap); application-level check-then-insert has a race.

Handling partial server-side progress - If the server crashed after writing to its own DB but before charging the downstream processor, the idempotency record is either absent (crash before insert) or in_flight (crash mid-execution). - For absent: retry proceeds as a fresh attempt — safe. - For in_flight after a timeout: server should treat stale in_flight records as failed (with a timeout threshold), roll back any partial state if the DB is ACID, and allow the retry to re-execute cleanly. Alternatively, use a saga/two-phase approach: reserve → charge → confirm, with compensating actions for each partial failure.

Client-side retry behavior - Always retry with the same idempotency key — generating a new key on retry defeats the mechanism. - Use exponential backoff: wait ~ 2^n × base_delay before attempt n. - Add random jitter (e.g., multiply by a uniform random in [0.5, 1.5]) to desynchronize clients that all failed at the same moment (thundering herd). - Set a maximum retry count or total elapsed time; surface a terminal failure to the caller rather than retrying forever. - Common wrong turn: retrying on every error. Non-retriable errors (4xx client errors like bad card number) should not be retried regardless of idempotency — only transient failures (timeouts, 5xx, network errors) warrant retries.

Idempotency key record lifecycle - Retain records long enough to cover the client's maximum retry window (days to weeks is typical for payment APIs). - On eviction, a client retrying with an old key would be treated as a new request — document this contract explicitly so clients know the window. - Keys should be scoped to (client/API-key, idempotency-key) to prevent cross-client collisions.

Failure of the idempotency store - Fail closed (reject requests) is the safer default for money movement: a false positive (charging twice) is worse than a false negative (temporarily unavailable). Document this as the explicit trade-off. - Fail open risks double charges; acceptable only for operations where the cost of unavailability vastly exceeds the cost of duplication — rarely true for payments.

The underlying concept

Idempotency is the property that applying an operation multiple times produces the same result as applying it once. In distributed systems, clients cannot always know whether a request succeeded — the response may be lost even when the server completed the work — so safe retry requires the server to enforce idempotency rather than trusting the client to send only one request. The key insight is that 'exactly once' delivery is impossible at the network layer, but 'exactly once execution' is achievable at the application layer by making deduplication explicit: store a durable record of each logical operation keyed to a client-supplied identifier, and use that record to short-circuit duplicate executions. The hard part is not the happy path but the failure modes inside the mechanism itself — concurrent retries racing on the same key, partial execution before a crash, and what to do when the deduplication store itself is unavailable — each of which forces a deliberate trade-off between availability and correctness.

Source

Derived from Designing robust and predictable APIs with idempotency

SWE ·
Distilled from a public engineering post

Your team needs to migrate hundreds of millions of records from an old data model to a new one — say, moving subscription data from being embedded inside customer records into a dedicated table — without any downtime and without corrupting production data. Walk me through how you'd structure the migration and what invariants you'd enforce at each phase.

Systems design · migrations · distributed-systems · Feb 2017
Practice against the follow-up probes
  • Dual-writing keeps both tables alive during the transition. What happens if a write to the new table succeeds but the write to the old table fails — or vice versa? How do you handle that atomicity gap?
  • How do you backfill hundreds of millions of existing records without degrading production database performance?
  • How do you gain confidence that the new table is actually consistent with the old one before you flip reads over to it?
  • Once you've reversed the write order — writing to the new table first — what's your rollback plan if you discover an inconsistency a week later?
  • After all writes have been cut over, what's your process for cleaning up the old data, and what can go wrong at that final step?
Show answer guide
This guide is the map, not the speech. In a real interview, a strong candidate covers perhaps half of this — aloud, imperfectly, recovering when probed. Use it to check your reasoning afterward, not as a script to memorize.

What the interviewer is probing

This question probes whether the candidate can decompose a stateful migration into safe, incremental phases — and whether they reason about consistency at each phase transition rather than treating the migration as a bulk copy job. A strong answer surfaces the atomicity gap in dual writes, the danger of reading stale or inconsistent data before validation is complete, and the operational risk of touching the old data model during cleanup. Decision quality shows in how they manage rollback points and scope each phase to the minimum change set.

What a strong answer covers

  • Phase framing: break the migration into four distinct phases — dual-write, read migration, write migration, cleanup — with an explicit invariant for each: at no phase should any code path see inconsistent data.
  • Dual writing: start writing to both old and new stores simultaneously; ramp up gradually while watching write latency and error rates. Handle the non-atomic write pair by writing to the authoritative store first and tolerating brief lag on the secondary, or by catching failures and using a reconciliation job to heal divergence. Do not assume two separate writes are atomic.
  • Backfill strategy: query the production database as little as possible. Use offline snapshots fed to a distributed processing framework (MapReduce or equivalent) to generate the list of IDs to backfill. Run the backfill with a multi-threaded worker fleet, then re-run the offline job as a verification pass to confirm nothing was missed. Lazy migration on update events handles the long tail cheaply.
  • Validating reads before flipping: before switching reads to the new table, run a shadow-read experiment — execute both code paths and compare results for every live request; alert immediately on any mismatch. Gain statistical confidence across the full call graph before committing. Common wrong turn: flipping reads based on backfill completion alone, without live consistency checks.
  • Write path migration: reverse the write order so the new store is authoritative; keep the old store as a lagging archive for rollback. Migrate one write code path at a time, run shadow comparisons after each, and enforce that deprecated fields are inaccessible (raise errors on access) to prevent accidental regression.
  • Cleanup: lazy deletion first — zero out the old field on each load — then an offline verification pass to find remaining populated records, then a final bulk deletion. Drop the old column only after deletion is confirmed complete and a safe observation window has passed.
  • Key trade-offs to name: offline processing vs. production query load; lazy migration vs. explicit backfill completeness; shadow reads vs. increased per-request latency; rolling rollback capability vs. complexity of maintaining two live stores.

The underlying concept

Online schema migrations at scale are fundamentally a consistency problem, not just a data-movement problem. The core technique — dual writing — accepts a window of eventual consistency in exchange for zero downtime, which means every phase must have an explicit mechanism to detect and close consistency gaps before the next phase begins. Shadow execution (running two code paths in parallel and comparing results) is the standard tool for building confidence before a read or write cutover, because it catches divergence in production traffic patterns that unit tests miss. Backfill via offline snapshots decouples the read/write pressure of migration from the production database, which is critical when the table size is large enough that online queries would cause latency spikes or lock contention. The overall pattern generalizes: any stateful migration can be made safe by making each phase independently reversible and by never advancing to the next phase without a measurable consistency signal.

Source

Derived from Online migrations at scale

The canon

Curated questions on this company's enduring themes.

DS ·
Editor-written company foundation

Stripe launches a feature that automatically retries failed subscription payments on an optimized schedule. Determine whether it creates incremental recovered revenue without harming customers.

Product case · payments · causal-inference
Practice against the follow-up probes
  • Many failed payments would have succeeded on the merchant's existing retry logic anyway. How do you avoid claiming credit for those?
  • What harm could this feature cause, and what guardrail metrics catch it?
  • Merchants differ wildly — subscription size, industry, existing dunning tools. How does heterogeneity change your analysis and your rollout?
  • The feature interacts with merchants' own retry configurations. How do you attribute recovery between the two systems?
  • What would the merchant-facing report claim, and how do you make sure it's honest?
Show answer guide
This guide is the map, not the speech. In a real interview, a strong candidate covers perhaps half of this — aloud, imperfectly, recovering when probed. Use it to check your reasoning afterward, not as a script to memorize.
Also: experimentation

What the interviewer is probing

Whether the candidate instinctively reaches for a counterfactual rather than a raw recovery rate — the seductive wrong answer is "we recovered $X of failed payments," most of which would have been recovered anyway. Also probing harm-awareness: retries are not free (card network fees, decline-rate penalties, customers charged after intending to cancel) and strong candidates surface these unprompted. This is Stripe's flavor of DS: financial correctness and merchant trust as first-class constraints.

What a strong answer covers

Must establish

  • Define the estimand as incremental revenue: revenue recovered by this feature that would NOT have been recovered by the merchant's baseline process within the same window, not a gross recovery total.
  • Randomize at the merchant level, because the feature operates on merchant configuration and payment-level randomization within a merchant leaks through shared retry budgets and issuer behavior.
  • Measure cumulative recovery curves over 30 to 60 days, not point-in-time rates, because a retry can merely accelerate recovery that would have happened anyway.

A strong answer adds

  • Stratify merchants by size, industry, and presence of existing dunning tools at randomization time.
  • Track guardrail metrics: involuntary churn, disputes and chargebacks, refund rate, support contacts, card-network decline penalties, and customer complaints about post-cancellation charges.
  • Treat any guardrail metric moving adversely as a launch blocker regardless of the recovered-revenue result.
  • Report heterogeneous treatment effects by stratum; expect the feature to help small merchants with no dunning sophistication far more than large ones.
  • Use stratum-level effect estimates to shape both rollout priority and pricing decisions.
  • Merchant-facing reports must state incremental recovery with uncertainty, not gross recovery, because overstated claims destroy trust when merchants audit.

Common misses

  • Claims credit for gross recovered revenue rather than defining recovery against a counterfactual baseline, the seductive wrong answer.
  • Randomizes at the payment level rather than the merchant level, allowing leakage through shared retry budgets and issuer behavior.
  • Compares point-in-time recovery rates instead of cumulative curves over a long enough window to distinguish acceleration from creation of recovery.
  • Does not surface harm metrics unprompted, treating guardrails as an afterthought rather than half the evaluation.
  • Ignores merchant heterogeneity and reports a single average treatment effect without stratifying by dunning sophistication or merchant size.

The underlying concept

Attribution without a counterfactual systematically overstates impact — the same failure mode as "email campaigns drive purchases from people who would have bought anyway." The general antidote is to define the estimand first (incremental effect vs. baseline process), pick the randomization unit where the mechanism operates (here, the merchant), and measure over a horizon long enough to distinguish acceleration of an outcome from creation of one. In payments, the additional twist is that every intervention carries direct costs and trust costs, so guardrails aren't an afterthought — they're half the evaluation.

Source

Distilled Prep canon — curated from Stripe's public work on payments reliability and revenue recovery.

Source: Stripe engineering blog

DS ·
Editor-written company foundation

A large merchant's payment success rate dropped from 92% to 85% after they shipped a new checkout flow. Diagnose what happened and decide what Stripe should tell them.

Product case · payments · data-quality
Practice against the follow-up probes
  • Decompose "payment failed." What are the distinct failure layers?
  • How do you separate a causal product effect from a traffic-mix shift?
  • Which baselines make a decline interpretable?
  • The merchant blames Stripe. How does your analysis address that directly?
  • What concrete recommendations might come out, and how do you prioritize them?
Show answer guide
This guide is the map, not the speech. In a real interview, a strong candidate covers perhaps half of this — aloud, imperfectly, recovering when probed. Use it to check your reasoning afterward, not as a script to memorize.
Also: fraud-detection

What the interviewer is probing

Payments-funnel literacy plus consultative rigor: failures decompose into user abandonment, integration errors, authentication (3DS) friction, fraud blocks, and issuer declines — each with a different owner and fix. Strong candidates also check mix shifts (the new flow may attract different traffic) and frame findings the way Stripe actually would for a merchant: prioritized, evidence-backed, actionable.

What a strong answer covers

Must establish

  • Decompose the funnel into discrete stages: checkout started, payment details submitted, authentication (3DS), fraud screening, issuer authorization, and success. The diagnosis begins by attributing the 7-point drop to whichever stage's pass-rate moved, not to 'payments' as a whole.
  • Recognize that layer owners differ: abandonment and integration errors point to the merchant's new flow; authentication friction points to flow configuration; fraud blocks point to risk rules reacting to new signal patterns; issuer declines point to card or traffic mix or retry behavior.
  • Run a mix-shift check before making any causal claim: compare card brands, countries, device split, and new-vs-returning customers pre- and post-launch, because a flow that converts more first-time international users can lower the success rate with nothing broken.

A strong answer adds

  • Use two baselines to make the decline interpretable: the merchant's own pre-launch rates by segment, and Stripe's cross-merchant benchmarks for similar segments. 'Your 85% is normal for your new traffic mix' is a legitimate and complete finding.
  • Specifically investigate integration errors: spikes in particular error codes, malformed requests, missing fields, and client-side timeouts are the most common culprit after a checkout rewrite and the fastest category to fix.
  • Deliver ranked findings with magnitudes and owners. For example: '4 of the 7 points traced to 3DS now triggering on domestic cards, fix is a config change; 2 points traced to new international mix, expected and not broken; 1 point traced to elevated card-declined from a retry storm, fix is a backoff adjustment.' Each finding carries an owner and an expected recovery.

Common misses

  • Treats 'payment failed' as a single event rather than decomposing it across funnel stages, which makes attribution and ownership impossible.
  • Makes causal claims about the new checkout flow without first checking whether the traffic mix changed post-launch.
  • Skips segment-level baselines and cross-merchant benchmarks, leaving no way to judge whether 85% is actually anomalous for this merchant's new traffic profile.
  • Fails to check integration error codes and client-side issues, missing the most common and most quickly resolved failure mode after a checkout rewrite.

The underlying concept

A payment is a pipeline of independent gates, so a success-rate change is a sum of gate-level changes — and attribution must precede recommendation because each gate has a different owner and remedy. The statistical trap is composition: aggregate rates move when the mix moves, so segment-controlled comparisons (or standardization to a fixed mix) are mandatory before declaring causation. This question is also a communication test: analysis becomes value only when translated into prioritized actions with expected impact.

Source

Distilled Prep canon — curated from Stripe's public work on payments performance and checkout optimization.

Source: Stripe engineering blog

SWE ·
Editor-written company foundation

Design a webhook delivery platform that notifies millions of merchant endpoints about payment events — where one slow merchant must never delay anyone else's notifications.

Systems design · distributed-systems · api-design
Practice against the follow-up probes
  • What delivery guarantee do you offer, and what does the consumer contract require of merchants in return?
  • One merchant's endpoint hangs for 30 seconds per request. Trace what protects everyone else.
  • Retries: schedule, ceiling, and what happens after the ceiling?
  • What ordering do you promise? What do you refuse to promise, and why?
  • How do merchants debug "I never got the webhook," and how do they verify a webhook is really from you?
Show answer guide
This guide is the map, not the speech. In a real interview, a strong candidate covers perhaps half of this — aloud, imperfectly, recovering when probed. Use it to check your reasoning afterward, not as a script to memorize.
Also: reliability

What the interviewer is probing

Multi-tenant reliability engineering: the defining challenge is isolation — millions of endpoints with wildly varying health sharing one delivery system — solved with per-tenant queues/concurrency budgets, circuit breakers, and honest contracts (at-least-once, no strict ordering). Developer-experience details (signatures, replay, delivery logs) separate candidates who've operated APIs from those who've only called them.

What a strong answer covers

Must establish

  • Contract first: at-least-once delivery is the only honest promise because exactly-once to an external HTTP endpoint is impossible; events carry unique IDs and merchants must dedupe, treating handlers as idempotent.
  • Per-merchant (or per-endpoint) delivery queues with per-tenant concurrency caps and short timeouts, so a slow or hung endpoint backs up only its own queue and cannot pool-starve workers serving other tenants.
  • No global ordering promised: events carry timestamps and sequence hints, and merchants must reconcile via API reads if order matters.

A strong answer adds

  • Retries use exponential backoff with jitter over a day-scale horizon, giving unhealthy endpoints time to recover without hammering them.
  • A circuit breaker per endpoint moves persistently failing endpoints into probation with reduced attempts, containing damage before the ceiling is reached.
  • After the retry ceiling, events go to a dead-letter store with merchant-visible status, dashboard alerts, and support for manual or automatic replay once the endpoint recovers.
  • HMAC signatures with rotating secrets and timestamped payloads bound replay-attack windows and let merchants verify a webhook is genuinely from the platform.
  • Delivery logs queryable by merchants, showing attempt count, response codes, and latencies, so 'I never got the webhook' is a debugging surface, not just a support ticket.
  • Test-mode events and a replay button are product features, not afterthoughts: deliverability is a shared debugging problem with the merchant.
  • Per-tenant rate limits, payload size caps, and egress protections including SSRF checks on merchant URLs and blocking of internal address space.

Exceptional depth

  • Per-endpoint success and latency SLIs, backlog age alarms, and global versus tenant-scoped dashboards let on-call engineers classify an incident as a platform-wide problem or a single merchant's pathology within seconds.
  • Fairness scheduling across the worker fleet as a distinct mechanism beyond concurrency caps, ensuring no tenant's queue volume can crowd out scheduling time for others.

Common misses

  • Promises exactly-once delivery without acknowledging it is impossible to an external HTTP endpoint.
  • Uses a single shared queue across merchants, leaving no mechanism to contain a slow endpoint's backlog from affecting unrelated tenants.
  • Omits the consumer contract: does not state that merchants must dedupe and make handlers idempotent as the other side of the at-least-once guarantee.
  • Retries without jitter or a ceiling, producing thundering-herd behavior against recovering endpoints and no defined escalation path.
  • Does not frame dead-lettering as a merchant-visible artifact with replay capability, leaving the failure surface invisible to the affected party.
  • Omits HMAC signatures entirely, so merchants have no way to verify a webhook is legitimately from the platform.
  • Promises strict global ordering, which costs more than it is worth and cannot be honestly guaranteed across a distributed delivery system.

The underlying concept

Webhooks invert the usual client-server reliability relationship: the platform becomes a client of millions of servers it doesn't control, so the design center is isolation — one tenant's pathology must be contained by construction (dedicated queues, concurrency budgets, breakers), not by hoping. The contract follows from distributed-systems truth: across an unreliable network with non-transactional receivers, at-least-once + consumer idempotency is the only honest promise, and ordering guarantees cost more than they're worth. Mature platforms treat observability and replay as part of the API: deliverability is a shared debugging problem with the merchant.

Source

Distilled Prep canon — curated from Stripe's public work on webhooks and API platform reliability.

Source: Stripe engineering blog

DS · MLE ·
Editor-written company foundation

You own the fraud-screening model that decides whether to block payments. Where should the blocking threshold sit, how do you measure the fraud you never see, and how do you know the model is actually getting better?

Product case · fraud-detection · payments
Practice against the follow-up probes
  • What does a false positive actually cost here, and to whom?
  • Blocked transactions never reveal whether they were fraud. How do you learn and evaluate under that censoring?
  • Chargebacks arrive weeks late. How does label delay shape training and monitoring?
  • Should the threshold be global, or vary — by what, and why?
  • Fraudsters adapt to the model. What does that do to your evaluation?
Show answer guide
This guide is the map, not the speech. In a real interview, a strong candidate covers perhaps half of this — aloud, imperfectly, recovering when probed. Use it to check your reasoning afterward, not as a script to memorize.
Also: experimentation

What the interviewer is probing

Whether the candidate treats fraud as a decision problem under selective observation: blocking censors the labels (you never learn if a blocked payment was legitimate), losses are asymmetric and merchant-specific, and adversaries shift the distribution. The technical centerpiece is counterfactual evaluation — holdback traffic that lets some risky payments through to keep learning honest.

What a strong answer covers

Must establish

  • Frame the threshold as an economic trade-off: the blocking threshold sits where marginal expected fraud loss equals marginal expected good-revenue loss, per segment. False negative costs include fraud loss, chargeback fees, and network penalties; false positive costs include lost sale, damaged customer relationship, and merchant trust in the platform, and for many merchants the false positive cost dominates.
  • Name the censoring problem: outcomes exist only for approved payments, so training and evaluation on approved-only data biases the model against exactly the region near the threshold. A small randomized holdout/exploration slice where block decisions are relaxed (bounded loss budget) is required to generate ground truth for unbiased evaluation and recalibration.
  • Address label delay: chargebacks arrive over weeks, so training requires delay-adjusted labels using maturation curves, monitoring requires early proxies such as issuer fraud declines and disputes filed calibrated to final outcomes, and cohorts of different ages must never be compared naively.

A strong answer adds

  • Vary the threshold by segment rather than applying a single global cutoff: relevant dimensions include merchant risk tolerance (offer levers), transaction size (asymmetry scales with amount), and segment base rates.
  • Expose expected trade-off curves so that threshold choice is a business decision with visible prices, not a model-internal parameter.
  • Use value-weighted metrics for improvement measurement: dollar-weighted precision and recall, evaluated on exploration traffic rather than the full approved population.
  • Run online experiments where the treatment is the new model, the primary metric is total cost combining fraud loss and false-positive revenue loss, and the guardrail is merchant-level dispersion so no merchant class silently worsens.

Exceptional depth

  • Treat adversarial drift as a structural property: expect the distribution to move because the model improved, so monitor feature-distribution shifts and reserve fast-retrain paths.
  • Treat stable performance as evidence of measurement problems rather than victory, because fraudsters adapting to the model should move the distribution.

Common misses

  • Optimizes on a single global threshold without framing it as an economic trade-off or accounting for segment differences in cost asymmetry.
  • Does not recognize the censoring problem: evaluates model quality using only approved-payment outcomes, ignoring that blocked transactions never reveal whether they were fraud.
  • Ignores label delay: compares cohorts of different label-maturity ages naively or trains without delay-adjusted labels.
  • Treats the threshold as a modeling decision rather than a business policy decision made visible through trade-off curves.

The underlying concept

Fraud modeling is decision-making under selective labels: the policy being evaluated controls which outcomes get observed, so naive evaluation on its own approvals is circular. Exploration — deliberately, boundedly letting the model be overridden — is the price of unbiased learning, the same logic as bandit exploration. Add asymmetric, segment-varying costs and delayed labels, and the mature frame emerges: the model outputs calibrated risk; thresholds are economic policy set on trade-off curves; and evaluation is counterfactual, dollar-weighted, and adversary-aware.

Source

Distilled Prep canon — curated from Stripe's public work on fraud prevention and risk modeling.

Source: Stripe engineering blog

SWE ·
Editor-written company foundation

Design a double-entry ledger service for a global payments platform — the system of record for every money movement.

Systems design · payments · databases
Practice against the follow-up probes
  • Why double-entry at all? What invariant does it buy?
  • A payout was recorded wrong yesterday. How do corrections work if history is immutable?
  • What are the concurrency semantics for posting to a hot account?
  • How do pending vs. settled money and multiple currencies fit the model?
  • How does reconciliation against banks and processors work, and what happens when it disagrees?
Show answer guide
This guide is the map, not the speech. In a real interview, a strong candidate covers perhaps half of this — aloud, imperfectly, recovering when probed. Use it to check your reasoning afterward, not as a script to memorize.
Also: distributed-systems

What the interviewer is probing

Whether the candidate understands ledgers as invariant-enforcing state machines: every transaction's entries sum to zero, history is append-only, and corrections are new entries — never edits. Then the systems reality: hot-account contention, idempotent posting, multi-currency modeling, and reconciliation as the external truth check. Financial correctness culture is exactly what Stripe interviews for.

What a strong answer covers

Must establish

  • Core double-entry model: accounts typed as merchant balance, platform fees, bank settlement, or suspense; transactions containing two or more entries that sum to zero per currency; append-only entries with immutable IDs and timestamps; the zero-sum invariant is checked at commit so money is neither created nor destroyed, only moved.
  • Corrections are reversal entries (contra postings) that reference the original transaction, preserving a complete audit trail; history is never edited, so 'what was the balance believed to be on date X' stays answerable forever.
  • Posting semantics: transactions commit atomically (all entries or none) with idempotency keys, because retries are constant in payments, and serialization per account enforces strict ordering.

A strong answer adds

  • Hot-account contention: the platform fee account is touched by every charge, so the design must shard it into sub-accounts summed on read, or buffer postings through an ordered log applied by a single writer.
  • Balances are materialized from entries via periodic snapshots plus tail replay rather than stored as mutable truth; entries are the truth and balances are a cache.
  • Pending vs. posted states model authorization-vs-capture and settlement lag as first-class concepts in the ledger.
  • Currencies never mix within a single entry; FX is modeled as two legs through a dedicated FX account.
  • Reconciliation: ledger settlement accounts are continuously matched against external bank and processor statements; discrepancies flow into suspense accounts with aging alarms and human workflows, because the design assumes disagreement will happen and makes it visible, bounded, and workable.

Exceptional depth

  • Scale and multi-region: partitioning by account with consensus-replicated partitions.
  • Audit and regulatory requirements shape retention and access design from day one, not as an afterthought.

Common misses

  • Treating corrections as edits to existing entries rather than as new reversal entries, which destroys auditability.
  • Storing balances as mutable state rather than deriving them from the immutable entry log.
  • No handling of hot-account contention, missing that contention engineering is where ledger designs live or die.
  • Mixing currencies within a single entry rather than modeling FX as two legs through an FX account.
  • Treating reconciliation as optional or purely automated rather than designing for inevitable disagreements with suspense accounts and human workflows.

The underlying concept

Double-entry is an ancient invariant-preservation technique: by recording every movement as balanced entries, the system makes "money appeared from nowhere" structurally impossible and turns errors into detectable imbalances. Append-only history converts time into data — corrections become part of the record rather than destruction of it — which is what auditability actually means. The distributed-systems translation: the ledger is a replicated state machine whose transitions are balanced transactions, with idempotency and per-account ordering as the concurrency contract, and reconciliation as the system's external consistency check against the rest of the financial world.

Source

Distilled Prep canon — curated from Stripe's public work on financial infrastructure and ledger design.

Source: Stripe engineering blog