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

Your biggest focus is system design: you self-rated it 1/5, selected API/data/caching/sharding/jobs/concurrency/payment topics, and called out a distributed-systems-heavy onsite. You're not signaling a gap in CSV parsing, graph routing, Luhn validation, or frontend behavior, so those stay normal/brief review rather than from-scratch study. For Stripe, this plan highlights payment ledgers, idempotent API/webhook-style integrations, concurrency-safe payments, sharding, caching, event delivery, and distributed job execution. With 1–3 months, bias weekly study time toward onsite system design drills while keeping coding warm with interval, simulation, graph, and parsing practice.
Technical Screen — 71 min
Coding & Algorithms
-
CSV Parsing, Validation, And Joins — covered in depth under Online Assessment below.
-
Time Windows, Scheduling, And Notifications — covered in depth under Online Assessment below.
Software Engineering Fundamentals
-
Payment Ledgers, Reconciliation, And State Machines (Focus) — covered in depth under Onsite below.
-
Transaction Rule Engines And Expression Parsing — covered in depth under Onsite below.
-
Python Requests HTTP 307 Stream Replay — covered in depth under Onsite below.
System Design
-
Reliable API Integration And Distributed Design (Focus) — covered in depth under Onsite below.
-
Concurrency Control And Transactional Integrity In Payments (Focus) — covered in depth under Onsite below.
-
Sharding And Partitioning For Payment Data (Focus) — covered in depth under Onsite below.
-
Distributed Job Scheduling And Exactly-Once Workflows (Focus) — covered in depth under Onsite below.
-
Caching And Cache Invalidation For Payment APIs (Focus) — covered in depth under Onsite below.
-
Real-Time Event Delivery And WebSockets (Focus) — covered in depth under Onsite below.
Onsite — 59 min
Coding & Algorithms
-
CSV Parsing, Validation, And Joins — covered in depth under Online Assessment below.
-
Time Windows, Scheduling, And Notifications — covered in depth under Online Assessment below.
Software Engineering Fundamentals
Focus area — You selected payment systems and payment ledger; for Stripe, ledger correctness and deterministic state transitions are core.

