Stripe Software Engineer Interview Prep Guide
Everything Stripe actually asks Software Engineer candidates — concept walkthroughs, worked examples, and the real interview questions, drawn from candidate reports. Free to read.
Last updated

Focus most on Stripe-flavored correctness: money-safe computation, ledger/API system design, stateful event processing, and lifecycle simulations, since you rated Coding & Algorithms and System Design 3/5 and have no solved-category signal yet. Merely review default-solid, non-flagged areas like Luhn, SQL/Python joins, frontend debugging, and behavioral stories rather than spending from-scratch time there. The Stripe-specific emphasis is on financial arithmetic, idempotent API/webhook thinking, ledger consistency, PaymentIntent-style state machines, and pagination/rate-limit tradeoffs. With one month, budget steady weekly practice: roughly two coding sessions, one system-design session, and one short review/mock session per week.
Technical Screen — 39 min
System Design
Focus area — Your System Design rating is 3/5 with high viewing activity; Stripe ledger consistency and idempotency deserve extra space.

What's being tested
Stripe-style system design for a Software Engineer probes whether you can build systems where money-like state and high-volume event-derived state have different correctness requirements. A ledger demands strong consistency, idempotency, auditability, and clear failure semantics; an activity counter often accepts bounded staleness but must handle scale, hot keys, deduplication, and time-windowed aggregation. The interviewer is looking for crisp separation of invariants: what must be linearizable, what can be eventually consistent, and how you recover after retries, crashes, and external dependency failures. Stripe cares because payments infrastructure frequently combines durable financial records with operational counters, limits, fraud signals, and integrations with third-party services.
Core knowledge
-
Ledger systems should be modeled as append-only immutable entries, not mutable balances. Store debits and credits as journal lines with a transaction id, account id, currency, amount, and timestamp; derive balance as , optionally materialized for speed.
-
Double-entry accounting is the core invariant for financial movement: every transaction must balance, typically in a single currency and ledger domain. Enforce this inside one database transaction or equivalent consensus-backed write path.
-
Linearizability matters for operations like “reserve funds,” “capture payment,” or “prevent overdraft.” If two concurrent writes can both observe the same prior balance, you need serializable isolation, conditional writes, row locks, or a consensus store such as
Spanner,FoundationDB, or carefully configuredPostgres. -
Idempotency keys turn client or worker retries into safe operations. Persist
(idempotency_key, operation_type, request_hash, response/result)with a unique constraint; if the same key appears with a different payload, return a conflict rather than silently creating a second ledger movement. -
Outbox pattern is safer than “write database, then publish event.” In the same transaction as the ledger write, insert an outbox row; a relay later publishes to
Kafka,SQS, orPub/Sub. Consumers deduplicate by event id, giving at-least-once delivery without losing ledger facts. -
Distributed transactions should be avoided across internal ledger state and external services. If integrating with a routing, bank, or card-network API, use a state machine:
PENDING → CONFIRMEDorPENDING → FAILED, plus retries, reconciliation jobs, and compensating entries instead of pretending two-phase commit exists across the internet. -
Activity counters are usually derived views over events, not source-of-truth records. A robust design ingests events, deduplicates by event id, partitions by entity and time bucket, and stores aggregates like
(counter_id, bucket_start, granularity, count)inRedis,DynamoDB,Cassandra, orBigtable. -
Tumbling windows use fixed buckets such as one row per minute: bucket = . Sliding windows are computed by summing recent smaller buckets, e.g. last 5 minutes = five 1-minute buckets, trading read amplification for simple writes.
-
Hot-key sharding is necessary when one merchant, location, or activity becomes extremely popular. Write to shards like
(counter_id, bucket, shard_id)whereshard_id = hash(event_id) % S; reads sum acrossSshards. IncreaseSwhen per-key QPS exceeds a partition’s safe write rate. -
Deduplication for counters is bounded by the time horizon. Use a durable event-id table for strict correctness, or a
RedisSET/Bloom filter with TTL for near-real-time counters. Bloom filters reduce memory but introduce false positives, undercounting by roughly the configured false-positive rate. -
Consistency tradeoffs should be explicit. A dashboard counter can tolerate seconds of lag and eventual convergence; an authorization limit or ledger balance usually cannot. Name the SLA: for example, “counter visible within 5 seconds, exact after backfill,” versus “ledger write committed once and visible immediately.”
-
Observability and reconciliation are part of the design, not afterthoughts. Track ingestion lag, dedup hit rate, dropped events, outbox backlog, ledger imbalance count, and reconciliation mismatches. For ledgers, a daily job should recompute balances from entries and alert on any non-zero imbalance.
Worked example
For Design ledger and bikemap integration, a strong candidate should first clarify the invariant: “Is the ledger the source of truth for billable activity, and is the external bikemap service authoritative only for route distance or pricing inputs?” Then declare assumptions: ledger writes must be strongly consistent and auditable; bikemap calls may fail, time out, or return changed routes; users may retry requests. Organize the answer around four pillars: data model, write path, external integration workflow, and reconciliation/observability.
The data model should separate immutable ledger entries from trip or route metadata: a trips table can hold route state, while a ledger_entries table records balanced debits and credits with an idempotency key. The write path should avoid a distributed transaction between the ledger database and bikemap; instead, create a PENDING_ROUTE or PENDING_CHARGE state, call the external service via an async worker, then commit a balanced ledger transaction when the required data is available. A specific tradeoff to flag is synchronous versus asynchronous integration: synchronous gives lower latency for the user if bikemap is healthy, but asynchronous state transitions are more reliable under timeouts and retries. If the external service returns different results on retry, store the request parameters and versioned response used for billing so the ledger remains explainable. Close by saying that, with more time, you would add reconciliation jobs, webhook replay handling if the service is event-driven, and security controls such as signed requests, scoped credentials, and audit logs.
A second angle
For Design a local activity counter service, the same discipline applies, but the correctness bar moves from financial invariants to bounded error and freshness. Instead of designing a serializable double-entry write path, frame the system as event ingestion, deduplication, bucketed aggregation, hot-key mitigation, and query serving. The interviewer may push on tumbling versus sliding windows, so explain that one-minute buckets can serve both hourly tumbling counts and “last 15 minutes” sliding counts by summing recent buckets. Idempotency still matters, but duplicate events affect counts rather than money movement, so you can choose between exact dedup storage and TTL-based approximate dedup depending on SLA. The key transfer is knowing which state is authoritative and which state is a derived, eventually consistent view.
Common pitfalls
Pitfall: Treating counters and ledgers as the same consistency problem.
A tempting answer is “put all events in Kafka and aggregate later,” but that is not sufficient for financial correctness. For a ledger, the committed database record is the source of truth; streams and counters are downstream projections, not the authority for whether money moved.
Pitfall: Hand-waving idempotency as “we’ll retry safely.”
Retries are only safe if the system can recognize the same logical operation after a client timeout, worker crash, or duplicate message. A stronger answer names the unique key, where it is stored, how payload mismatches are handled, and whether the prior response is replayed to the caller.
Pitfall: Over-indexing on technology before invariants.
Jumping straight to Kafka, Redis, and Cassandra can sound scalable but shallow. Start with invariants and access patterns: exact balance checks, append-only audit trails, write QPS, read freshness, window sizes, and hot keys; then choose storage and queues that support those constraints.
Connections
Interviewers often pivot from this area into event sourcing, CQRS, rate limiting, distributed locking, stream processing, and database isolation levels. Be ready to compare Postgres serializable transactions with consensus-backed stores, and to explain when approximate structures like Count-Min Sketch or Bloom filters are acceptable.
Further reading
-
Designing Data-Intensive Applications — especially chapters on transactions, replication, stream processing, and derived data systems.
-
Google Spanner paper — useful for understanding externally consistent distributed transactions and why global linearizability is expensive.
-
Stripe API idempotent requests — concrete example of production-grade idempotency semantics for retried API operations.
Practice questions
Focus area — Bumped for Stripe-specific API/webhook retries, idempotency, external contracts, and failure handling rather than adding a duplicate webhook concept.
What's being tested
Stripe-style integration design tests whether you can build correct internal state while depending on unreliable external APIs. The interviewer is probing for choices around consistency, idempotency, failure isolation, schema boundaries, and API contracts, not just whether you can draw boxes. For a Software Engineer, the core skill is separating what must be strongly correct inside your system, such as a ledger mutation, from what can be retried, cached, degraded, or reconciled when an external service fails. Stripe cares because payment systems routinely integrate with banks, networks, risk providers, tax engines, maps, schedulers, and notification vendors where duplicate calls, partial failures, and stale data can directly affect money movement or user trust.
Core knowledge
-
Idempotency keys are mandatory for any operation that may be retried after timeout, connection reset, client crash, or
5xx. Store{idempotency_key, request_hash, response, status}with a uniqueness constraint; if the same key arrives with a different payload, return409 Conflict. -
Strong consistency should be scoped narrowly to the system of record. For financial flows, keep double-entry ledger writes in
`Postgres`or another transactional store usingSERIALIZABLEisolation, row-level locks, or append-only journal entries; do not couple correctness to an external routing or mapping API. -
Double-entry accounting models every movement as balanced debits and credits: . Per transaction, enforce this invariant at write time. Append-only entries plus derived balances are safer than mutable balance rows alone, because they support audit, replay, and reconciliation.
-
External service calls should usually sit outside database transactions. Holding a transaction open while calling
BikeMapor a notification provider risks lock contention, transaction timeouts, and ambiguous commits. Prefer reserve/commit state machines, outbox pattern, or asynchronous workers. -
Outbox pattern means writing the internal state change and an outbound task in the same local transaction, then having a worker deliver the external call. This avoids the “database committed but API call never happened” gap without requiring distributed transactions across systems.
-
Retry policy needs bounded exponential backoff with jitter; for example,
delay = min(base * 2^attempt, max_delay) + random_jitter. Retry429,500,502,503, and network timeouts; do not blindly retry semantic errors like400,401, or validation failures. -
Timeouts and circuit breakers protect your service from dependency collapse. Set client timeouts below your own
`p99`latency budget, cap concurrent calls with bulkheads, and open a circuit after sustained failures so requests degrade quickly instead of exhausting threads or connection pools. -
API contract design should distinguish canonical internal models from provider-specific DTOs. Keep a translation layer around
BikeMap,Twilio,SendGrid, or calendar APIs so provider fields, error codes, pagination, and version changes do not leak throughout your domain model. -
Consistency models must be explicit. A user-facing route estimate can be eventually consistent or cached with a TTL, while a ledger entry must be linearizable or at least serializable within an account. Stronger consistency increases latency and coordination cost, so apply it only where invariants demand it.
-
Calendar and schedule integration requires precise time semantics. Store instants in
UTC, preserve the user’s IANA timezone likeAmerica/Los_Angeles, and expand recurring rules usingRFC 5545semantics. Daylight saving transitions create nonexistent or duplicated local times that must be defined. -
Conflict resolution should be deterministic and explainable. For notifications, conflicts may be handled by priority, quiet hours, deduplication window, or “latest user preference wins.” For money movement, avoid “last write wins”; use optimistic concurrency with version checks or append-only commands.
-
Capacity planning should be back-of-the-envelope and tied to bottlenecks. If users each have schedules and generate reminders/day, notification volume is approximately QPS on average, with spikes around local morning hours requiring queue smoothing.
Worked example
For Design ledger and bikemap integration, a strong candidate first clarifies the domain boundary: “Is the ledger authoritative for payments or balances, and is BikeMap only used to compute route pricing, ETA, or validation?” They should also ask whether route data can be stale, whether prices are quoted before ledger commit, and what happens if the map provider is unavailable. A clean answer can be organized around four pillars: the ledger data model, the external integration boundary, the transaction/state machine, and failure handling.
The ledger design should be append-only, double-entry, and backed by a transactional store such as `Postgres`, with unique idempotency keys per client request. The BikeMap call should be wrapped behind an internal RouteService adapter that normalizes provider responses, applies timeouts, caches route estimates when allowed, and records provider request IDs for debugging. The key tradeoff to flag is whether to call BikeMap synchronously before ledger commit or asynchronously after: synchronous gives immediate pricing validation but adds latency and dependency risk, while asynchronous improves resilience but requires quote reservation or later adjustment. A robust design often computes or validates the route before creating a pending ledger transaction, then commits only the balanced ledger entries under a local transaction. If the provider times out after the client retries, idempotency ensures the same request does not create duplicate ledger movements. To close, say that with more time you would add reconciliation jobs, audit tooling, provider failover strategy, and integration tests with fault injection for timeout, duplicate response, and stale route scenarios.
A second angle
For Generate user notifications from schedules, the same integration principles apply, but the correctness boundary shifts from financial invariants to time, preference, and delivery semantics. The internal scheduler should decide what notification is due based on user profile, timezone, locale, recurrence rules, and quiet hours before calling an external push, email, or SMS provider. Unlike a ledger, duplicate delivery may be tolerable only within strict deduplication windows, so idempotency keys should be based on {user_id, schedule_occurrence_id, channel}. The external provider call belongs behind an adapter with retries, rate-limit handling, and delivery status mapping. The tricky design questions are around daylight saving time, recurring event expansion, user preference changes between scheduling and send time, and whether the system promises “exactly once” delivery or more realistically “at least once with deduplication.”
Common pitfalls
Pitfall: Treating external APIs as if they are reliable local function calls.
A tempting answer is “call BikeMap, then write the ledger, then return success,” without discussing timeouts, retries, or ambiguous outcomes. A stronger answer names the failure matrix: provider call succeeds but response is lost, database commit succeeds but worker crashes, client retries after timeout, and provider returns inconsistent errors.
Pitfall: Overusing distributed transactions.
Candidates sometimes propose two-phase commit across your database and the external service. That is usually impractical because most third-party APIs do not participate in 2PC, and even when possible it harms availability. Prefer local transactions plus idempotent APIs, outbox, sagas, reconciliation, and explicit pending/committed/failed states.
Pitfall: Ignoring domain-specific edge cases.
For notifications, saying “store the timestamp and send at that time” misses timezone, locale, recurrence, DST, and user preference updates. For ledgers, saying “update the balance” misses append-only auditability, balanced entries, concurrency control, and duplicate request protection. Interviewers reward candidates who surface these edge cases before being prompted.
Connections
Interviewers may pivot from this topic into distributed transactions, event-driven architecture, rate limiting, webhook design, or database isolation levels. For Stripe specifically, also be ready to discuss idempotent API design, ledger reconciliation, API versioning, and observability through structured logs, correlation IDs, metrics, and traces.
Further reading
-
Stripe API Idempotent Requests — practical reference for request replay safety and idempotency-key behavior.
-
Designing Data-Intensive Applications — strong foundation for consistency, transactions, replication, and fault tolerance tradeoffs.
-
RFC 5545: Internet Calendaring and Scheduling Core Object Specification — canonical reference for recurrence rules and calendar edge cases.
Practice questions
Focus area — Company-specific addendum: practice modeling payment states, confirmation, failure, cancellation, retries, and client/server responsibilities.
What's being tested
Interviewers are probing your ability to design, reason about, and implement a robust PaymentIntent lifecycle: a deterministic state machine that survives partial failures, concurrency, and unreliable external payment providers. They want to see you model states/transitions, enforce idempotency, handle webhooks and retries, and choose persistence and concurrency controls that guarantee correctness at scale. At Stripe, money correctness and operational simplicity are primary — the interviewer checks that you can make safe tradeoffs (durability vs latency, coordination vs eventual consistency) and explain them.
Core knowledge
-
PaymentIntent states: Typical states include requires_confirmation, requires_payment_method, requires_action (e.g.,
3DS), processing, succeeded, canceled, and requires_capture; transitions must be deterministic and single-sourced of truth. -
State machine modeling: Persist either a snapshot plus version or an append-only event stream; enforce transitions with explicit guards (allowed-from → allowed-to) and a canonical transition table to avoid ad-hoc state mutations.
-
Idempotency: Use the idempotency-key pattern (client-supplied key stored with result) so repeated API calls return the same outcome; store request fingerprint, response, and final state in the same transactional write.
-
At-least-once delivery & webhooks: External PSPs and webhooks are at-least-once; design handlers to be idempotent, use a dead-letter queue for manual reconciliation, and treat webhooks as eventual signals, not authoritative commands unless corroborated.
-
Concurrency control: Prefer optimistic locking (version number or
SELECT ... FOR UPDATEdepending on latency) for typical workloads; fallback to pessimistic locks for rare high-contention paths (e.g., capture vs cancel racing). -
External systems & compensation: You cannot use two-phase commit with external PSPs; implement the saga pattern with compensating actions (refunds, reversal) and clear semantics for partial success/failure.
-
Retries, backoff, and timeouts: Use exponential backoff, bounded retries (e.g., 3–5 attempts configurable), and a TTL-based escalation for long-running
processingstates to avoid indefinite waits. -
Eventual consistency & reconciliation: Expect divergence (network partitions, PSP unknowns); build periodic reconciliation jobs that compare authoritative PSP reports to internal state and produce compensating workflows.
-
Durability and storage choices: Use a transactional store like
Postgresfor canonical intent state and small writes; useKafkaor an event log for async processing, andRedisfor short-lived locks/caches only. -
Observability & SLAs: Instrument
p99latency for state transitions, webhook delivery success rate, and reconciliation lead time; alert on increases in ambiguous/held intents. -
Ambiguous outcome handling: Define an explicit ambiguous state for intents where the provider response is unknown; surface to operators and run automated reconciliation rather than guessing.
-
Security & audit: Keep an immutable audit trail (event log) for all state transitions and idempotency-key usage to support disputes and forensics.
Worked example
(Designing a PaymentIntent state machine for modern card flows) First 30s: clarify the scope — do you need to support 3DS, captures, partial refunds, multiple PSPs, and at-least-once webhooks? State assumptions: single merchant tenant model, Postgres as canonical store, external PSPs are unreliable. Skeleton answer pillars: (1) Define canonical states and allowed transitions, including an ambiguous terminal; (2) Persistence model: row with state, version, idempotency_key, and append-only event stream for audit; (3) Concurrency: optimistic locking with version check and retry loop; (4) Integration: idempotent webhook handlers and async workers that reconcile. Tradeoff to flag: using optimistic locking keeps high throughput but can cause retries under contention — consider partitioning by merchant to reduce collisions. Close: mention operational work — build reconciliation flights, simulate PSP failures in tests, and add dashboards; if more time, model multi-PSP routing and cost-based failover.
A second angle
(Reconciling ambiguous payments after PSP timeouts) Frame this as a reliability & operations task: define the conditions that make an intent ambiguous (no PSP ack within timeout, network error during capture). Strategy pillars: (1) mark intent ambiguous with timestamp and retry window; (2) trigger automated queries to PSP transaction history and escalate to manual review if unresolved after N hours; (3) for user-visible flows, surface a clear UX status (e.g., "Payment processing — do not retry") and provide an idempotency-safe retry API. Constraint differences: here you spend more design on reconciliation batchers, idempotent lookups against PSP APIs, and operator tooling; throughput and latency matter less than accuracy and auditability.
Common pitfalls
Pitfall: Modeling side effects inside transitions.
Engineers often mix external side effects (call PSP, send webhook) inside the same transaction that mutates state; when the side effect fails after commit you have an inconsistency. Better: commit the state change first, emit an event into a durable queue, and process side effects asynchronously with idempotent handlers.
Pitfall: Relying on exactly-once semantics.
Assumingexactly-oncedelivery from PSPs or webhooks leads to brittle designs; instead assume at-least-once and make all operations idempotent. Explain this assumption early in the interview.
Pitfall: Not defining an
ambiguouspath.
A common shallow answer ignores ambiguous/out-of-band failures; failing to define how to surface and reconcile those cases is a depth mistake. Define escalation, TTLs, and operator-visible signals.
Connections
Interviewers can pivot to adjacent problems like distributed transactions (compare sagas vs 2PC), event sourcing and CQRS for auditability, or designing high-throughput webhook delivery and retry systems (backpressure, DLQs, and exponential backoff policies).
Further reading
-
Designing Data-Intensive Applications — Martin Kleppmann — deep discussion of event sourcing, consistency models, and sagas.
-
[Patterns for Payment Systems — various industry posts] — look for posts on idempotency and webhook handling for practical patterns and failure case studies.
Practice questions
Focus area — Company-specific addendum: prepare for practical Stripe API tradeoffs around list traversal, object expansion, backoff, and client ergonomics.
What's being tested
Candidates must show practical mastery of designing and using paginated REST APIs, the expansion pattern for reducing round-trips, and robust handling of rate limits in client and server designs. Interviewers probe system correctness (consistency and ordering), performance (latency and throughput), operational safety (retry/backoff, idempotency), and tradeoffs between simplicity and scale. At Stripe, this maps to reliable developer UX for high-volume clients and safe multi-tenant service operation.
Core knowledge
-
Pagination: know difference between cursor-based and offset-based pagination; cursor is stable and O(1) per page while offset degrades with large offsets (O(offset) in many DBs). Use cursor when N >> page_size.
-
Cursor semantics: a cursor should be an opaque, compact token (e.g., base64 of last-seen key + shard id + timestamp); it must encode sorting key and tie-breaker to guarantee deterministic paging.
-
Offset pitfalls:
OFFSETinPostgres/MySQLscans/skips rows; for deep pages preferWHERE (key, id) > (k, id) LIMIT Mindexed scans or keyset pagination. -
Consistent pagination: decide between snapshot isolation (stable view, heavier — e.g., timestamped snapshot or transaction id) and eventual view (reflects concurrent writes). Be explicit in API docs about staleness guarantees.
-
Expansion pattern: expansion (e.g.,
expand=customerinStripe API) inlines related resources to avoid N+1 client fetches; server must fetch efficiently (batch queries,IN (...)), enforce size limits, and document extra cost. -
Rate limiting models: know token bucket (smooth bursts) vs leaky bucket (steady rate). Implement counters in
Redisfor distributed enforcement and use sliding-window or fixed-window with jitter to reduce thundering herd. -
Client behavior on 429s: honor
Retry-Afterheader; use exponential backoff with jitter (e.g., base * 2^n + random) and respect idempotency for retries. Surface backoff info to users or observability. -
Idempotency: design write APIs to accept an idempotency key (e.g., UUID) and persist the result for at least the time window clients may retry; consider storage TTL and eviction semantics.
-
Operational headers & telemetry: expose
X-RateLimit-Limit/Remaining/Resetor similar; logp99latency, error rates, and 429 counts per client and per route to tune quotas. -
Performance tradeoffs: precomputing denormalized lists (cache) speeds page responses for heavy reads but complicates consistency; measure reads per second and eviction cost before denormalizing.
-
Security & quotas: apply per-API-key, per-account, and per-IP quotas. Differentiate authentication tiers (e.g., test vs live) and implement graceful degradation (soft limits, informative 429 payloads).
-
Backwards compatibility: expanding fields changes response size; version responses and cap total payload size to avoid breaking mobile clients.
Worked example — "Design a paginated API for listing transactions"
Start by clarifying semantics: ask whether ordering must be strictly stable (e.g., by created_at, id) and whether the client expects a consistent snapshot across pages. Declare assumptions: sort by created_at DESC, id DESC, eventual consistency acceptable, and page size default 50.
Organize the solution into pillars: API contract (query parameters limit, cursor; return fields data, has_more, next_cursor); storage access (use keyset pagination: WHERE (created_at,id) < (:cursor_created_at, :cursor_id) ORDER BY created_at DESC, id DESC LIMIT :limit with an index on (created_at, id)); and operational concerns (rate limiting, caching). Explain scaling: for low-latency, read-through Redis cache per-account for most recent pages; for deep scans use index-only scans or a streaming background process to materialize per-account lists.
Flag tradeoffs: cursor tokens are safe and efficient but require clients to hold state; snapshot guarantees require storing a snapshot id or using MVCC and increase complexity. For heavy multi-tenant load, per-account cursor state persisted in Redis reduces recomputation but needs eviction rules.
Close: note follow-ups — if more time, discuss prefetching next page, partial responses with expand controls, and monitoring metrics like page p99 and 429 rates to tune per-account quotas.
A second angle — "Implement a client library that handles rate limits and expansions"
Here the constraint is client-side: limited memory (mobile) and intermittent connectivity. The same concepts apply but framing shifts: the client should expose a high-level retry policy using exponential backoff with jitter, queue user-initiated requests and persist idempotency keys to local storage, and support lazy expansion (only expand when caller requests). For bandwidth constraints use If-None-Match/ETag or request small pages first and allow server-side expand hints. The client must surface Retry-After to the caller and offer cancellation hooks; on unstable networks, prefer idempotent operations and exponential backoff with capped retries.
Common pitfalls
Pitfall: Choosing
OFFSETpagination for large datasets.
UsingOFFSETfor deep pages leads to high DB CPU and unpredictable latency; interviewers will prefer keyset (cursor) pagination and ask how you handle ties.
Pitfall: Ignoring expansion cost and payload size.
Blindly allowing unlimited expansions can produce large responses and backend N+1 queries; always cap expansions server-side and batch related-fetches (SELECT ... WHERE id IN (...)).
Pitfall: Retrying non-idempotent writes without idempotency keys.
A tempting quick fix is naive retries on 5xx/429; a stronger answer explicitly requires idempotency keys and explains TTL/cleanup and semantic guarantees.
Connections
This area commonly pivots to API versioning (how changes to expanded fields affect clients), distributed caching (cache invalidation strategies for paginated lists), and observability/alerting (instrumenting 429 spikes and per-client throttling metrics). Interviewers may also ask about database indexing and query plans when you justify keyset vs offset.
Further reading
-
Stripe API Reference — Pagination and Expanding Objects — canonical patterns for cursor and expansion usage in a production API.
-
RFC 7231 (HTTP/1.1 Semantics) — authoritative guidance on status codes like
429andRetry-After.
Practice questions
Coding & Algorithms
Money-Safe Financial Computation
Focus areaFocus area — Stripe interviews heavily reward integer-money correctness, rounding discipline, and rejection rules; emphasize despite default-solid concept status.

What's being tested
These problems test money-safe stateful computation: applying ordered events, pricing rules, validation, and aggregation without floating-point surprises. Interviewers look for clean data modeling, deterministic edge-case handling, and fixed-point arithmetic that preserves cents, currencies, and balances.
Patterns & templates
-
Fixed-point integer arithmetic — store money in minor units like cents; compute
fee = amount * bps // 10000with explicit rounding policy. -
Composite-key maps — use
(account_id, currency),(country, payment_method), or(item_type, tier)as dictionary keys; expectO(1)lookup. -
Sort-then-apply event processing — sort by
timestampplus stable tie-breaker; update balances sequentially inO(n log n)time. -
Configuration-driven calculators — encode tiers, discounts, tax, and fee rules as data; keep calculation logic generic and testable.
-
Boundary-safe tier loops — calculate
units_in_tier = min(remaining, tier_limit - prev_limit); watch inclusive vs exclusive thresholds. -
Validation-first pipelines — reject missing currency, negative quantity, unknown rate, malformed account, or unsupported tier before mutating state.
-
Deterministic output ordering — sort results by account, currency, item name, or timestamp as specified; never rely on dictionary iteration.
Common pitfalls
Pitfall: Using
floatfor prices, tax, or fees; binary rounding errors can produce incorrect cents and flaky tests.
Pitfall: Aggregating before validation; invalid transactions must not partially update balances, totals, or platform overdraft coverage.
Pitfall: Treating all currencies as interchangeable; balances, fees, and totals usually aggregate per currency unless conversion rates are explicitly provided.
Practice these
The practice cards below cover the canonical variants — solve all of them and time yourself.
Practice questions
Central to Stripe-style event processing, but no shaky/new flag; practice deterministic state updates and ordering.