What's being tested
These problems test whether you can model money movement as explicit state transitions rather than as ad hoc mutations to a balance. You need to preserve monetary precision, deterministic ordering, idempotency, and invariants while handling rejection, retries, cancellation, capture, overdraft, and settlement. Stripe cares because a small correctness bug in a ledger can create duplicated funds, missing funds, or unreconcilable records; interviewers are probing both implementation discipline and your ability to explain tradeoffs clearly.
Core knowledge
-
Integer fixed-point money stores the smallest currency unit, such as cents, rather than floating-point values. For a percentage fee, calculate with integer arithmetic and document rounding:
fee = floor(amount * rate / denominator)or the explicitly specified half-up rule. -
A ledger should be append-oriented: record immutable entries such as debit, credit, fee, reversal, and settlement, then derive balances. A mutable balance cache improves reads, but it must be reproducible from entries and updated atomically with the authoritative write.
-
Use a state machine with enumerated states and an explicit transition table. For example,
accepted -> captured -> settled, whileaccepted -> canceledmay be valid; reject illegal transitions such assettled -> canceledunless a separate reversal operation exists. -
Keep acceptance, authorization or limit usage, and settlement distinct. A payment can be accepted by business validation, consume an account limit, and remain unsettled; collapsing these into one boolean makes refunds, retries, and reconciliation ambiguous.
-
Idempotency means repeating the same logical command has the same effect as applying it once. Key commands by a stable operation or idempotency key, persist the result, and reject reuse with different payloads rather than silently applying a second mutation.
-
Define ordering precisely: sort by timestamp only when timestamps are guaranteed unique; otherwise use
(timestamp, sequence_number)or another stable tie-breaker. Never rely on input order unless the contract explicitly guarantees it. -
Maintain invariants such as and, for a double-entry ledger, total debits equaling total credits. Assert these during tests and expose violations rather than repairing balances silently.
-
Reconciliation compares two independently produced views, such as internal ledger entries and processor settlement records. Match on stable identifiers, amount, currency, and lifecycle status; classify unmatched, duplicated, amount-mismatched, and timing-difference cases for deterministic remediation.
-
Process each account-currency pair independently. A balance for
USDmust not offset a balance forEUR, and an account identifier alone is insufficient as a key; use a composite key such as(account_id, currency). -
For overdrafts, make the coverage rule explicit: consume the account’s available balance first, then debit a platform or reserve account for the deficit, or reject atomically. Preserve the invariant that the resulting account balance and platform coverage reflect the same accepted transaction.
-
Transactional atomicity matters whenever a command changes multiple records. In
Postgres, a database transaction with row-level locking can protect a balance update; optimistic concurrency with a version column can work when conflicts are expected to be low. -
Parsing is part of correctness: preserve empty fixed-position fields, validate field count and types before indexing, and separate syntax errors from semantic rejection. A parser should produce a typed command; the state machine should decide whether that command is legal.
Worked example — Design a Ledger with Separate Acceptance, Limit, and Settlement States
For “Design a Ledger with Separate Acceptance, Limit, and Settlement States,” I would first clarify the command grammar, whether deposits can be retried, whether timestamps determine ordering, and whether query output must reflect accepted commands only or settled funds. I would state that amounts are integer minor units, account balances are keyed by (account_id, currency), and commands are processed deterministically in the specified order. I would organize the solution around four pillars: parsing and validation, account initialization, explicit acceptance/limit/settlement state, and balance-query generation. I would represent each command’s lifecycle separately rather than using one status, because acceptance can succeed while settlement is delayed or limit usage remains active. I would also define the invariant that rejected commands have no financial side effects and that repeated command identifiers cannot create additional balance. A key tradeoff is whether to store only current balances or append immutable ledger entries plus a balance projection; the latter is easier to audit and replay, while the former is simpler and faster for a small coding exercise. I would explain atomic updates if acceptance changes both an account and a platform limit, using a transaction or a version check. If I had more time, I would add property-based tests for illegal transitions, duplicate commands, zero or negative amounts, currency isolation, and replaying the same input to obtain identical results.
A second angle
For “Design a Fixed-Position Payment Command State Machine,” the same principles apply, but the primary risk shifts from aggregation to parsing and transition legality. I would first preserve empty fields and validate exact field positions, then convert the parsed record into a typed command before applying business rules. The state table should distinguish approval, capture, cancellation, and retry, including whether a retry is idempotent or represents a new attempt. The important transfer is that every mutation needs a clear precondition, postcondition, and duplicate-command behavior, even when no balance is changed yet.
Common pitfalls
Analytical mistake: Treating a payment status as a single linear enum such as pending -> paid -> failed is tempting, but it cannot represent independent authorization, limit, and settlement dimensions. Use separate state or ledger concepts and document which transitions affect funds, limits, and reporting.
Pitfall: Using floating-point arithmetic or silently rounding fees can produce reconciliation differences of one or more minor units.
Communication mistake: Jumping directly into maps and loops without clarifying ordering, retries, currencies, or rejection semantics makes a correct implementation look accidental. State assumptions explicitly, write the invariants on a whiteboard, and call out what the prompt does not specify.
Depth mistake: Saying “wrap it in a database transaction” is not enough. Explain which records are locked or versioned, how duplicate commands are detected, what happens after a timeout, and how immutable entries or audit data let operators reconstruct the result.
Connections
An interviewer may pivot to double-entry bookkeeping, idempotency keys, optimistic concurrency control, or event sourcing and projections. They may also ask how to test invariants with property-based testing or how to diagnose a reconciliation break without mutating historical records.
Practice questions
Relevant to Stripe fraud and policy logic, but not explicitly selected as a system-design gap.
What's being tested
These exercises test whether you can turn a small rule language into a reliable execution pipeline: tokenization, parsing, validation, and evaluation. The interviewer is probing whether you preserve precise Boolean semantics—especially left-to-right evaluation, operator precedence, nested parentheses, and short-circuiting—rather than relying on ad hoc string splitting. Stripe cares because transaction and fraud decisions must be deterministic, explainable, safe on malformed input, and efficient under high request volume.
Core knowledge
-
Lexing converts input text into tokens such as identifiers, string literals, Boolean literals, comparison operators,
AND,OR, and parentheses. Scan left to right and reject unknown characters, unterminated quotes, and malformed operators before evaluation. -
Recursive-descent parsing maps grammar productions directly to functions such as
parse_or(),parse_and(),parse_primary(), andparse_comparison(). A standard grammar givesANDhigher precedence thanOR:expr := or_expr,or_expr := and_expr (OR and_expr)*. -
Abstract syntax trees (ASTs) separate syntax from execution. Represent a comparison as
Comparison(field, operator, value)and Boolean composition asBinary(op, left, right); this makes validation, testing, explanation, and later optimization straightforward. -
Operator precedence must be explicit rather than accidental. Parentheses override precedence, so
A OR B AND CmeansA OR (B AND C), while(A OR B) AND Chas a different AST and result. -
If the specification says chains are evaluated strictly left to right, implement that semantics directly; do not silently apply conventional precedence. A fold such as
result = result AND next_valuepreserves left associativity, while parentheses require recursive grouping. -
Short-circuit evaluation avoids unnecessary work:
false AND Xreturnsfalse, andtrue OR Xreturnstrue. It also prevents unsafe field access or function calls on branches whose result cannot affect the outcome. -
Type checking distinguishes a missing field, a field with the wrong type, and a valid comparison that evaluates to false. For example, comparing a Boolean
is_internationalwith the string"true"should generally be a malformed rule, not an ordinary transaction rejection. -
Separate syntax errors, semantic errors, and business outcomes. An unbalanced parenthesis or unknown field is invalid input; a valid rule whose condition is false is a normal non-match; an ordered rule list may then continue to the next rule.
-
For ordered accept/block rules, define precedence and termination explicitly: evaluate in list order, decide whether the first matching rule wins, and state the default when no rule matches. Return both the decision and useful metadata such as rule ID or reason.
-
Shunting-yard parsing is a viable alternative for a larger operator set. It converts infix tokens to postfix using an operator stack, typically in time and space; recursive descent is usually clearer for a small fixed grammar.
-
Use an immutable or controlled evaluation context containing transaction attributes rather than dynamically evaluating arbitrary source code. Never call
eval; a rule language must whitelist fields, operators, literals, and any callable functions. -
Complexity is generally per rule expression, where is token count and is visited AST size; short-circuiting can reduce evaluation work. Cache parsed ASTs by rule version or canonical text when many transactions reuse the same rules.
Worked example: Build a Transaction Rule Engine with AND/OR Chains and Nested Parentheses
A strong candidate first asks whether operators use conventional precedence or the explicitly stated left-to-right behavior, what fields and types are legal, and whether malformed rules should throw errors or return a structured validation result. They then declare assumptions about quoting, case sensitivity, missing attributes, and the default decision when no rule matches. The answer should be organized around four pillars: a tokenizer, a parser or AST builder, an evaluator with short-circuit semantics, and validation/error handling. For a small grammar, they would likely choose recursive descent with separate functions for grouped expressions, comparisons, AND, and OR. They would test isolated comparisons, empty or malformed input, deeply nested parentheses, mixed operators, missing fields, and expressions where short-circuiting skips an invalid branch. The key design decision to flag is whether to honor ordinary precedence or the prompt's strict left-to-right rule; silently choosing one can produce systematically wrong transaction decisions. They should return a structured result such as {status, decision, error} rather than conflating parser failure with a false predicate. If they had more time, they would add property-based tests comparing the parser and evaluator against a small reference interpreter, plus rule-versioned AST caching.
A second angle
Evaluate Ordered Accept/Block Transaction Rules with AND, OR and Parentheses
Here the central concern shifts from evaluating one expression to composing multiple policies with ordered rule resolution. Clarify whether the first matching accept or block rule wins, whether later rules can override it, and what happens when every rule misses. Keep parsing and evaluation independent from policy selection: each rule produces a match or non-match, then a separate resolver applies ordering and default behavior. This separation makes it easier to attach an audit reason and test precedence without embedding list-order logic inside the expression parser. In a production service, rule versions should be immutable during one decision so a transaction cannot observe a partially updated policy set.
Common pitfalls
Pitfall: Treating malformed input as a normal false result hides configuration defects. Return a distinct error category for unknown fields, invalid literals, and unbalanced parentheses, while reserving false for a valid predicate that does not match.
A tempting implementation uses expression.split("AND") and split("OR"). That breaks quoted values, nested parentheses, repeated whitespace, operator precedence, and field names containing operator-like text; a tokenizer plus grammar is the more reliable answer.
Candidates sometimes describe a parser but never state the language contract. Explicitly name associativity, precedence, supported types, missing-field behavior, default policy behavior, and whether evaluation is fail-closed; those details are where production correctness lives.
Connections
The interviewer may pivot to finite-state machines for parsing, property-based testing for expression equivalence, and versioned configuration rollout for safely deploying rule changes. Stateful fraud authorization processing also connects evaluation to event ordering, idempotency, time windows, and deterministic replay.
Practice questions
API reliability debugging is adjacent to your system-design gap; focus on replay safety and minimal tests.
What's being tested
These debugging questions test tracing HTTP 307 redirect semantics through Python requests, especially preserving the method and replaying a streamed request body. You must reason from bytes on the wire—stream position, Content-Length, socket reads, and timeout behavior—then propose a minimal, robust fix for seekable versus non-replayable bodies.
Patterns & templates
-
307 preserves method and body: the redirected request remains
POST; unlike303, it must resend the original payload to the new URL. -
Seekable stream replay: record the initial position with
body.tell(), then callbody.seek(position)before retrying or redirect replay. -
requestsmay rewind using its internal body-position metadata; verifyPreparedRequest._body_positionand inspectrequests/urllib3behavior rather than assuming automatic replay. -
Content-Lengthframing must match bytes actually available; a reused stream at EOF can advertiseNbytes while sending zero, causing the server to wait. -
Prefer a bytes/string body for small payloads: it is naturally replayable; use a reopened file or seekable object for large uploads.
-
Treat generators and pipes as non-replayable unless buffered; retrying them can duplicate, truncate, or silently alter the payload.
-
Debug with a short
timeout, request/response logging,tell()before and after sends, and a local redirect server; distinguish client hang from server processing.
Common pitfalls
Pitfall: Saying “307 changes the request to
GET” confuses it with303;307preserves both method and body.
Pitfall: Rewinding only after the timeout is insufficient if the first request already consumed the stream; restore the position before the redirected send.
Pitfall: Recommending blind retries ignores non-seekable bodies and duplicate side effects; use idempotency keys or explicit buffering where appropriate.
Practice these
The practice cards below cover the canonical variants — solve all of them and time yourself.
Practice questions
System Design
Focus area — System Design is 1/5 and you selected API design; apply this to Stripe idempotency, retries, and webhook-like flows.

What's being tested
These problems test whether you can turn an unreliable, partially specified API workflow into a correct distributed system with explicit contracts, state transitions, and recovery behavior. The interviewer is probing idempotency, consistency, concurrency control, failure isolation, and the ability to distinguish your service’s guarantees from those of an external dependency. Stripe cares because payments, ledgers, feature controls, counters, and third-party integrations must remain correct when requests are retried, duplicated, delayed, reordered, or only partially completed.
Core knowledge
-
Contract-first API design starts by defining request and response schemas, validation rules, coordinate or currency units, authentication, timeouts, pagination, and error semantics before choosing storage or infrastructure. State assumptions explicitly when the prompt leaves behavior unspecified.
-
Idempotency keys let a client safely retry a mutation: persist
(account_id, idempotency_key, request_hash, status, response)and return the original result for an identical request. Reject reuse with a different payload rather than silently applying a second operation. -
Retry policy must distinguish transient failures (
429, connection resets, many5xx) from permanent failures (400, authorization errors, malformed input). Use exponential backoff with jitter, bounded attempts, and a deadline; retries without an idempotency key can duplicate side effects. -
Timeouts and circuit breakers prevent a slow dependency from consuming every worker. Set connect, read, and overall deadlines; trip a circuit after measured failures, serve a safe fallback where possible, and use half-open probes to recover gradually.
-
Exactly-once effects are usually implemented as at-least-once delivery plus deduplication, not promised by the network. A unique database constraint, request log, or inbox table converts duplicate deliveries into one durable state transition.
-
Linearizability means each operation appears to occur atomically at one point between invocation and response. For a single-winner dispatch acceptance, use a conditional update such as
UPDATE ... WHERE status = 'open', then verify affected-row count. -
Consistency choices should follow the invariant. A financial ledger and winner selection need strong consistency; a feature-flag read path may tolerate bounded staleness if rollout safety is preserved. State whether the system offers read-after-write, eventual, or monotonic-read guarantees.
-
Transactional boundaries matter when combining local writes with external APIs. A local database transaction cannot atomically commit with a routing provider; use an outbox, durable workflow state, reconciliation, and compensating actions rather than claiming distributed atomicity.
-
Outbox/inbox patterns persist an event alongside the business mutation, then publish asynchronously and deduplicate consumption. Include a stable event ID, versioned schema, retry state, dead-letter handling, and replay-safe handlers.
-
Hot-key mitigation is essential for counters and popular feature flags. Shard a frequently updated key across buckets, write to one bucket per event, and read by summing buckets; this trades write contention for read amplification and requires a defined time-window strategy.
-
Geospatial matching commonly uses an index such as
geohash,S2, orH3to query nearby cells before calculating exact distance with the Haversine formula. Expand neighboring cells near boundaries and define freshness, radius, and tie-breaking rules. -
Observability and security should expose request IDs, idempotency keys, dependency latency, retry counts, circuit state, and invariant violations without logging secrets or payment data. Track
p50,p95, andp99separately; averages hide tail failures that trigger cascading retries.
Worked example: Design ledger and bikemap integration
In the first 30 seconds, I would clarify the ledger’s currency and precision, whether debits must be strictly ordered, the routing provider’s latency and quota limits, acceptable stale route data, and whether a successful response means “ledger committed” or “route obtained.” I would declare that ledger mutations require strong consistency, while route lookup is an external, fallible read that must not corrupt balances. I would organize the answer around the ledger data model and invariants, the idempotent mutation API, the routing-adapter boundary, and asynchronous recovery and reconciliation. For the ledger, I would use integer minor units, immutable double-entry records, balance checks, and a unique idempotency key enforced transactionally in Postgres. For the bikemap call, I would apply a short timeout, bounded jittered retries for transient errors, caching keyed by normalized coordinates, and a circuit breaker. The central tradeoff is whether to block the user-visible operation until routing succeeds or commit the financial operation first and calculate the route asynchronously; I would choose based on whether the route is essential to the business invariant. I would explicitly explain that a local transaction cannot provide atomic commit with the provider, so an outbox and reconciliation job are safer than a two-phase commit assumption. I would close by saying that, with more time, I would specify provider failover, quota handling, audit retention, and property-based tests for duplicate and reordered requests.
A second angle
Design a Feature Flag Service with Percentage Rollouts and Allow/Block Lists uses the same reliability principles but shifts the primary constraint from transactional money movement to low-latency, highly available reads. Evaluation should be deterministic: hash a stable subject and flag key, map the result to a bucket, then apply precedence such as block list, allow list, explicit targeting, percentage rollout, and default. A stale cached configuration may be safer than failing closed or open, but the choice must be explicit for each flag’s risk level. Configuration writes need versioning, audit history, propagation monitoring, and idempotent update requests.
Common pitfalls
Pitfall: Saying “use retries for reliability” without specifying idempotency, retryable status codes, deadlines, backoff, or duplicate suppression leaves the most dangerous failure mode unanswered. Tie every retry policy to a particular side effect and invariant.
Pitfall: Jumping directly to
Kafka, sharding, or microservices before clarifying correctness and scale makes the design feel like infrastructure shopping. Start with API semantics, state transitions, throughput, latency, and failure guarantees; then choose components that support them.
Pitfall: Claiming a distributed transaction makes a ledger update and third-party API call atomic is incorrect. Explain the unavoidable partial-failure window and provide an outbox, workflow state machine, reconciliation, and compensating behavior.
Connections
An interviewer may pivot to sagas, outbox/inbox delivery, leader election, multi-region consistency, or property-based testing of invariants. They may also ask how to design rate limits, webhook verification, dead-letter recovery, or safe schema evolution without breaking old clients.
Further reading
-
Stripe: Idempotent requests — concrete treatment of safely retrying API mutations.
-
Designing Data-Intensive Applications by Martin Kleppmann — strong coverage of replication, consistency, transactions, and distributed failure modes.
-
Pat Helland, “Life Beyond Distributed Transactions” — explains practical alternatives to global atomic transactions.
Practice questions
Focus area — You selected concurrency and transactional integrity; drill double-spend prevention, isolation, locking, idempotency keys, and ledger atomicity.