What's being tested
These problems test stateful stream processing: applying events in deterministic time order while maintaining per-entity state such as balances, locks, fraud counters, schedules, or notification queues. Interviewers look for clean data structures, explicit ordering rules, and edge-case handling around time, retries, conflicts, and mutations.
Patterns & templates
-
Sort-then-scan event streams —
events.sort(key=(ts, tie_breaker)), then single pass; usuallyO(n log n)time,O(k)state. -
Per-key state maps — use
dict[(account_id, currency)] -> balanceordict[user_id] -> schedule; initialize defaults carefully. -
Priority queue scheduler —
heapqby(next_run_at, stable_id)for due jobs; push updated recurrence after processing. -
Interval / TTL rules — store active rules by
start <= ts < end; expire with min-heaps or ordered scans to avoid stale decisions. -
LRU eviction — combine
dictplus doubly linked list orOrderedDict;get,put, and eviction areO(1). -
Deterministic conflict resolution — define tie-breakers for same timestamp: cancellation before creation, rule update before auth, rejection before overdraft.
-
Timezone-safe scheduling — convert user-local wall time with
zoneinfo; store canonical UTC instants; handle DST gaps and duplicated hours explicitly.
Common pitfalls
Pitfall: Processing input order as truth instead of sorting by timestamp produces wrong balances, fraud decisions, and email order.
Pitfall: Treating recurring schedules as precomputed infinite lists can explode memory; generate the next occurrence lazily.
Pitfall: Ignoring idempotency or duplicate event IDs leads to double-charged balances, duplicate notifications, or repeated lock acquisition.
Practice these
The practice cards below cover the canonical variants — solve all of them and time yourself.
Practice questions
Coding self-rating is 3/5 with no solved signal, so keep graph modeling in the regular rotation.

What's being tested
You’re being tested on graph modeling: turning relations like similarity, exchange rates, or roads into nodes, edges, weights, and traversal rules. Interviewers look for correct algorithm choice, clean data structures, edge-case handling, and clear complexity reasoning.
Patterns & templates
-
Adjacency list with
`dict[node] -> list[(neighbor, weight)]`— best default for sparse graphs; build inO(E)space. -
DFS/BFS traversal for connected components or reachability —
O(V+E)time; trackvisitedbefore enqueueing to avoid cycles. -
Weighted path accumulation for ratios/conversions — multiply edge weights along a path; add reciprocal edges for bidirectional rates.
-
Dijkstra’s algorithm for nonnegative route costs —
O((V+E) log V)withheapq; don’t use plain BFS on weighted edges. -
Topological sort for DAG dependency chains —
O(V+E)using indegree queue; detect cycles when processed count< V. -
Union-Find for grouping linked records — near-constant amortized
find()/union(); useful when edges imply equivalence components. -
Consistency checking in weighted graphs — compare implied versus supplied ratios with epsilon tolerance; floating-point equality is unsafe.
Common pitfalls
Pitfall: Treating every graph as unweighted; bicycle routes and conversion rates require preserving edge costs or multiplicative weights.
Pitfall: Forgetting disconnected components; queries may involve two nodes with no valid path.
Pitfall: Overengineering dynamic updates; first solve the static graph correctly, then discuss incremental rebuilds or cached components.
Practice these
The practice cards below cover the canonical variants — solve all of them and time yourself.
Practice questions
- Load Balancing And Resource Lifecycle Simulation — covered in depth under Take-home Project below.
Take-home Project — 5 min
Coding & Algorithms
Appears in both screen and take-home; practice clean state structures, lifecycle transitions, and deterministic routing behavior.