What's being tested
This topic tests whether you can preserve transactional integrity when payment requests are retried, duplicated, reordered, or processed concurrently. You should be able to combine database constraints, transaction isolation, idempotency, state-machine design, and asynchronous recovery rather than relying on a single “lock” or “exactly once” claim. Stripe cares because a race can create duplicate charges, lost refunds, incorrect balances, or an irreversible financial inconsistency. The interviewer is probing whether your design remains correct when clients time out, workers crash, and multiple requests target the same payment simultaneously.
Core knowledge
-
Idempotency keys let a client safely retry an operation after a timeout. Store
(merchant_id, idempotency_key)with the normalized request, result, and terminal status; a duplicate key must return the original result, while a reused key with different parameters should fail. -
Database uniqueness constraints are the final arbiter for duplicate creation. A unique index such as
(account_id, external_payment_id)prevents two application servers from creating the same logical payment, even if both read “not found” before either writes. -
Transactions must cover the invariant being protected, not merely individual statements. For example, reserving available balance requires reading the balance, validating it, and updating it in one transaction; otherwise concurrent withdrawals can both observe stale funds.
-
Isolation levels trade correctness against concurrency.
READ COMMITTEDpermits some write-skew patterns;REPEATABLE READprevents many rereads from changing but is database-specific;SERIALIZABLEgives the strongest model, often by aborting conflicting transactions that callers must retry. -
Row-level locks such as
SELECT ... FOR UPDATEserialize updates to a known payment or ledger account. Lock a deterministic set of rows and keep the transaction short; inconsistent lock ordering can create deadlocks, which should be detected and retried safely. -
A payment should use an explicit state machine, for example
requires_action → processing → succeededorprocessing → failed. Define legal transitions and make each transition conditional on the current version or state, preventing a late failure callback from overwriting a later success. -
Optimistic concurrency control stores a version or compares the expected state in the update:
UPDATE payments SET ... , version = version + 1 WHERE id = ? AND version = ?. If zero rows change, reload and resolve the conflict rather than silently overwriting another worker. -
External processors make true distributed transactions unavailable. Commit local intent first, call the processor with a stable processor-side idempotency key, then persist the response; a timeout means “unknown outcome,” not automatic permission to issue a second charge.
-
The outbox pattern atomically writes domain state and an outbound event into the same database transaction. A publisher later delivers the event, potentially more than once, so consumers need their own deduplication key or idempotent state transition.
-
Exactly-once processing is generally not an end-to-end property across a database, network, and payment processor. Design for at-least-once delivery plus idempotent handlers, durable correlation IDs, bounded retries, dead-letter handling, and reconciliation for permanently ambiguous cases.
-
Ledger entries should be immutable and balanced rather than repeatedly mutating a single “current balance” as the only record. For double-entry accounting, every transaction satisfies ; derived balances can be rebuilt and audited from the ledger.
-
Failure recovery must distinguish retryable from terminal errors. Deadlocks, connection resets, and transient
5xxresponses may be retried with exponential backoff and jitter; invalid payment details or a confirmed decline should transition deterministically and stop retrying.
Worked example
Representative prompt: “Design an idempotent payment creation API.”
In the first 30 seconds, I would clarify whether “create” authorizes funds, captures them immediately, or only creates an intent, and I would ask about retry behavior, consistency requirements, processor semantics, and acceptable latency. I would state that clients may retry after any timeout and that the system must never create two charges for one merchant operation. I would organize the answer around the API contract and idempotency record, the payment state machine, the database transaction and uniqueness constraints, and the processor-integration and recovery path. The request would atomically create or load an idempotency record and payment intent, while a stable processor key would make processor retries safe. A worker or request handler would use conditional state transitions such as processing → succeeded, and webhooks would be deduplicated and applied only through legal transitions. I would explicitly choose durable READ COMMITTED transactions plus unique constraints and conditional updates over holding a database lock across a slow processor call. I would explain that a processor timeout leaves the payment in processing or unknown, followed by status polling or reconciliation rather than an immediate second charge. If I had more time, I would specify retention and abuse limits for idempotency keys, observability, reconciliation jobs, and multi-region ownership rules.
A second angle
Representative prompt: “Prevent two concurrent refunds from exceeding the captured payment.”
The framing changes from duplicate request creation to enforcing a cumulative invariant: successful refunds must never exceed the captured amount. I would serialize refund allocation with a row lock or optimistic version check, calculate captured - refunded - pending_refunds, and insert the refund only if sufficient capacity remains. The external refund call would not occur inside the database transaction; instead, the transaction records an authorized refund intent and an outbox event. Retries and processor callbacks would use a refund-specific idempotency key, while a reconciliation process handles an unknown processor result.
Common pitfalls
Pitfall: Saying “we use a lock, so duplicates are impossible” is incomplete. A process crash can occur after the external charge but before the lock-protected database commit, and a lock cannot span an unreliable third-party call; use durable intent, idempotency, and reconciliation.
Pitfall: Treating a request ID as automatically idempotent misses the replay contract. Explain where the key is scoped, how parameters are compared, how the original response is stored, what happens during an in-progress duplicate, and when records expire.
Pitfall: Claiming “exactly once” without naming the failure boundary is a depth mistake. State which components provide at-least-once delivery, which operations are deduplicated, how ambiguous outcomes are resolved, and which invariant is enforced by the database.
Connections
An interviewer may pivot to double-entry ledgers, outbox and inbox processing, distributed sagas, database isolation levels, or reconciliation systems. They may also ask how multi-region writes, webhook ordering, deadlocks, and schema migrations affect the same invariants.
Further reading
-
Stripe: Idempotent requests — concrete API semantics for safely retrying payment-related requests.
-
Martin Kleppmann, Designing Data-Intensive Applications, Chapters 7–9 — strong treatment of transactions, consistency, replication, and distributed-system failure modes.
-
Pat Helland, “Life Beyond Distributed Transactions” — useful perspective on business-level transactions and recovery across service boundaries.
Practice questions
Focus area — You explicitly selected sharding and partitioning; Stripe discussions often need tenant, account, and ledger partition choices.