What's being tested
These problems test stateful simulation with deterministic load-balancing behavior: routing, locking, lifecycle transitions, eviction, and shutdown handling. Interviewers are probing whether you can turn ambiguous product rules into clean data structures, preserve invariants, and handle edge cases without overcomplicating the code.
Patterns & templates
-
Entity state maps — use
dict[id] -> statefor connections, accounts, or targets; most operations should beO(1)average time. -
Round-robin pointer — maintain
next_index; after choosing a target, advance modulon, skipping unavailable or full targets carefully. -
Capacity-aware routing — track
load[target]and reject or reroute whenload[target] == capacity[target]; define deterministic tie-breaking upfront. -
Sticky assignment — keep
connection_id -> target; duplicateconnectshould usually return existing assignment, not consume capacity again. -
Lifecycle transitions — model
connect,disconnect,shutdown,lock,unlock, andevictas explicit state changes with invariant checks. -
LRU cache template — combine
dictplus doubly linked list orOrderedDict;get/put/evict areO(1). -
Ordered rerouting on shutdown — collect affected connections in stable order, free old capacity, then reassign deterministically to avoid hidden ordering bugs.
Common pitfalls
Pitfall: Treating duplicate connects as new connections, which silently inflates load and breaks capacity invariants.
Pitfall: Updating the round-robin pointer on failed assignment; clarify whether failed attempts should advance before coding.
Pitfall: Handling shutdown by deleting a server first, then losing the list or order of connections that must be rerouted.
Practice these
The practice cards below cover the canonical variants — solve all of them and time yourself.
Practice questions
Luhn Checksum
Light reviewLight review — Default-solid and narrow take-home topic; keep to a quick parity/checksum refresher and one implementation drill.