What's being tested
This topic tests whether you can design a payment-data system that scales horizontally without weakening correctness, isolation, or operability. The interviewer is probing your ability to choose a partition key, route requests, handle cross-shard queries, and reason about resharding and failures. Stripe cares because payment operations are high-value, retry-prone, and correctness-sensitive: duplicate charges, lost state transitions, or inconsistent reads are materially worse than an ordinary web-service failure.
Core knowledge
-
Sharding distributes rows across independent database instances, while partitioning divides data within a logical database. A useful design separates the logical data model from physical placement so shards can be moved, split, or replicated without changing API semantics.
-
Choose a key based on dominant access patterns and isolation boundaries. For merchant-owned payment objects,
merchant_idoften provides tenant locality; for globally queried objects, hashpayment_intent_idto distribute load. Never assume one key serves every query efficiently. -
Hash partitioning gives relatively even distribution:
shard = hash(key) mod N, though consistent hashing or a virtual-node ring reduces movement whenNchanges. Range partitioning supports ordered scans and time-based retention but risks hot ranges, especially for monotonically increasing IDs. -
A good shard key should offer high cardinality, stable ownership, predictable routing, and resistance to hotspots. Avoid status, currency, or country alone: their low cardinality creates concentrated partitions. A merchant with unusually high volume may require dedicated shards or subpartitioning.
-
Preserve idempotency at the application boundary and storage layer. An idempotency key should map to the original request result and be uniquely constrained within the correct scope, such as
(merchant_id, idempotency_key); retries must not create a second payment attempt. -
Model payment state transitions as guarded writes, not read-then-write logic. For example, update only when
status = 'requires_confirmation', check the affected-row count, and use a transaction or compare-and-set operation to prevent concurrent transitions from charging twice. -
Replication improves read capacity and availability but introduces replica lag. Route correctness-critical reads—such as immediately checking whether a capture succeeded—to the primary or use a session consistency token; stale replicas are acceptable only for explicitly tolerant views.
-
Cross-shard transactions are expensive and often unavailable. Prefer a local transaction plus an outbox record, then process asynchronous effects idempotently. If an operation spans payment state, ledger state, and notifications, define recovery and reconciliation rather than assuming distributed two-phase commit.
-
Secondary indexes must be co-located with the data they index or implemented as deliberately maintained lookup tables. A global index across shards creates a coordination bottleneck; a denormalized index should include the target shard and tolerate stale entries with repair or fallback lookup.
-
Resharding needs a migration protocol: dual-read or dual-write, backfill existing rows, verify counts/checksums, switch routing, and retain rollback capability. Virtual shards make movement incremental; production traffic must continue safely during copying and cutover.
-
Use consistent routing metadata rather than embedding shard arithmetic throughout application code. A directory can map tenant or virtual-shard IDs to physical databases, with cached reads, versioned assignments, and a safe behavior when metadata is unavailable.
-
Design observability around shard-level signals:
p99latency, error rate, replication lag, transaction conflicts, storage growth, queue age, and load skew. Alert on the hottest shard and on correctness symptoms such as duplicate idempotency conflicts or reconciliation mismatches.
Worked example
Design a sharded payment-data store is a representative design prompt for this topic. In the first 30 seconds, clarify the required operations—create, retrieve by ID, list by merchant and time range, update state, and reconcile—and ask about scale, latency, durability, and whether cross-merchant queries are required. I would declare that payment writes require strong correctness, retries are expected, and the primary lookup is by payment ID while merchant-scoped listing is also common. I would organize the answer around four pillars: data model and invariants, shard-key and routing strategy, consistency/idempotency, and migration/operations. A practical design could hash payment IDs for even distribution while maintaining a merchant-local index for listing, or use merchant-based placement if tenant isolation and locality dominate. I would explicitly flag the tradeoff: merchant-based sharding simplifies tenant queries but allows a large platform to create a hot shard, whereas payment-ID hashing balances load but makes merchant listings more complex. I would close by discussing replica reads, outbox-driven secondary indexes, resharding, and reconciliation; if I had more time, I would quantify capacity per shard and walk through a failed cutover.
A second angle
A different framing is designing a merchant activity feed or “list payments by customer and date” query over the same data. The correctness requirement for the primary payment record remains strong, but the read model can be eventually consistent if it is clearly separated from authorization or capture decisions. The main challenge shifts from write serialization to index design, pagination, and avoiding scatter-gather queries across every shard. A cursor containing the last sort key plus a stable tie-breaker is safer than offset pagination while records are being added concurrently.
Common pitfalls
Analytical mistake: Choosing merchant_id automatically because payments are merchant-owned ignores skew and payment-ID lookups. A stronger answer names the access patterns, estimates distribution, and explains whether large merchants receive dedicated capacity or a second-level partition.
Pitfall: “Hash everything by payment ID, then scan all shards for merchant history” sounds scalable for writes but produces unacceptable read amplification.
Communication mistake: Jumping directly into Kafka, databases, and hash functions without stating invariants makes the design difficult to evaluate. Lead with the operations, correctness requirements, scale assumptions, and the failure that must never happen—such as two successful charges for one idempotent request.
Depth mistake: Saying “use a transaction” does not explain what it protects across shards or retries. Specify the transaction boundary, guarded state transition, idempotency constraint, outbox behavior, and reconciliation path when an external side effect succeeds but the process crashes.
Connections
The interviewer may pivot to idempotency keys, leader/replica consistency, distributed transactions, outbox patterns, or consistent hashing. They may also ask how a ledger differs from mutable payment metadata: financial records generally require append-only, auditable invariants rather than ordinary row updates.
Further reading
-
Designing Data-Intensive Applications — Chapters on partitioning, replication, transactions, and stream processing provide the core distributed-systems vocabulary.
-
Stripe Engineering: Idempotency — Explains why retry-safe APIs matter for payment operations and how idempotent request handling shapes system design.
-
Google Spanner: “Spanner: Google’s Globally-Distributed Database” — Useful for understanding strongly consistent distributed storage and the cost of global coordination.
Practice questions
Focus area — You selected distributed job scheduling and called out a distributed-heavy onsite; practice leases, retries, and idempotent workers.

What's being tested
This topic tests whether you can design a distributed scheduler that remains correct when workers crash, messages duplicate, machines partition, and requests are retried. The interviewer is probing your understanding of durable state, leases, idempotency, retry semantics, and the limits of “exactly once” execution. Stripe cares because payment and financial workflows must avoid both duplicate side effects and silently lost work while operating at high availability and scale.
Core knowledge
-
Exactly-once processing is not generally achievable across independent systems without coordination: a worker can commit an external charge and crash before recording completion. Prefer effectively-once side effects through durable idempotency keys and downstream deduplication.
-
Separate job state from execution attempts:
PENDING,CLAIMED,RUNNING,SUCCEEDED,RETRYABLE_FAILURE, andDEAD_LETTER. Store attempt number, lease expiry, next-run time, error details, and a stable workflow or task identifier. -
Use leases rather than permanent ownership. A worker atomically claims eligible work with a compare-and-swap or transaction, renews periodically, and loses ownership when
lease_until < now; stale workers must be rejected by a fencing token. -
A fencing token is a monotonically increasing claim version attached to every write. Downstream storage accepts updates only from the newest token, preventing a paused worker from committing after another worker has taken over.
-
Use a durable database such as
`Postgres`for authoritative state, with an index like(status, next_run_at)andSELECT ... FOR UPDATE SKIP LOCKEDfor moderate-scale polling. At very high throughput, partition queues by shard, tenant, or time bucket. -
Treat queue delivery as at-least-once: acknowledgments can be lost, consumers can crash after processing, and brokers can redeliver. A queue such as
`Kafka`or`SQS`improves dispatch, but it does not itself provide exactly-once business effects. -
Make every handler idempotent using a deterministic key such as
(workflow_id, step_id, attempt-independent operation). Record the key and result atomically in an inbox or side-effect table; repeated requests return the original result rather than repeating the operation. -
Implement retries with exponential backoff and jitter, for example . Classify errors as transient, permanent, or unknown; cap attempts, use a dead-letter path, and avoid retry storms during dependencies’ outages.
-
Workflow progression needs a durable state machine. A step completion should transactionally record its output and make dependent steps runnable; use an outbox pattern when publishing a dispatch event must be atomic with the database transition.
-
Scheduling requires explicit time semantics: define UTC or a canonical clock, handle daylight-saving changes, and decide whether cron means wall-clock time or fixed intervals. Avoid relying on local machine time for correctness; monitor clock skew and use server-side timestamps.
-
Design for backpressure and fairness. Bound per-tenant concurrency, reserve capacity for high-priority work, enforce workflow timeouts, and use weighted fair queuing so one customer or runaway workflow cannot exhaust all workers.
-
Failure recovery must be observable and repairable: expose queue depth, oldest runnable age, claim latency, execution latency, retry counts, lease expirations, stuck workflows, and dead-letter volume. Preserve correlation IDs and provide safe replay or operator cancellation.
Worked example — Design a distributed job scheduler with exactly-once workflow execution
A strong candidate first clarifies whether “exactly once” means one worker attempt or one externally visible business effect, what latency and throughput are required, and whether downstream APIs support idempotency keys. They would state an assumption such as: the scheduler may deliver a task multiple times, but each payment-related side effect must be effectively once. The answer can be organized around four pillars: durable workflow state, lease-based claiming with fencing, idempotent handlers, and recovery/observability. They might store workflow and task rows in `Postgres`, atomically claim runnable tasks, publish work through an outbox, and let workers renew short leases while executing. The central tradeoff is between synchronous transactional coordination, which improves correctness but increases coupling and latency, and asynchronous at-least-once delivery, which scales better but requires idempotent consumers. They should explicitly explain the crash window after an external API call and before local acknowledgment, then show how a stable idempotency key makes the retry safe. They close by noting that, with more time, they would quantify shard capacity, model multi-region failover, and design replay and reconciliation tooling.
A second angle — Recovering a workflow after worker and region failure
The same principles apply to a workflow engine that executes several dependent steps, but the emphasis shifts from dispatch throughput to recovery and consistency. The candidate should explain how a coordinator reconstructs runnable work from durable task state rather than trusting in-memory queues. They should address split-brain coordinators with leader epochs or fencing tokens, and define whether failover can duplicate attempts. A strong design includes reconciliation that scans for expired leases, verifies downstream idempotency records, and advances or retries tasks safely. The key framing difference is recovery correctness: availability matters, but a failover must not cause a financial step to execute twice.
Common pitfalls
Pitfall: Claiming that a transactional queue or
`Kafka`“guarantees exactly once” end to end. Broker-level exactly-once semantics do not cover an external payment API, email provider, or database unless the side effect participates in the same coordination protocol.
Pitfall: Saying “the worker marks the job complete after processing” without defining the crash window, ownership rule, or duplicate behavior. Explain the atomic state transition, lease expiry, fencing, and idempotency key explicitly.
Pitfall: Focusing only on the happy path and naming technologies without failure reasoning. Walk through worker crash before acknowledgment, worker pause past lease expiry, duplicate delivery, database outage, poison messages, and downstream timeout; these cases reveal whether the design is actually robust.
Connections
An interviewer may pivot to leader election, transactional outbox/inbox patterns, consistent hashing, backpressure, or multi-region consistency. They may also ask how to test failure windows using fault injection, deterministic clocks, and property-based tests.
Further reading
-
Designing Data-Intensive Applications — Martin Kleppmann’s treatment of replication, transactions, stream processing, and distributed-system guarantees.
-
“Exactly-Once Semantics in Apache Kafka” — useful for distinguishing broker processing guarantees from end-to-end side-effect guarantees.
-
“Idempotency Keys” — Stripe API documentation — concrete application of safe retries for externally visible operations.
Practice questions
Focus area — You selected caching; emphasize correctness boundaries, stale reads, cache keys, invalidation, and when not to cache payment data.