What's being tested
These prompts test checksum implementation, string validation, and bounded combinatorial search around payment-card numbers. Interviewers look for clean isValidLuhn, brand/network rule handling, wildcard completion, and single-digit recovery without brute-forcing unnecessarily.
Patterns & templates
-
Luhn checksum — scan right-to-left, double every second digit, subtract
9if over9; valid whensum % 10 == 0. -
Check digit derivation — compute partial sum excluding final digit, then
check = (10 - partial % 10) % 10; avoid trialing0..9. -
Network validation — model card brands as
{name, lengths, prefixes}; validate digits, length, prefix, then checksum in a predictable order. -
Wildcard enumeration — for small
?counts, DFS over digits; for larger counts, use modulo-DP over positions with statesum_mod_10. -
Counting completions — dynamic program
dp[i][mod]inO(n * 10 * choices)time andO(10)space; apply brand constraints first. -
Single-error correction — try replacing each digit with
0..9except itself, then re-run format and checksum; complexityO(n * 10 * n)naïvely, reducible with precomputed contributions. -
Input hygiene — normalize spaces/dashes only if allowed; otherwise reject non-digits early and keep leading zeros meaningful.
Common pitfalls
Pitfall: Doubling parity is based on distance from the right, not absolute index; parity changes when length changes.
Pitfall: Passing Luhn alone is not enough; a number can satisfy the checksum but fail brand prefix or length rules.
Pitfall: Wildcard brute force explodes at
10^k; switch to modulo counting whenkis more than a few digits.
Practice these
The practice cards below cover the canonical variants — solve all of them and time yourself.
Practice questions