What's being tested
This topic tests whether you can design a cache for read-heavy payment APIs without returning incorrect authorization, payment, or balance state. The interviewer is probing your understanding of cache invalidation, consistency guarantees, idempotency, failure handling, and distributed coordination—not merely whether you know Redis commands. Stripe cares because stale payment data can cause duplicate actions, incorrect merchant decisions, or customer-visible contradictions, so a strong Software Engineer makes correctness boundaries explicit before optimizing latency.
Core knowledge
-
Cache-aside is usually the safest default: read from
Redis, fetch from the source of truth on a miss, then populate the cache. Writes update the database first and invalidate afterward; never treat cache population as the durable write. -
Cache invalidation must be tied to every state transition, not only the primary update path. Payment objects may change through synchronous requests, webhook processing, retries, asynchronous workers, administrative tools, or reconciliation jobs.
-
Choose a consistency model explicitly: strong consistency for authorization or capture decisions, bounded staleness for dashboards, and eventual consistency for search-like views. A cache hit is acceptable only if its freshness matches the endpoint’s correctness requirement.
-
Use versioned values or monotonic timestamps to prevent an older asynchronous writer from overwriting newer data. A cached record can contain
version; accept an update only whenincoming_version >= cached_version. -
Delete-on-write is simpler than write-through for mutable payment state: commit the database transaction, then remove the key. If invalidation fails, a TTL limits damage, while a durable invalidation stream or retry queue improves recovery.
-
TTL is a safety net, not a correctness proof. If stale data is unacceptable for an operation, bypass the cache or re-read the authoritative store; randomize TTLs with jitter to avoid synchronized expiration and load spikes.
-
Prevent a cache stampede when a hot key expires: use request coalescing, a short distributed lock, probabilistic early refresh, or stale-while-revalidate. Locks require bounded leases and ownership checks so a delayed client cannot delete another client’s lock.
-
Negative caching can reduce repeated misses for nonexistent objects, but it must use short TTLs and careful invalidation. Otherwise, a newly created payment or customer can remain invisible until expiration.
-
Scope keys by tenant and authorization context, such as
account:{id}:payment:{payment_id}:v:{schema}. Never cache a response containing fields that differ by caller unless the key includes the relevant permission or the response is safely filtered. -
Idempotency keys protect repeated mutations; caches do not. A cached
POSTresponse must not be confused with durable idempotency storage, which needs atomic persistence, request-parameter validation, and a defined replay policy. -
Treat
Redisas an optimization, not the source of truth. During outages, decide whether to fail closed, serve stale reads, or bypass the cache based on endpoint semantics; monitor hit rate, origin load, stale-read indicators, eviction rate, andp99latency. -
In a multi-region deployment, distinguish replication lag from cache staleness. Region-local caches can show older state after a write elsewhere, so route read-after-write traffic to the writer, use version checks, or expose an explicit consistency token.
Worked example
Representative question: “Design a cache for retrieving PaymentIntent objects through a payment API.” In the first 30 seconds, I would clarify whether reads influence payment mutations, the required read-after-write behavior, the object size and traffic skew, tenant isolation, and whether updates arrive through webhooks or background workers. I would state an assumption: cache-aside reads are acceptable for dashboards, but mutation preconditions must read authoritative state. I would organize the answer around key design and authorization, read/write and invalidation flows, failure and stampede protection, and observability. I would use Redis for cache-aside reads, commit changes to the primary database first, then invalidate account:{id}:payment_intent:{id} with retries backed by a durable event or outbox. The explicit tradeoff is whether to delete immediately or write the new value: deletion avoids publishing a partially transformed object, while write-through can reduce misses but creates more ordering and failure cases. I would address concurrent updates with object versions and reject stale cache writes. If I had more time, I would specify multi-region behavior, load-test hot merchants, and define alerts for stale-read evidence and invalidation backlog.
A second angle
Representative question: “How would you cache a payment status endpoint while preventing stale authorization decisions?” The framing changes because the endpoint’s response may directly influence whether a capture, refund, or fulfillment action is allowed. I would permit cached data for display but require a conditional authoritative read for the mutation, using an object version or If-Match-style precondition to detect changes. The main challenge becomes separating presentation latency from correctness-critical state transitions, rather than maximizing the general cache hit rate. I would also discuss whether a short-lived “processing” state can be served stale while terminal states require stronger guarantees.
Common pitfalls
Pitfall: “Set a five-minute TTL and the problem is solved” is analytically insufficient; TTL bounds staleness but does not prevent an incorrect decision during those five minutes. Tie freshness to endpoint semantics and invalidate on every mutation path.
Pitfall: A candidate may describe only the happy path—database write followed by cache delete—without explaining invalidation failure, concurrent writers, worker retries, or regional lag. Strong communication names the failure modes and gives each one a concrete recovery or safety policy.
Pitfall: Saying “use a distributed lock” without lease expiry, ownership tokens, or a fallback path shows shallow depth. Explain how the lock prevents stampedes, how a crashed holder releases safely, and why the system remains correct if the lock service is unavailable.
Connections
An interviewer may pivot to idempotency, outbox patterns, optimistic concurrency control, webhook ordering, rate limiting, or multi-region replication. Be ready to distinguish caching from durable deduplication and to explain how database transactions and event delivery determine whether invalidation is reliable.
Further reading
-
Designing Data-Intensive Applications — chapters on replication, consistency, and derived data provide the foundation for reasoning about stale cached state.
-
Caching at Netflix: EVCache — a practical example of distributed caching, failure isolation, and operational tradeoffs.
-
Stripe: Idempotent Requests — useful for separating mutation safety from read caching and understanding replay semantics.
Practice questions
Real-Time Event Delivery And WebSockets
Focus areaFocus area — You selected real-time messaging; focus on ordering, fanout, reconnects, backpressure, and delivery guarantees.

What's being tested
This topic tests whether you can design a reliable, scalable real-time delivery system rather than merely open a socket and push bytes. The interviewer is probing your understanding of connection lifecycle, event ordering, reconnect behavior, delivery guarantees, backpressure, fanout, and horizontal scaling. Stripe cares because payment state changes must reach dashboards, merchants, and internal tools promptly without exposing customers to duplicated, missing, unauthorized, or misleading events.
Core knowledge
-
WebSockets provide a long-lived, bidirectional connection after an HTTP upgrade, but they do not provide durable delivery, replay, ordering across producers, or automatic recovery. The application must define those semantics explicitly.
-
Separate the durable event log from the connection-serving layer. Persist events in systems such as
`Kafka`,`Postgres`, or an event store, then use WebSocket workers to deliver them; a process crash must not erase undelivered state. -
Use a per-stream or per-tenant monotonic sequence number. Clients can acknowledge sequence
s, reconnect withlast_seen=s, and request events wheresequence > s; globally ordered delivery is usually unnecessary and expensive. -
Choose delivery semantics deliberately: at-most-once minimizes duplicates but can lose events, while at-least-once permits retries and duplicates. Exactly-once presentation generally requires client-side deduplication using stable event IDs.
-
Design reconnect as a normal path, not an exception. The client should use exponential backoff with jitter, authenticate again when needed, send its last acknowledged cursor, and perform a bounded replay before resuming live delivery.
-
A connection registry maps a logical subscription to active connection workers. In a multi-instance deployment, route events through shared infrastructure such as
`Kafka`pub/sub, Redis Streams, or a broker; never assume an in-memory registry is globally complete. -
Control fanout explicitly. A high-volume event may target many subscribers, so batch serialization, share immutable payloads, coalesce replaceable updates, and apply per-tenant quotas rather than creating unbounded work per recipient.
-
Implement backpressure with bounded per-connection queues and explicit overflow behavior. Slow clients can be disconnected and forced to replay, or lower-priority updates can be dropped; unbounded buffers eventually exhaust memory.
-
Define ordering at the narrowest useful scope: per account, resource, or subscription. Partitioning by that key preserves order while allowing parallelism; asynchronous handlers, retries, and multiple producers can otherwise reorder events.
-
Use heartbeats to detect half-open TCP connections. A ping/pong interval must be shorter than infrastructure idle timeouts, while server-side write deadlines and connection limits prevent dead peers from consuming resources indefinitely.
-
Treat authorization as a subscription-time and event-time concern. Validate the tenant, resource scope, and token claims when subscribing; avoid trusting client-provided channel names, and re-check revocation or permission changes for long-lived sessions.
-
Make events versioned and self-describing: include event ID, type, schema version, resource identifier, sequence, creation time, and tenant context. Consumers should tolerate additive fields and reject or quarantine incompatible versions safely.
Worked example
No interview title was supplied, so consider the representative prompt: “Design a real-time event delivery service using WebSockets.” In the first 30 seconds, clarify event volume, concurrent connections, required latency, tenant isolation, replay duration, ordering scope, and whether missing events are acceptable. State an assumption such as 500,000 concurrent connections, at-least-once delivery, per-account ordering, and a seven-day replay window. Organize the answer around four pillars: durable event production, subscription and authorization, horizontally scalable connection workers, and reconnect/replay semantics. Explain that clients receive an event ID and sequence number, acknowledge a cursor, and resume from that cursor after reconnecting. Explicitly flag the tradeoff between storing a durable per-client cursor and making clients replay from their own last acknowledged cursor; the latter is simpler but may increase duplicate delivery. Close by saying that, with more time, you would quantify queue memory, test broker lag and regional failure behavior, and define operational `p99` latency and drop-rate alerts.
A second angle
A related prompt might focus on delivering payment-status updates to a browser that can disappear and reconnect frequently. The framing shifts from maximum fanout toward correctness of replay, authorization, and stale-state handling. You should distinguish transient UI updates from durable state transitions: a client may skip intermediate progress events but must be able to fetch the current payment state. This naturally leads to snapshot-plus-delta delivery, where the server sends a current resource version followed by newer events.
Common pitfalls
Pitfall: Saying “WebSockets guarantee reliable delivery” is incorrect; TCP only provides ordered bytes while the connection exists. Explain persistence, acknowledgments, replay, and deduplication at the application layer.
Pitfall: Jumping directly to
`Kafka`partitions and Redis nodes without first stating delivery, ordering, replay, and scale assumptions makes the design feel accidental. Establish guarantees and workload numbers before naming infrastructure.
Pitfall: Claiming exactly-once delivery as a simple feature is a depth mistake. A stronger answer distinguishes broker delivery from user-visible effects and uses idempotent event IDs, cursors, and client or consumer deduplication.
Connections
An interviewer may pivot to long polling and Server-Sent Events for transport tradeoffs, or to idempotency, event sourcing, and consumer backpressure for correctness. They may also explore multi-region routing, graceful deployment of connection workers, and observability through connection count, broker lag, replay rate, and `p99` delivery latency.
Further reading
-
RFC 6455: The WebSocket Protocol — protocol-level handshake, framing, control frames, and closing behavior.
-
Designing Data-Intensive Applications by Martin Kleppmann — durable logs, stream processing, ordering, and delivery semantics.
-
The Tail at Scale — why tail latency and stragglers matter in distributed request and fanout systems.
Practice questions
Online Assessment — 14 min
Coding & Algorithms
Coding is 3/5 and this appears across rounds, but you did not flag parsing as a weak area.
What's being tested
These problems test CSV parsing, record validation, and join/aggregation algorithms over semi-structured text data. You must preserve input order where required, define duplicate-key and malformed-row behavior, and choose between straightforward scanning, sorting, or hash-based indexing. Strong solutions separate normalization, validation, and output formatting so edge cases are explicit and testable.
Patterns & templates
-
Use Python's
csv.readerorcsv.DictReader; handle quoted commas, escaped quotes, headers, blank lines, and malformed records deliberately. -
Normalize comparison fields with
strip(),casefold(), whitespace tokenization, and stop-word removal before forbidden-word or overlap checks. -
Build a hash index
key -> list[record]for joins; preserve every left row and emit all matching right rows, givingO(n + m + output)time. -
For sorted joins, sort both inputs by key and advance two pointers; complexity is
O(n log n + m log m)with lower auxiliary indexing overhead. -
Validate each row independently, collecting deterministic error codes; distinguish missing, empty, overlong, forbidden, and structurally malformed fields.
-
Aggregate changed files through
file -> ownersandowner -> changed filesmaps; deduplicate reviewers while retaining a specified stable ordering.
Common pitfalls
Pitfall: Treating CSV as
line.split(",")breaks quoted commas and embedded quotes; use a standards-aware parser.
Pitfall: Implementing a dictionary join as one-to-one silently drops duplicate matches; store lists and define the Cartesian expansion explicitly.
Pitfall: Calling
lower()without trimming, tokenizing, or removing ignored words produces inconsistent overlap and forbidden-word results.
Practice these
The practice cards below cover the canonical variants — solve all of them and time yourself.
Practice questions
Coding is moderate and interval work is common; review half-open intervals, sorting, and deterministic formatting.
What's being tested
These problems test interval algorithms, event-driven simulation, and stateful time processing over ordered data. You must normalize time zones and boundary semantics, merge or subtract windows efficiently, process same-timestamp events deterministically, and preserve correctness across recurring schedules and account state changes.
Patterns & templates
-
Sweep line for interval union/difference: sort endpoints, track active counts, and emit transitions in
O(n log n)time. -
Convert recurring weekly times to integer minutes in
[0, 10080), apply offsets modulo a week, and explicitly handle wraparound intervals. -
For allowed-minus-freeze windows, merge overlaps first, subtract freezes with two pointers, then filter by lead time, minimum duration, and result cap.
-
Use a priority queue or k-way merge for notification streams; define deterministic ordering by timestamp, event type, and stable identifier.
-
Model account changes as ordered events with explicit state transitions; renewal, cancellation, and plan-change precedence must be documented.
-
Use a rolling window with a deque or per-key queues; evict timestamps
< current - window, maintainingO(n)total work. -
Separate computation from rendering: calculate eligible notifications first, sort/filter efficiently, then apply templates; avoid repeated scans over large account lists.
Common pitfalls
Pitfall: Treating interval endpoints inconsistently; choose half-open
[start, end)semantics so adjacent windows do not create false overlap.
Pitfall: Applying time-zone offsets after filtering; convert candidate intervals to UTC before lead-time comparisons, while preserving duration.
Pitfall: Assuming input order resolves ties; explicitly specify same-time event precedence and stable tie-breakers.
Practice these
The practice cards below cover the canonical variants — solve all of them and time yourself.
Practice questions